- Fixed unused function parameters by prefixing with underscore - Removed unused imports and variables - Applied eslint --fix for automatic style fixes - Property shorthand - String template literals - Prefer const over let where appropriate - Spacing and formatting Reduces lint errors from 108+ to 78 (61 unused vars, 17 other issues) Related to CI lint failures in previous commit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
238 lines
5.5 KiB
JavaScript
238 lines
5.5 KiB
JavaScript
/**
|
|
* ScheduledTask Model
|
|
*
|
|
* Multi-project calendar and reminder system for governance, framework reviews,
|
|
* and project-specific tasks. Integrated with session-init.js for startup reminders.
|
|
*
|
|
* Copyright 2025 Tractatus Project
|
|
* Licensed under Apache License 2.0
|
|
*/
|
|
|
|
const mongoose = require('mongoose');
|
|
|
|
const scheduledTaskSchema = new mongoose.Schema({
|
|
title: {
|
|
type: String,
|
|
required: true,
|
|
trim: true,
|
|
maxlength: 200
|
|
},
|
|
|
|
description: {
|
|
type: String,
|
|
required: true,
|
|
maxlength: 2000
|
|
},
|
|
|
|
dueDate: {
|
|
type: Date,
|
|
required: true,
|
|
index: true
|
|
},
|
|
|
|
priority: {
|
|
type: String,
|
|
enum: ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'],
|
|
default: 'MEDIUM',
|
|
index: true
|
|
},
|
|
|
|
category: {
|
|
type: String,
|
|
enum: ['framework', 'governance', 'security', 'project', 'research', 'deployment', 'other'],
|
|
default: 'other',
|
|
index: true
|
|
},
|
|
|
|
// Optional: Link to specific project (null for cross-project or framework tasks)
|
|
projectId: {
|
|
type: mongoose.Schema.Types.ObjectId,
|
|
ref: 'Project',
|
|
default: null,
|
|
index: true
|
|
},
|
|
|
|
projectName: {
|
|
type: String,
|
|
default: null // e.g., "tractatus", "family-history", "sydigital"
|
|
},
|
|
|
|
recurrence: {
|
|
type: String,
|
|
enum: ['once', 'daily', 'weekly', 'monthly', 'quarterly', 'yearly'],
|
|
default: 'once'
|
|
},
|
|
|
|
// For recurring tasks: when was this instance generated from?
|
|
parentTaskId: {
|
|
type: mongoose.Schema.Types.ObjectId,
|
|
ref: 'ScheduledTask',
|
|
default: null
|
|
},
|
|
|
|
status: {
|
|
type: String,
|
|
enum: ['pending', 'in_progress', 'completed', 'dismissed', 'overdue'],
|
|
default: 'pending',
|
|
index: true
|
|
},
|
|
|
|
// Reference to documentation or related files
|
|
documentRef: {
|
|
type: String,
|
|
default: null // e.g., "docs/AUTONOMOUS_FRAMEWORK_WORK_2025-10-23.md"
|
|
},
|
|
|
|
// Links to external resources or admin pages
|
|
links: [{
|
|
label: String,
|
|
url: String
|
|
}],
|
|
|
|
createdBy: {
|
|
type: String,
|
|
enum: ['system', 'user', 'session-init', 'migration'],
|
|
default: 'system'
|
|
},
|
|
|
|
assignedTo: {
|
|
type: String, // User email or "PM" or "Framework"
|
|
default: 'PM' // Default to human PM
|
|
},
|
|
|
|
completedDate: {
|
|
type: Date,
|
|
default: null
|
|
},
|
|
|
|
dismissedDate: {
|
|
type: Date,
|
|
default: null
|
|
},
|
|
|
|
dismissedReason: {
|
|
type: String,
|
|
default: null
|
|
},
|
|
|
|
// Flexible metadata for filtering and grouping
|
|
metadata: {
|
|
type: Map,
|
|
of: mongoose.Schema.Types.Mixed,
|
|
default: {}
|
|
},
|
|
|
|
// Tags for flexible categorization
|
|
tags: [{
|
|
type: String,
|
|
lowercase: true,
|
|
trim: true
|
|
}],
|
|
|
|
// Session-init display configuration
|
|
showInSessionInit: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
|
|
// How many days before due date to show in session-init
|
|
reminderDaysBefore: {
|
|
type: Number,
|
|
default: 7
|
|
}
|
|
|
|
}, {
|
|
timestamps: true
|
|
});
|
|
|
|
// Indexes for common queries
|
|
scheduledTaskSchema.index({ dueDate: 1, status: 1 });
|
|
scheduledTaskSchema.index({ category: 1, status: 1 });
|
|
scheduledTaskSchema.index({ projectId: 1, status: 1 });
|
|
scheduledTaskSchema.index({ status: 1, priority: -1, dueDate: 1 });
|
|
|
|
// Virtual: days until due
|
|
scheduledTaskSchema.virtual('daysUntilDue').get(function() {
|
|
const now = new Date();
|
|
const due = new Date(this.dueDate);
|
|
const diffTime = due - now;
|
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
return diffDays;
|
|
});
|
|
|
|
// Virtual: is overdue
|
|
scheduledTaskSchema.virtual('isOverdue').get(function() {
|
|
return this.status === 'pending' && new Date(this.dueDate) < new Date();
|
|
});
|
|
|
|
// Method: Complete task
|
|
scheduledTaskSchema.methods.complete = function() {
|
|
this.status = 'completed';
|
|
this.completedDate = new Date();
|
|
return this.save();
|
|
};
|
|
|
|
// Method: Dismiss task
|
|
scheduledTaskSchema.methods.dismiss = function(reason) {
|
|
this.status = 'dismissed';
|
|
this.dismissedDate = new Date();
|
|
this.dismissedReason = reason || 'Dismissed by user';
|
|
return this.save();
|
|
};
|
|
|
|
// Static: Get tasks due soon (for session-init)
|
|
scheduledTaskSchema.statics.getDueSoon = function(daysAhead = 7) {
|
|
const now = new Date();
|
|
const futureDate = new Date(now.getTime() + (daysAhead * 24 * 60 * 60 * 1000));
|
|
|
|
return this.find({
|
|
dueDate: { $lte: futureDate },
|
|
status: 'pending',
|
|
showInSessionInit: true
|
|
})
|
|
.sort({ priority: -1, dueDate: 1 })
|
|
.limit(10);
|
|
};
|
|
|
|
// Static: Get overdue tasks
|
|
scheduledTaskSchema.statics.getOverdue = function() {
|
|
return this.find({
|
|
dueDate: { $lt: new Date() },
|
|
status: 'pending'
|
|
})
|
|
.sort({ priority: -1, dueDate: 1 });
|
|
};
|
|
|
|
// Static: Get tasks by category
|
|
scheduledTaskSchema.statics.getByCategory = function(category, includeCompleted = false) {
|
|
const query = { category };
|
|
if (!includeCompleted) {
|
|
query.status = { $in: ['pending', 'in_progress'] };
|
|
}
|
|
return this.find(query).sort({ dueDate: 1 });
|
|
};
|
|
|
|
// Static: Get tasks by project
|
|
scheduledTaskSchema.statics.getByProject = function(projectId, includeCompleted = false) {
|
|
const query = { projectId };
|
|
if (!includeCompleted) {
|
|
query.status = { $in: ['pending', 'in_progress'] };
|
|
}
|
|
return this.find(query).sort({ dueDate: 1 });
|
|
};
|
|
|
|
// Pre-save hook: Update overdue status
|
|
scheduledTaskSchema.pre('save', function(next) {
|
|
if (this.status === 'pending' && this.dueDate < new Date()) {
|
|
this.status = 'overdue';
|
|
}
|
|
next();
|
|
});
|
|
|
|
// Enable virtuals in JSON
|
|
scheduledTaskSchema.set('toJSON', { virtuals: true });
|
|
scheduledTaskSchema.set('toObject', { virtuals: true });
|
|
|
|
const ScheduledTask = mongoose.model('ScheduledTask', scheduledTaskSchema);
|
|
|
|
module.exports = ScheduledTask;
|