feat: Wiederkehrende Aufgaben-Vorlagen (recurring task templates)

Neue TaskTemplate-Entität mit YEARLY/MONTHLY/INTERVAL_DAYS-Wiederholung.
Fällige Aufgaben werden lazy bei GET /tasks erzeugt (Compare-and-Swap auf
nextDueDate), damit mehrere API-Replicas im Kubernetes-Cluster dieselbe
Vorlage nicht doppelt feuern - ohne zusätzliche Cron-Abhängigkeit.

Frontend: Verwaltungsseite unter /settings/task-templates zum Anlegen,
Auflisten und Deaktivieren von Vorlagen.
This commit is contained in:
Nick Szaunig committed 2026-08-25 05:36:55 +02:00
1 parent e458a7cf07
commit 4609a0f9ec
20 files changed
+1341

No files matched your search

@@ -0,0 +1,24 @@
import { apiFetch } from "@/lib/auth/api-client";
import { CreateTaskTemplateInput, TaskTemplate, UpdateTaskTemplateInput } from "./types";
export function fetchTaskTemplates(): Promise<TaskTemplate[]> {
return apiFetch<TaskTemplate[]>("/task-templates");
}
export function createTaskTemplate(input: CreateTaskTemplateInput): Promise<TaskTemplate> {
return apiFetch<TaskTemplate>("/task-templates", {
method: "POST",
body: JSON.stringify(input),
});
}
export function updateTaskTemplate(id: string, input: UpdateTaskTemplateInput): Promise<TaskTemplate> {
return apiFetch<TaskTemplate>(`/task-templates/${id}`, {
method: "PATCH",
body: JSON.stringify(input),
});
}
export function deactivateTaskTemplate(id: string): Promise<TaskTemplate> {
return apiFetch<TaskTemplate>(`/task-templates/${id}/deactivate`, { method: "POST" });
}
@@ -0,0 +1,43 @@
import { TaskPriority } from "@/lib/tasks/types";
export type TaskTemplateRecurrenceType = "YEARLY" | "MONTHLY" | "INTERVAL_DAYS";
export interface TaskTemplate {
id: string;
title: string;
description: string | null;
priority: TaskPriority;
assignedUserId: string | null;
createdByUserId: string | null;
recurrenceType: TaskTemplateRecurrenceType;
recurrenceMonth: number | null;
recurrenceDayOfMonth: number | null;
intervalDays: number | null;
nextDueDate: string;
active: boolean;
createdAt: string;
updatedAt: string;
}
export interface CreateTaskTemplateInput {
title: string;
description?: string;
priority?: TaskPriority;
assignedUserId?: string;
recurrenceType: TaskTemplateRecurrenceType;
recurrenceMonth?: number;
recurrenceDayOfMonth?: number;
intervalDays?: number;
}
export interface UpdateTaskTemplateInput {
title?: string;
description?: string;
priority?: TaskPriority;
assignedUserId?: string;
recurrenceType?: TaskTemplateRecurrenceType;
recurrenceMonth?: number;
recurrenceDayOfMonth?: number;
intervalDays?: number;
active?: boolean;
}