rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI (#61)
CI / Architecture Guardrails (push) Successful in 2m38s
CI / Assistant Split Regression (push) Successful in 3m33s
CI / Typecheck (push) Successful in 3m51s
CI / Lint (push) Successful in 5m2s
CI / E2E Tests (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Architecture Guardrails (push) Successful in 2m38s
CI / Assistant Split Regression (push) Successful in 3m33s
CI / Typecheck (push) Successful in 3m51s
CI / Lint (push) Successful in 5m2s
CI / E2E Tests (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI (#61) Co-authored-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com> Co-committed-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com>
This commit was merged in pull request #61.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Generate samples/CapaKrakenExamples.xlsx from the live database.
|
||||
* Generate samples/NexusExamples.xlsx from the live database.
|
||||
*
|
||||
* Run from repo root:
|
||||
* pnpm --filter @capakraken/db db:excel
|
||||
* pnpm --filter @nexus/db db:excel
|
||||
*/
|
||||
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
@@ -10,10 +10,10 @@ import ExcelJS from "exceljs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { loadWorkspaceEnv } from "./load-workspace-env.js";
|
||||
import { assertCapaKrakenDbTarget } from "./safe-destructive-env.js";
|
||||
import { assertNexusDbTarget } from "./safe-destructive-env.js";
|
||||
|
||||
loadWorkspaceEnv();
|
||||
assertCapaKrakenDbTarget("db:excel");
|
||||
assertNexusDbTarget("db:excel");
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
@@ -21,20 +21,20 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
// ─── Colour palette ──────────────────────────────────────────────────────────
|
||||
|
||||
const COLORS = {
|
||||
headerBg: "FF1E3A5F", // dark navy
|
||||
headerFg: "FFFFFFFF",
|
||||
altRow: "FFF5F7FA",
|
||||
border: "FFCDD5E0",
|
||||
headerBg: "FF1E3A5F", // dark navy
|
||||
headerFg: "FFFFFFFF",
|
||||
altRow: "FFF5F7FA",
|
||||
border: "FFCDD5E0",
|
||||
// status
|
||||
completed: "FFD1FAE5",
|
||||
active: "FFDBEAFE",
|
||||
onHold: "FFFEF3C7",
|
||||
draft: "FFF3F4F6",
|
||||
cancelled: "FFFEE2E2",
|
||||
completed: "FFD1FAE5",
|
||||
active: "FFDBEAFE",
|
||||
onHold: "FFFEF3C7",
|
||||
draft: "FFF3F4F6",
|
||||
cancelled: "FFFEE2E2",
|
||||
// allocation status
|
||||
allocActive: "FFDBEAFE",
|
||||
allocActive: "FFDBEAFE",
|
||||
allocConfirmed: "FFD1FAE5",
|
||||
allocProposed: "FFFEF3C7",
|
||||
allocProposed: "FFFEF3C7",
|
||||
allocCompleted: "FFF3F4F6",
|
||||
};
|
||||
|
||||
@@ -55,8 +55,8 @@ function applyHeader(ws: ExcelJS.Worksheet, columns: ExcelJS.Column[]) {
|
||||
ws.columns = columns;
|
||||
const headerRow: AnyRow = ws.getRow(1);
|
||||
headerRow.eachCell((cell) => {
|
||||
cell.font = { bold: true, color: { argb: COLORS.headerFg }, size: 10 };
|
||||
cell.fill = { type: "pattern", pattern: "solid", fgColor: { argb: COLORS.headerBg } };
|
||||
cell.font = { bold: true, color: { argb: COLORS.headerFg }, size: 10 };
|
||||
cell.fill = { type: "pattern", pattern: "solid", fgColor: { argb: COLORS.headerBg } };
|
||||
cell.border = {
|
||||
bottom: { style: "thin", color: { argb: COLORS.border } },
|
||||
};
|
||||
@@ -73,9 +73,9 @@ function styleDataRows(ws: ExcelJS.Worksheet, startRow: number, totalRows: numbe
|
||||
if (isAlt) {
|
||||
cell.fill = { type: "pattern", pattern: "solid", fgColor: { argb: COLORS.altRow } };
|
||||
}
|
||||
cell.font = { size: 10 };
|
||||
cell.font = { size: 10 };
|
||||
cell.alignment = { vertical: "middle" };
|
||||
cell.border = {
|
||||
cell.border = {
|
||||
bottom: { style: "hair", color: { argb: COLORS.border } },
|
||||
};
|
||||
});
|
||||
@@ -89,22 +89,33 @@ function highlightCell(cell: ExcelJS.Cell, argb: string) {
|
||||
|
||||
function statusColor(status: string): string {
|
||||
switch (status) {
|
||||
case "COMPLETED": return COLORS.completed;
|
||||
case "ACTIVE": return COLORS.active;
|
||||
case "ON_HOLD": return COLORS.onHold;
|
||||
case "DRAFT": return COLORS.draft;
|
||||
case "CANCELLED": return COLORS.cancelled;
|
||||
default: return COLORS.draft;
|
||||
case "COMPLETED":
|
||||
return COLORS.completed;
|
||||
case "ACTIVE":
|
||||
return COLORS.active;
|
||||
case "ON_HOLD":
|
||||
return COLORS.onHold;
|
||||
case "DRAFT":
|
||||
return COLORS.draft;
|
||||
case "CANCELLED":
|
||||
return COLORS.cancelled;
|
||||
default:
|
||||
return COLORS.draft;
|
||||
}
|
||||
}
|
||||
|
||||
function allocStatusColor(status: string): string {
|
||||
switch (status) {
|
||||
case "ACTIVE": return COLORS.allocActive;
|
||||
case "CONFIRMED": return COLORS.allocConfirmed;
|
||||
case "PROPOSED": return COLORS.allocProposed;
|
||||
case "COMPLETED": return COLORS.allocCompleted;
|
||||
default: return COLORS.draft;
|
||||
case "ACTIVE":
|
||||
return COLORS.allocActive;
|
||||
case "CONFIRMED":
|
||||
return COLORS.allocConfirmed;
|
||||
case "PROPOSED":
|
||||
return COLORS.allocProposed;
|
||||
case "COMPLETED":
|
||||
return COLORS.allocCompleted;
|
||||
default:
|
||||
return COLORS.draft;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,29 +129,37 @@ async function buildResourcesSheet(wb: ExcelJS.Workbook) {
|
||||
});
|
||||
|
||||
applyHeader(ws, [
|
||||
{ header: "EID", key: "eid", width: 22 },
|
||||
{ header: "Display Name", key: "displayName", width: 24 },
|
||||
{ header: "Chapter", key: "chapter", width: 30 },
|
||||
{ header: "Type of Work", key: "typeOfWork", width: 30 },
|
||||
{ header: "City", key: "city", width: 16 },
|
||||
{ header: "Employee Type", key: "empType", width: 16 },
|
||||
{ header: "LCR (€/h)", key: "lcr", width: 12 },
|
||||
{ header: "UCR (€/h)", key: "ucr", width: 12 },
|
||||
{ header: "Chargeability %", key: "chargeability",width: 16 },
|
||||
{ header: "Mon h", key: "mon", width: 8 },
|
||||
{ header: "Tue h", key: "tue", width: 8 },
|
||||
{ header: "Wed h", key: "wed", width: 8 },
|
||||
{ header: "Thu h", key: "thu", width: 8 },
|
||||
{ header: "Fri h", key: "fri", width: 8 },
|
||||
{ header: "Skills", key: "skills", width: 40 },
|
||||
{ header: "EID", key: "eid", width: 22 },
|
||||
{ header: "Display Name", key: "displayName", width: 24 },
|
||||
{ header: "Chapter", key: "chapter", width: 30 },
|
||||
{ header: "Type of Work", key: "typeOfWork", width: 30 },
|
||||
{ header: "City", key: "city", width: 16 },
|
||||
{ header: "Employee Type", key: "empType", width: 16 },
|
||||
{ header: "LCR (€/h)", key: "lcr", width: 12 },
|
||||
{ header: "UCR (€/h)", key: "ucr", width: 12 },
|
||||
{ header: "Chargeability %", key: "chargeability", width: 16 },
|
||||
{ header: "Mon h", key: "mon", width: 8 },
|
||||
{ header: "Tue h", key: "tue", width: 8 },
|
||||
{ header: "Wed h", key: "wed", width: 8 },
|
||||
{ header: "Thu h", key: "thu", width: 8 },
|
||||
{ header: "Fri h", key: "fri", width: 8 },
|
||||
{ header: "Skills", key: "skills", width: 40 },
|
||||
] as ExcelJS.Column[]);
|
||||
|
||||
let rowIdx = 2;
|
||||
for (const res of resources) {
|
||||
const avail = res.availability as {
|
||||
monday: number; tuesday: number; wednesday: number; thursday: number; friday: number;
|
||||
monday: number;
|
||||
tuesday: number;
|
||||
wednesday: number;
|
||||
thursday: number;
|
||||
friday: number;
|
||||
};
|
||||
const dynFields = res.dynamicFields as {
|
||||
workType?: string;
|
||||
city?: string;
|
||||
employeeType?: string;
|
||||
};
|
||||
const dynFields = res.dynamicFields as { workType?: string; city?: string; employeeType?: string };
|
||||
const skills = (res.skills as Array<{ skill: string; proficiency: number }> | null) ?? [];
|
||||
const skillStr = skills.map((s) => `${s.skill} (${s.proficiency})`).join(", ");
|
||||
|
||||
@@ -177,17 +196,17 @@ async function buildProjectsSheet(wb: ExcelJS.Workbook) {
|
||||
});
|
||||
|
||||
applyHeader(ws, [
|
||||
{ header: "Short Code", key: "code", width: 14 },
|
||||
{ header: "Name", key: "name", width: 46 },
|
||||
{ header: "Client", key: "client", width: 14 },
|
||||
{ header: "Order Type", key: "orderType", width: 14 },
|
||||
{ header: "Alloc Type", key: "allocType", width: 12 },
|
||||
{ header: "Status", key: "status", width: 14 },
|
||||
{ header: "Start Date", key: "startDate", width: 14 },
|
||||
{ header: "End Date", key: "endDate", width: 14 },
|
||||
{ header: "Win Prob %", key: "winProb", width: 12 },
|
||||
{ header: "Budget (€)", key: "budget", width: 16 },
|
||||
{ header: "Responsible", key: "responsible", width: 24 },
|
||||
{ header: "Short Code", key: "code", width: 14 },
|
||||
{ header: "Name", key: "name", width: 46 },
|
||||
{ header: "Client", key: "client", width: 14 },
|
||||
{ header: "Order Type", key: "orderType", width: 14 },
|
||||
{ header: "Alloc Type", key: "allocType", width: 12 },
|
||||
{ header: "Status", key: "status", width: 14 },
|
||||
{ header: "Start Date", key: "startDate", width: 14 },
|
||||
{ header: "End Date", key: "endDate", width: 14 },
|
||||
{ header: "Win Prob %", key: "winProb", width: 12 },
|
||||
{ header: "Budget (€)", key: "budget", width: 16 },
|
||||
{ header: "Responsible", key: "responsible", width: 24 },
|
||||
] as ExcelJS.Column[]);
|
||||
|
||||
let rowIdx = 2;
|
||||
@@ -283,17 +302,17 @@ async function buildAllocationsSheet(wb: ExcelJS.Workbook) {
|
||||
});
|
||||
|
||||
applyHeader(ws, [
|
||||
{ header: "Project Code", key: "projCode", width: 14 },
|
||||
{ header: "Project Name", key: "projName", width: 40 },
|
||||
{ header: "EID", key: "eid", width: 20 },
|
||||
{ header: "Resource Name", key: "resource", width: 24 },
|
||||
{ header: "Chapter", key: "chapter", width: 28 },
|
||||
{ header: "Role", key: "role", width: 20 },
|
||||
{ header: "Start Date", key: "startDate", width: 14 },
|
||||
{ header: "End Date", key: "endDate", width: 14 },
|
||||
{ header: "h/Day", key: "hours", width: 8 },
|
||||
{ header: "Daily Cost (€)", key: "dailyCost", width: 16 },
|
||||
{ header: "Status", key: "status", width: 14 },
|
||||
{ header: "Project Code", key: "projCode", width: 14 },
|
||||
{ header: "Project Name", key: "projName", width: 40 },
|
||||
{ header: "EID", key: "eid", width: 20 },
|
||||
{ header: "Resource Name", key: "resource", width: 24 },
|
||||
{ header: "Chapter", key: "chapter", width: 28 },
|
||||
{ header: "Role", key: "role", width: 20 },
|
||||
{ header: "Start Date", key: "startDate", width: 14 },
|
||||
{ header: "End Date", key: "endDate", width: 14 },
|
||||
{ header: "h/Day", key: "hours", width: 8 },
|
||||
{ header: "Daily Cost (€)", key: "dailyCost", width: 16 },
|
||||
{ header: "Status", key: "status", width: 14 },
|
||||
] as ExcelJS.Column[]);
|
||||
|
||||
let rowIdx = 2;
|
||||
@@ -338,29 +357,29 @@ async function buildSummarySheet(wb: ExcelJS.Workbook) {
|
||||
] as ExcelJS.Column[];
|
||||
|
||||
const title = ws.getCell("A1");
|
||||
title.value = "CapaKraken — Seed Data Summary";
|
||||
title.font = { bold: true, size: 14, color: { argb: COLORS.headerBg } };
|
||||
title.value = "Nexus — Seed Data Summary";
|
||||
title.font = { bold: true, size: 14, color: { argb: COLORS.headerBg } };
|
||||
ws.mergeCells("A1:B1");
|
||||
ws.getRow(1).height = 28;
|
||||
|
||||
const generated = ws.getCell("A2");
|
||||
generated.value = `Generated: ${new Date().toISOString().split("T")[0]}`;
|
||||
generated.font = { italic: true, size: 10, color: { argb: "FF666666" } };
|
||||
generated.font = { italic: true, size: 10, color: { argb: "FF666666" } };
|
||||
ws.mergeCells("A2:B2");
|
||||
ws.getRow(2).height = 18;
|
||||
|
||||
const rows: [string, number | string][] = [
|
||||
["Resources", resourceCount],
|
||||
["Projects", projectCount],
|
||||
["Resources", resourceCount],
|
||||
["Projects", projectCount],
|
||||
["Allocations", allocationCount],
|
||||
];
|
||||
|
||||
rows.forEach(([label, value], i) => {
|
||||
const row = ws.getRow(4 + i);
|
||||
row.getCell(1).value = label;
|
||||
row.getCell(1).font = { bold: true, size: 10 };
|
||||
row.getCell(1).font = { bold: true, size: 10 };
|
||||
row.getCell(2).value = value;
|
||||
row.getCell(2).font = { size: 10 };
|
||||
row.getCell(2).font = { size: 10 };
|
||||
row.height = 20;
|
||||
});
|
||||
}
|
||||
@@ -371,8 +390,8 @@ async function main() {
|
||||
console.log("Connecting to database...");
|
||||
|
||||
const wb = new ExcelJS.Workbook();
|
||||
wb.creator = "CapaKraken";
|
||||
wb.created = new Date();
|
||||
wb.creator = "Nexus";
|
||||
wb.created = new Date();
|
||||
wb.modified = new Date();
|
||||
|
||||
console.log("Building sheets...");
|
||||
@@ -381,11 +400,14 @@ async function main() {
|
||||
await buildProjectsSheet(wb);
|
||||
await buildAllocationsSheet(wb);
|
||||
|
||||
const outPath = path.resolve(__dirname, "../../../samples/CapaKrakenExamples.xlsx");
|
||||
const outPath = path.resolve(__dirname, "../../../samples/NexusExamples.xlsx");
|
||||
await wb.xlsx.writeFile(outPath);
|
||||
console.log(`Excel written to: ${outPath}`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => { console.error(e); process.exit(1); })
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
Reference in New Issue
Block a user