tasks: shared task board with sticker rewards, difficulty levels, and calendar
Some checks failed
CI / update (push) Has been cancelled

Complete household task management system behind task_users auth group:
- Task CRUD with recurring schedules, assignees, tags, and optional difficulty
- Blobcat SVG sticker rewards on completion, rarity weighted by difficulty
- Sticker collection page with calendar view and progress tracking
- Redesigned cards with left accent urgency strip, assignee PFP, round check button
- Weekday-based due date labels for tasks within 7 days
- Tasks link added to homepage LinksGrid
This commit is contained in:
2026-04-02 07:32:53 +02:00
parent 3cafe8955a
commit 9027dd9881
69 changed files with 11158 additions and 1 deletions

92
src/models/Task.ts Normal file
View File

@@ -0,0 +1,92 @@
import mongoose from 'mongoose';
export interface ITask {
_id?: string;
title: string;
description?: string;
assignees: string[];
tags: string[];
difficulty?: 'low' | 'medium' | 'high';
isRecurring: boolean;
frequency?: {
type: 'daily' | 'weekly' | 'biweekly' | 'monthly' | 'custom';
customDays?: number;
};
nextDueDate: Date;
lastCompletedAt?: Date;
lastCompletedBy?: string;
createdBy: string;
active: boolean;
createdAt?: Date;
updatedAt?: Date;
}
const TaskSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
trim: true
},
description: {
type: String,
trim: true
},
assignees: [{
type: String,
trim: true
}],
tags: [{
type: String,
trim: true,
lowercase: true
}],
difficulty: {
type: String,
enum: ['low', 'medium', 'high']
},
isRecurring: {
type: Boolean,
required: true,
default: false
},
frequency: {
type: {
type: String,
enum: ['daily', 'weekly', 'biweekly', 'monthly', 'custom']
},
customDays: {
type: Number,
min: 1
}
},
nextDueDate: {
type: Date,
required: true
},
lastCompletedAt: {
type: Date
},
lastCompletedBy: {
type: String,
trim: true
},
createdBy: {
type: String,
required: true,
trim: true
},
active: {
type: Boolean,
default: true
}
},
{
timestamps: true
}
);
TaskSchema.index({ active: 1, nextDueDate: 1 });
TaskSchema.index({ tags: 1 });
export const Task = mongoose.model<ITask>('Task', TaskSchema);

View File

@@ -0,0 +1,51 @@
import mongoose from 'mongoose';
export interface ITaskCompletion {
_id?: string;
taskId: mongoose.Types.ObjectId;
taskTitle: string;
completedBy: string;
completedAt: Date;
stickerId?: string;
tags: string[];
}
const TaskCompletionSchema = new mongoose.Schema(
{
taskId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Task',
required: true
},
taskTitle: {
type: String,
required: true,
trim: true
},
completedBy: {
type: String,
required: true,
trim: true
},
completedAt: {
type: Date,
required: true,
default: Date.now
},
stickerId: {
type: String,
trim: true
},
tags: [{
type: String,
trim: true,
lowercase: true
}]
}
);
TaskCompletionSchema.index({ completedBy: 1 });
TaskCompletionSchema.index({ taskId: 1 });
TaskCompletionSchema.index({ completedAt: -1 });
export const TaskCompletion = mongoose.model<ITaskCompletion>('TaskCompletion', TaskCompletionSchema);