Michi 0ebe7fa13d
Some checks failed
🚀 Continuous Integration / 🔧 Backend Tests (18.x) (push) Has been cancelled
🚀 Continuous Integration / 🔧 Backend Tests (20.x) (push) Has been cancelled
🚀 Continuous Integration / 🎨 Frontend Tests (18.x) (push) Has been cancelled
🚀 Continuous Integration / 🎨 Frontend Tests (20.x) (push) Has been cancelled
🚀 Continuous Integration / 🔍 Code Quality (push) Has been cancelled
🚀 Continuous Integration / 🔒 Security Checks (push) Has been cancelled
🚀 Continuous Integration / 🎨 Theme Tests (push) Has been cancelled
🚀 Continuous Integration / ♿ Accessibility Tests (push) Has been cancelled
🚀 Continuous Integration / 📱 Cross-Browser Tests (push) Has been cancelled
🚀 Continuous Integration / 🏗️ Build Tests (push) Has been cancelled
🚀 Continuous Integration / 📊 Performance Tests (push) Has been cancelled
🚀 Continuous Integration / 🎯 Integration Tests (push) Has been cancelled
🚀 Continuous Integration / ✅ All Tests Passed (push) Has been cancelled
Initial commit: ToDo Kids v1.0.0
2025-08-04 15:46:08 +02:00

278 lines
6.3 KiB
JavaScript

const mongoose = require('mongoose');
// Schema für Aufgaben
const taskSchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true,
maxlength: 100
},
description: {
type: String,
trim: true,
maxlength: 500
},
icon: {
type: String,
default: 'task',
enum: [
'task', 'clean', 'dishes', 'vacuum', 'trash', 'bed', 'homework',
'teeth', 'shower', 'clothes', 'toys', 'pets', 'garden', 'help',
'read', 'practice', 'exercise', 'cook', 'organize', 'other'
]
},
points: {
type: Number,
required: true,
min: 1,
max: 500,
default: 10
},
difficulty: {
type: String,
enum: ['easy', 'medium', 'hard'],
default: 'easy'
},
category: {
type: String,
enum: [
'household', 'personal', 'homework', 'chores', 'hygiene',
'pets', 'garden', 'helping', 'learning', 'exercise', 'other'
],
default: 'household'
},
assignedTo: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Child',
required: true
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
dueDate: {
type: Date,
required: true
},
status: {
type: String,
enum: ['pending', 'completed', 'approved', 'rejected'],
default: 'pending'
},
completedAt: {
type: Date
},
approvedAt: {
type: Date
},
approvedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
rejectionReason: {
type: String,
trim: true,
maxlength: 200
},
recurring: {
isRecurring: {
type: Boolean,
default: false
},
frequency: {
type: String,
enum: ['daily', 'weekly', 'monthly'],
required: function() { return this.recurring.isRecurring; }
},
daysOfWeek: [{
type: Number,
min: 0,
max: 6 // 0 = Sonntag, 6 = Samstag
}],
endDate: Date,
nextDueDate: Date
},
priority: {
type: String,
enum: ['low', 'normal', 'high', 'urgent'],
default: 'normal'
},
estimatedTime: {
type: Number, // in Minuten
min: 1,
max: 480 // max 8 Stunden
},
tags: [{
type: String,
trim: true,
maxlength: 20
}],
notes: {
type: String,
trim: true,
maxlength: 1000
},
attachments: [{
filename: String,
url: String,
uploadedAt: {
type: Date,
default: Date.now
}
}],
isActive: {
type: Boolean,
default: true
},
createdAt: {
type: Date,
default: Date.now
},
updatedAt: {
type: Date,
default: Date.now
}
});
// Indizes für bessere Performance
taskSchema.index({ assignedTo: 1, status: 1, dueDate: 1 });
taskSchema.index({ createdBy: 1, createdAt: -1 });
taskSchema.index({ dueDate: 1, status: 1 });
taskSchema.index({ 'recurring.isRecurring': 1, 'recurring.nextDueDate': 1 });
// Middleware: updatedAt automatisch setzen
taskSchema.pre('save', function(next) {
this.updatedAt = new Date();
next();
});
// Methode: Aufgabe als erledigt markieren
taskSchema.methods.markCompleted = function() {
this.status = 'completed';
this.completedAt = new Date();
return this.save();
};
// Methode: Aufgabe genehmigen
taskSchema.methods.approve = async function(approvedByUserId) {
this.status = 'approved';
this.approvedAt = new Date();
this.approvedBy = approvedByUserId;
// Punkte dem Kind gutschreiben
const Child = mongoose.model('Child');
const child = await Child.findById(this.assignedTo);
if (child) {
await child.addPoints(this.points);
// Statistiken aktualisieren
child.stats.tasksCompleted += 1;
child.stats.tasksCompletedToday += 1;
child.updateStreak();
await child.save();
}
// Nächste wiederkehrende Aufgabe erstellen
if (this.recurring.isRecurring) {
await this.createNextRecurringTask();
}
return this.save();
};
// Methode: Aufgabe ablehnen
taskSchema.methods.reject = function(reason, rejectedByUserId) {
this.status = 'rejected';
this.rejectionReason = reason;
this.approvedBy = rejectedByUserId;
return this.save();
};
// Methode: Nächste wiederkehrende Aufgabe erstellen
taskSchema.methods.createNextRecurringTask = async function() {
if (!this.recurring.isRecurring) return;
const Task = mongoose.model('Task');
let nextDueDate = new Date(this.dueDate);
// Nächstes Fälligkeitsdatum berechnen
switch (this.recurring.frequency) {
case 'daily':
nextDueDate.setDate(nextDueDate.getDate() + 1);
break;
case 'weekly':
nextDueDate.setDate(nextDueDate.getDate() + 7);
break;
case 'monthly':
nextDueDate.setMonth(nextDueDate.getMonth() + 1);
break;
}
// Prüfen ob End-Datum erreicht
if (this.recurring.endDate && nextDueDate > this.recurring.endDate) {
return;
}
// Neue Aufgabe erstellen
const newTask = new Task({
title: this.title,
description: this.description,
icon: this.icon,
points: this.points,
difficulty: this.difficulty,
category: this.category,
assignedTo: this.assignedTo,
createdBy: this.createdBy,
dueDate: nextDueDate,
recurring: this.recurring,
priority: this.priority,
estimatedTime: this.estimatedTime,
tags: this.tags,
notes: this.notes
});
await newTask.save();
};
// Statische Methode: Überfällige Aufgaben finden
taskSchema.statics.findOverdue = function() {
const now = new Date();
return this.find({
dueDate: { $lt: now },
status: { $in: ['pending', 'completed'] },
isActive: true
});
};
// Statische Methode: Heutige Aufgaben für ein Kind
taskSchema.statics.findTodayTasks = function(childId) {
const today = new Date();
today.setHours(0, 0, 0, 0);
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
return this.find({
assignedTo: childId,
dueDate: { $gte: today, $lt: tomorrow },
isActive: true
}).sort({ priority: -1, createdAt: 1 });
};
// Virtuelle Eigenschaft: Ist überfällig
taskSchema.virtual('isOverdue').get(function() {
return this.dueDate < new Date() && this.status === 'pending';
});
// Virtuelle Eigenschaft: Ist heute fällig
taskSchema.virtual('isDueToday').get(function() {
const today = new Date();
today.setHours(0, 0, 0, 0);
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
return this.dueDate >= today && this.dueDate < tomorrow;
});
module.exports = mongoose.model('Task', taskSchema);