feat(planning): ship holiday-aware planning and assistant upgrades

This commit is contained in:
2026-03-28 22:49:28 +01:00
parent 2a005794e7
commit 4f48afe7b4
151 changed files with 17738 additions and 1940 deletions
+32 -10
View File
@@ -12,6 +12,11 @@ import {
} from "@capakraken/shared";
import { PrismaClient, type Prisma, type Resource, type Project } from "@prisma/client";
import { hash } from "@node-rs/argon2";
import { getHolidayDemoProfileForIndex } from "./holiday-demo-profiles.js";
import { loadWorkspaceEnv } from "./load-workspace-env.js";
import { assertSafeSeedTarget } from "./safe-destructive-env.js";
loadWorkspaceEnv();
const prisma = new PrismaClient();
@@ -273,10 +278,11 @@ function parseAllocationType(s: string): AllocationType {
// ─── Main ──────────────────────────────────────────────────────────────────────
async function main() {
console.warn("Seeding Planarchy with 3D studio example data...");
const target = assertSafeSeedTarget("db:seed");
console.warn(`Seeding CapaKraken example data into ${target.databaseName} (${target.hostname}${target.port ? `:${target.port}` : ""})...`);
// ── 1. Delete all data (keep users) ────────────────────────────────────────
console.warn("Deleting existing data...");
console.warn(`Deleting existing data from disposable seed target '${target.databaseName}'...`);
await prisma.auditLog.deleteMany({});
await prisma.notification.deleteMany({});
// Estimates (deep hierarchy)
@@ -362,7 +368,7 @@ async function main() {
const cityMap = new Map<string, string>(); // cityName → id
for (const [countryId, cities] of [
[countryDE.id, ["Stuttgart", "Hamburg", "Muenchen", "Berlin"]],
[countryDE.id, ["Augsburg", "Berlin", "Hamburg", "Koeln", "Muenchen", "Stuttgart"]],
[countryIN.id, ["Bangalore", "Mumbai"]],
[countryES.id, ["Madrid", "Barcelona"]],
[countryUS.id, ["New York", "Los Angeles"]],
@@ -871,8 +877,16 @@ async function main() {
// ── 5. Create resources ────────────────────────────────────────────────────
const resourceMap = new Map<string, Resource>();
for (const row of RESOURCE_DATA) {
const [eid, chapter, typeOfWork, clientUnit, city, employeeType, lcr, ucr, fraction, availDays, chargeability] = row;
const countryIdByCode = new Map<string, string>([
["DE", countryDE.id],
["ES", countryES.id],
["IN", countryIN.id],
["US", countryUS.id],
]);
for (const [index, row] of RESOURCE_DATA.entries()) {
const [eid, chapter, typeOfWork, clientUnit, , employeeType, lcr, ucr, fraction, availDays, chargeability] = row;
const holidayProfile = getHolidayDemoProfileForIndex(index);
const displayName = eid
.split(".")
@@ -886,8 +900,8 @@ async function main() {
const skills = computeSkills(chapter, typeOfWork, lcr);
// Dispo v2: resolve FKs
const resCountryId = countryDE.id; // all seed resources are Germany
const resMetroCityId = cityMap.get(city) ?? null;
const resCountryId = countryIdByCode.get(holidayProfile.countryCode) ?? countryDE.id;
const resMetroCityId = cityMap.get(holidayProfile.cityName) ?? null;
// chapter → orgUnit mapping
const chapterToOrgUnit: Record<string, string> = {
@@ -943,9 +957,17 @@ async function main() {
chargeabilityTarget: chargeability * 100,
availability: availability as unknown as Prisma.InputJsonValue,
skills: skills as unknown as Prisma.InputJsonValue,
dynamicFields: { clientUnit, workType: typeOfWork, city, employeeType },
dynamicFields: {
clientUnit,
workType: typeOfWork,
city: holidayProfile.cityName,
employeeType,
holidayCountryCode: holidayProfile.countryCode,
holidayStateCode: holidayProfile.stateCode,
},
blueprintId: resourceBlueprint.id,
countryId: resCountryId,
federalState: holidayProfile.stateCode,
...(resMetroCityId ? { metroCityId: resMetroCityId } : {}),
...(resOrgUnitId ? { orgUnitId: resOrgUnitId } : {}),
...(resMgmtGroupId ? { managementLevelGroupId: resMgmtGroupId } : {}),
@@ -1037,7 +1059,7 @@ async function main() {
hoursPerDay: number;
percentage: number;
headcount: number;
status: string;
status: AllocationStatus;
}
const DEMAND_SEEDS: DemandSeed[] = [
@@ -1092,7 +1114,7 @@ async function main() {
end: string;
hoursPerDay: number;
percentage: number;
status: string;
status: AllocationStatus;
}
const ASSIGNMENT_SEEDS: AssignmentSeed[] = [