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,37 @@
-- CreateEnum
CREATE TYPE "TaskTemplateRecurrenceType" AS ENUM ('YEARLY', 'MONTHLY', 'INTERVAL_DAYS');
-- AlterTable
ALTER TABLE "Task" ADD COLUMN "sourceTemplateId" TEXT;
-- CreateTable
CREATE TABLE "TaskTemplate" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"priority" "TaskPriority" NOT NULL DEFAULT 'NORMAL',
"assignedUserId" TEXT,
"createdByUserId" TEXT,
"recurrenceType" "TaskTemplateRecurrenceType" NOT NULL,
"recurrenceMonth" INTEGER,
"recurrenceDayOfMonth" INTEGER,
"intervalDays" INTEGER,
"nextDueDate" TIMESTAMP(3) NOT NULL,
"active" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "TaskTemplate_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "TaskTemplate_active_nextDueDate_idx" ON "TaskTemplate"("active", "nextDueDate");
-- AddForeignKey
ALTER TABLE "Task" ADD CONSTRAINT "Task_sourceTemplateId_fkey" FOREIGN KEY ("sourceTemplateId") REFERENCES "TaskTemplate"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TaskTemplate" ADD CONSTRAINT "TaskTemplate_assignedUserId_fkey" FOREIGN KEY ("assignedUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TaskTemplate" ADD CONSTRAINT "TaskTemplate_createdByUserId_fkey" FOREIGN KEY ("createdByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;