feat: seed a starter annual task-template calendar
TaskTemplate had zero seed data - a fresh deployment showed a completely empty recurring-task list with no hint of what to even put there. Seeds 12 YEARLY templates, one per month, covering the standard German-beekeeping annual cycle (Frühjahrsdurchsicht, Schwarmkontrolle, Einfütterung, Varroa-Restentmilbung/Oxalsäure, Wintersitz-Kontrolle, etc.) so a new beekeeper gets a ready-made starting point instead of a blank page. Runs once (guarded by a taskTemplate.count() check, same idempotency shape as the existing admin-user seed) and only ever adds rows - admins can edit or deactivate any of them afterward. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
c4f970f071
commit
81986b3275
1 file changed
+161
@@ -30,11 +30,172 @@ function printLoginBanner(email: string, password: string): void {
|
|||||||
console.log(line);
|
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<void> {
|
||||||
|
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<void> {
|
async function main(): Promise<void> {
|
||||||
const prisma = new PrismaClient({
|
const prisma = new PrismaClient({
|
||||||
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
|
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await seedDefaultTaskTemplates(prisma);
|
||||||
|
|
||||||
const email = process.env.SEED_ADMIN_EMAIL ?? DEFAULT_ADMIN_EMAIL;
|
const email = process.env.SEED_ADMIN_EMAIL ?? DEFAULT_ADMIN_EMAIL;
|
||||||
const password = process.env.SEED_ADMIN_PASSWORD ?? generateRandomPassword();
|
const password = process.env.SEED_ADMIN_PASSWORD ?? generateRandomPassword();
|
||||||
const existing = await prisma.user.findUnique({ where: { email } });
|
const existing = await prisma.user.findUnique({ where: { email } });
|
||||||
|
|||||||
Reference in new issue
Block a user