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
278 lines
6.3 KiB
JavaScript
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); |