diff --git a/ApiCore-Backend/prisma/seed.ts b/ApiCore-Backend/prisma/seed.ts index d2e06df..29a4d51 100644 --- a/ApiCore-Backend/prisma/seed.ts +++ b/ApiCore-Backend/prisma/seed.ts @@ -30,11 +30,172 @@ function printLoginBanner(email: string, password: string): void { console.log(line); } +// Duplicated from src/task-templates/recurrence.ts rather than imported: +// tsconfig.seed.json scopes this build to rootDir "./prisma" only (its own +// isolated compile, separate from the main Nest build), so a cross-rootDir +// import would fail to compile. Only the YEARLY case is needed here. +function nextYearlyOccurrenceUtc( + month: number, + dayOfMonth: number, + from: Date, +): Date { + const year = from.getUTCFullYear(); + const clampedDay = (y: number): number => + Math.min(dayOfMonth, new Date(Date.UTC(y, month, 0)).getUTCDate()); + const today = new Date( + Date.UTC(from.getUTCFullYear(), from.getUTCMonth(), from.getUTCDate()), + ); + const candidate = new Date(Date.UTC(year, month - 1, clampedDay(year))); + if (candidate.getTime() >= today.getTime()) { + return candidate; + } + return new Date(Date.UTC(year + 1, month - 1, clampedDay(year + 1))); +} + +interface DefaultTaskTemplate { + title: string; + description: string; + priority: 'LOW' | 'NORMAL' | 'HIGH' | 'URGENT'; + month: number; + dayOfMonth: number; +} + +// The standard German-beekeeping annual cycle, one flagship task per month - +// gives a new deployment a ready-made starter calendar instead of a +// completely empty task-template list. Admins can edit/deactivate any of +// these afterward; this only ever runs once (see the count() guard below). +const DEFAULT_TASK_TEMPLATES: DefaultTaskTemplate[] = [ + { + title: 'Wintersitz kontrollieren', + description: + 'Ruhige Kontrolle von außen: Flugloch frei, Gewicht per Anheben schätzen, auf Mäuse-/Spechtschäden achten. Beute möglichst nicht öffnen.', + priority: 'NORMAL', + month: 1, + dayOfMonth: 15, + }, + { + title: 'Gemülldiagnose', + description: + 'Windel auswerten: natürlicher Varroa-Totenfall, Bienensitz und Lage der Wintertraube anhand des Gemülls einschätzen.', + priority: 'NORMAL', + month: 2, + dayOfMonth: 15, + }, + { + title: 'Frühjahrsdurchsicht', + description: + 'Erste Innendurchsicht bei ca. 15°C und ruhigem Wetter: Brutnest, Futtervorrat, Volksstärke und Weiselrichtigkeit prüfen.', + priority: 'HIGH', + month: 3, + dayOfMonth: 15, + }, + { + title: 'Volk erweitern', + description: + 'Zargen/Rähmchen nach Volksstärke erweitern, Baurahmen einhängen, Platz für die Frühtracht schaffen.', + priority: 'NORMAL', + month: 4, + dayOfMonth: 1, + }, + { + title: 'Schwarmkontrolle beginnen', + description: + 'Wöchentlich auf Weiselzellen kontrollieren, bis zum Ende der Schwarmzeit fortführen. Maßnahmen bei Bedarf: Ableger bilden, Zellen brechen.', + priority: 'HIGH', + month: 5, + dayOfMonth: 1, + }, + { + title: 'Honigernte vorbereiten', + description: + 'Reifegrad der Frühtracht prüfen (Spritzprobe), Schleuderplan und Gerätereinigung vorbereiten.', + priority: 'NORMAL', + month: 6, + dayOfMonth: 15, + }, + { + title: 'Varroa-Kontrolle nach der Ernte', + description: + 'Milbenbefall messen (z. B. Puderzuckermethode oder natürlicher Totenfall) und Behandlung planen, sobald die Honigräume abgeräumt sind.', + priority: 'HIGH', + month: 7, + dayOfMonth: 15, + }, + { + title: 'Einfütterung beginnen', + description: + 'Wintervorrat auffüttern (Zielgewicht je nach Beutensystem), Futteraufnahme nach ein bis zwei Wochen kontrollieren.', + priority: 'HIGH', + month: 8, + dayOfMonth: 1, + }, + { + title: 'Varroa-Restentmilbung / Winterfestigkeit prüfen', + description: + 'Zweite Varroabehandlung durchführen, Volksstärke und Weiselrichtigkeit für den Winter beurteilen.', + priority: 'HIGH', + month: 9, + dayOfMonth: 15, + }, + { + title: 'Fütterung abschließen, Mäusegitter anbringen', + description: + 'Letzte Futtergabe, Fluglochkeile bzw. Mäusegitter montieren, Bienenstand winterfest machen.', + priority: 'NORMAL', + month: 10, + dayOfMonth: 1, + }, + { + title: 'Winterbehandlung (Oxalsäure)', + description: + 'Brutfreiheit abwarten und Träufel- oder Sprühbehandlung gegen Varroa durchführen.', + priority: 'HIGH', + month: 11, + dayOfMonth: 15, + }, + { + title: 'Jahresrückblick und Gerätewartung', + description: + 'Saison auswerten, Zargen und Rähmchen reinigen/reparieren, Material für die kommende Saison planen.', + priority: 'LOW', + month: 12, + dayOfMonth: 15, + }, +]; + +async function seedDefaultTaskTemplates(prisma: PrismaClient): Promise { + const existingCount = await prisma.taskTemplate.count(); + if (existingCount > 0) { + return; + } + const now = new Date(); + await prisma.taskTemplate.createMany({ + data: DEFAULT_TASK_TEMPLATES.map((template) => ({ + title: template.title, + description: template.description, + priority: template.priority, + recurrenceType: 'YEARLY', + recurrenceMonth: template.month, + recurrenceDayOfMonth: template.dayOfMonth, + nextDueDate: nextYearlyOccurrenceUtc( + template.month, + template.dayOfMonth, + now, + ), + })), + }); + console.log( + `Seed: ${DEFAULT_TASK_TEMPLATES.length} Standard-Aufgabenvorlagen angelegt.`, + ); +} + async function main(): Promise { const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }), }); + await seedDefaultTaskTemplates(prisma); + const email = process.env.SEED_ADMIN_EMAIL ?? DEFAULT_ADMIN_EMAIL; const password = process.env.SEED_ADMIN_PASSWORD ?? generateRandomPassword(); const existing = await prisma.user.findUnique({ where: { email } });