147 lines
5.2 KiB
TypeScript
147 lines
5.2 KiB
TypeScript
/**
|
||
* seed-vacations.ts
|
||
* Populates 15 vacation days per resource per year (2025, 2026, 2027-partial).
|
||
* Spread across 3 blocks:
|
||
* – Spring/Easter (5 days, fixed)
|
||
* – Summer (5 days, staggered by resource index so teams don't all leave at once)
|
||
* – Christmas/NYE (5 days, fixed)
|
||
*/
|
||
|
||
import { PrismaClient } from "@prisma/client";
|
||
|
||
const prisma = new PrismaClient();
|
||
|
||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||
|
||
/** ISO date string → Date at midnight UTC */
|
||
function d(iso: string): Date {
|
||
return new Date(iso + "T00:00:00.000Z");
|
||
}
|
||
|
||
/** Add calendar days to a Date */
|
||
function addDays(date: Date, n: number): Date {
|
||
const r = new Date(date);
|
||
r.setUTCDate(r.getUTCDate() + n);
|
||
return r;
|
||
}
|
||
|
||
// ─── Vacation blocks per year ─────────────────────────────────────────────────
|
||
// Each block is { start, end } of a Mon–Fri week (5 working days).
|
||
// Summer has multiple offsets so resources are staggered (offset chosen by index % 4).
|
||
|
||
const BLOCKS: Record<
|
||
number,
|
||
{
|
||
spring: { start: string; end: string };
|
||
summer: { start: string; end: string }[];
|
||
christmas: { start: string; end: string };
|
||
}
|
||
> = {
|
||
2025: {
|
||
spring: { start: "2025-04-14", end: "2025-04-18" }, // Mon–Fri Easter week
|
||
summer: [
|
||
{ start: "2025-07-07", end: "2025-07-11" },
|
||
{ start: "2025-07-21", end: "2025-07-25" },
|
||
{ start: "2025-08-04", end: "2025-08-08" },
|
||
{ start: "2025-08-18", end: "2025-08-22" },
|
||
],
|
||
christmas: { start: "2025-12-22", end: "2025-12-26" }, // Mon–Fri
|
||
},
|
||
2026: {
|
||
spring: { start: "2026-03-30", end: "2026-04-03" }, // Mon–Fri Easter week
|
||
summer: [
|
||
{ start: "2026-07-06", end: "2026-07-10" },
|
||
{ start: "2026-07-20", end: "2026-07-24" },
|
||
{ start: "2026-08-03", end: "2026-08-07" },
|
||
{ start: "2026-08-17", end: "2026-08-21" },
|
||
],
|
||
christmas: { start: "2026-12-21", end: "2026-12-25" }, // Mon–Fri
|
||
},
|
||
2027: {
|
||
// Partial year — spring only (for future view context)
|
||
spring: { start: "2027-03-29", end: "2027-04-02" },
|
||
summer: [
|
||
{ start: "2027-07-05", end: "2027-07-09" },
|
||
{ start: "2027-07-19", end: "2027-07-23" },
|
||
{ start: "2027-08-02", end: "2027-08-06" },
|
||
{ start: "2027-08-16", end: "2027-08-20" },
|
||
],
|
||
christmas: { start: "2027-12-20", end: "2027-12-24" },
|
||
},
|
||
};
|
||
|
||
const YEARS = [2025, 2026, 2027];
|
||
|
||
// ─── Main ─────────────────────────────────────────────────────────────────────
|
||
|
||
async function main() {
|
||
// Get admin user to act as approver (fall back to manager, then any user)
|
||
const admin =
|
||
(await prisma.user.findFirst({ where: { systemRole: "ADMIN" }, select: { id: true } })) ??
|
||
(await prisma.user.findFirst({ where: { systemRole: "MANAGER" }, select: { id: true } })) ??
|
||
(await prisma.user.findFirst({ select: { id: true } }));
|
||
if (!admin) {
|
||
throw new Error("No users found — run the main seed first.");
|
||
}
|
||
|
||
// Get all resources ordered by eid for stable staggering
|
||
const resources = await prisma.resource.findMany({
|
||
where: { isActive: true },
|
||
select: { id: true, eid: true },
|
||
orderBy: { eid: "asc" },
|
||
});
|
||
|
||
console.log(`Found ${resources.length} active resources. Admin id: ${admin.id}`);
|
||
|
||
// Delete existing vacations to allow re-running
|
||
const deleted = await prisma.vacation.deleteMany({});
|
||
console.log(`Deleted ${deleted.count} existing vacation records.`);
|
||
|
||
let created = 0;
|
||
|
||
for (let i = 0; i < resources.length; i++) {
|
||
const resource = resources[i]!;
|
||
const summerOffset = i % 4; // 0-3 → pick one of the 4 staggered summer weeks
|
||
|
||
for (const year of YEARS) {
|
||
const blocks = BLOCKS[year]!;
|
||
const summerBlock = blocks.summer[summerOffset]!;
|
||
|
||
const vacationBlocks = [
|
||
{ type: "ANNUAL" as const, ...blocks.spring },
|
||
{ type: "ANNUAL" as const, ...summerBlock },
|
||
{ type: "ANNUAL" as const, ...blocks.christmas },
|
||
];
|
||
|
||
for (const block of vacationBlocks) {
|
||
await prisma.vacation.create({
|
||
data: {
|
||
resourceId: resource.id,
|
||
type: block.type,
|
||
status: "APPROVED",
|
||
startDate: d(block.start),
|
||
endDate: d(block.end),
|
||
requestedById: admin.id,
|
||
approvedById: admin.id,
|
||
approvedAt: new Date(),
|
||
note: `${year} annual leave`,
|
||
},
|
||
});
|
||
created++;
|
||
}
|
||
}
|
||
|
||
console.log(` ✓ ${resource.eid} — ${YEARS.length * 3} vacation blocks`);
|
||
}
|
||
|
||
console.log(`\nDone! Created ${created} vacation records.`);
|
||
console.log(` ${resources.length} resources × ${YEARS.length} years × 3 blocks = ${resources.length * YEARS.length * 3} expected`);
|
||
}
|
||
|
||
main()
|
||
.catch((e) => {
|
||
console.error(e);
|
||
process.exit(1);
|
||
})
|
||
.finally(() => void prisma.$disconnect());
|