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

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:
2026-05-21 16:28:40 +02:00
committed by Hartmut
parent d9a7ec0338
commit b41c1d2501
943 changed files with 24548 additions and 16832 deletions
+35 -35
View File
@@ -1,7 +1,7 @@
/**
* Updates CapaKrakenExamples.xlsx with missing columns and documentation.
* Adds: Display Name, Email, Skills, CapaKraken Notes columns to EID sheet.
* Adds: Start Date, End Date, Status, CapaKraken Notes columns to Projects sheet.
* Updates NexusExamples.xlsx with missing columns and documentation.
* Adds: Display Name, Email, Skills, Nexus Notes columns to EID sheet.
* Adds: Start Date, End Date, Status, Nexus Notes columns to Projects sheet.
*/
import ExcelJS from "exceljs";
@@ -11,7 +11,7 @@ import { dirname, join } from "path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const EXCEL_PATH = join(__dirname, "../../../samples/CapaKrakenExamples.xlsx");
const EXCEL_PATH = join(__dirname, "../../../samples/NexusExamples.xlsx");
// ─── Helpers ─────────────────────────────────────────────────────────────────
@@ -36,8 +36,8 @@ function computeSkillLabel(chapter, typeOfWork) {
return typeOfWork;
}
function computeCapaKrakenEid(eid) {
// In CapaKraken the EID stays as firstname.lastname (unique key)
function computeNexusEid(eid) {
// In Nexus the EID stays as firstname.lastname (unique key)
return eid;
}
@@ -139,28 +139,28 @@ async function main() {
// Column N: Display Name
// Column O: Email
// Column P: Skills (derived)
// Column Q: Description / Notes for CapaKraken
// Column Q: Description / Notes for Nexus
const newEidCols = [
{
col: 14, // N
header: "Display Name\n(auto-generated)",
doc: "Full display name derived from EID (firstname.lastname → Firstname Lastname). Used as the person's name in CapaKraken.",
doc: "Full display name derived from EID (firstname.lastname → Firstname Lastname). Used as the person's name in Nexus.",
},
{
col: 15, // O
header: "Email\n(generated)",
doc: "Generated email: firstname.lastname@capakraken.example. Required unique field in CapaKraken. Replace with real email in production.",
doc: "Generated email: firstname.lastname@capakraken.example. Required unique field in Nexus. Replace with real email in production.",
},
{
col: 16, // P
header: "Skills\n(derived from Chapter)",
doc: "Skill tags assigned based on Chapter + Type of Work. Format: 'SkillA | SkillB'. Stored as JSON array in CapaKraken with proficiency 1-5. Senior (LCR >= 118) -> 5, Mid-Senior (LCR >= 95) -> 4, Mid -> 3.",
doc: "Skill tags assigned based on Chapter + Type of Work. Format: 'SkillA | SkillB'. Stored as JSON array in Nexus with proficiency 1-5. Senior (LCR >= 118) -> 5, Mid-Senior (LCR >= 95) -> 4, Mid -> 3.",
},
{
col: 17, // Q
header: "CapaKraken Notes",
doc: "How data maps to CapaKraken:\n- EID = unique key (col A)\n- Chapter = chapter field\n- LCR / UCR -> multiply by 100 for integer cents (EUR85.00 -> 8500)\n- Hours fraction x 8 = daily availability hours\n- Chargeability -> multiply by 100 for % (0.75 -> 75%)\n- Employee type, City, Client Unit -> stored in dynamicFields JSONB",
header: "Nexus Notes",
doc: "How data maps to Nexus:\n- EID = unique key (col A)\n- Chapter = chapter field\n- LCR / UCR -> multiply by 100 for integer cents (EUR85.00 -> 8500)\n- Hours fraction x 8 = daily availability hours\n- Chargeability -> multiply by 100 for % (0.75 -> 75%)\n- Employee type, City, Client Unit -> stored in dynamicFields JSONB",
},
];
@@ -214,18 +214,18 @@ async function main() {
// Also add doc notes to existing header columns A-M in row 1
const eidExistingDocs = [
"Unique identifier. Used as EID in CapaKraken (no EMP-XXX prefix needed). e.g. steve.rogers",
"Team / department. Maps to 'chapter' field in CapaKraken.",
"Unique identifier. Used as EID in Nexus (no EMP-XXX prefix needed). e.g. steve.rogers",
"Team / department. Maps to 'chapter' field in Nexus.",
"Specialization within chapter. Stored in dynamicFields.workType.",
"Assigned client account. Stored in dynamicFields.clientUnit.",
"Unit-specific field. Currently unused — can be stored in dynamicFields.",
"Office city location. Stored in dynamicFields.city.",
"Employment type: Employee or Freelancer. Stored in dynamicFields.employeeType.",
"Loaded Cost Rate (LCR) in EUR/h. Multiply × 100 for CapaKraken cents. e.g. 133.77 → 13377",
"Loaded Cost Rate (LCR) in EUR/h. Multiply × 100 for Nexus cents. e.g. 133.77 → 13377",
"Unloaded/Utilization Cost Rate (UCR) in EUR/h. Multiply × 100 for cents.",
"FTE fraction (1.0 = 40h/week, 0.8 = 4 days, 0.5 = 20h/week). Combined with col K for availability JSON.",
"Available weekdays. 'all' = Mon-Fri. Specific days listed = only those days active at 8h.",
"Chargeability target as decimal. Multiply × 100 for CapaKraken % (0.75 → 75%).",
"Chargeability target as decimal. Multiply × 100 for Nexus % (0.75 → 75%).",
"(unused)",
];
for (let c = 1; c <= 13; c++) {
@@ -254,12 +254,12 @@ async function main() {
{
col: 19, // S
header: "Status\n(derived)",
doc: "CapaKraken status derived from 'is ordered' + win probability + date:\n• COMPLETED: ordered + 100% + past dates\n• ACTIVE: ordered + 100% + current/future\n• ON_HOLD: ordered but paused\n• DRAFT: not ordered or low win probability",
doc: "Nexus status derived from 'is ordered' + win probability + date:\n• COMPLETED: ordered + 100% + past dates\n• ACTIVE: ordered + 100% + current/future\n• ON_HOLD: ordered but paused\n• DRAFT: not ordered or low win probability",
},
{
col: 20, // T
header: "CapaKraken Notes",
doc: "How data maps to CapaKraken:\n• Col C (short code) → shortCode (unique key)\n• Col B → name\n• BD/CH/UN → OrderType: BD / CHARGEABLE / INTERNAL\n• Internal/External → allocationType: INT / EXT\n• Resource Costs (col I) × 100 = budgetCents in CapaKraken\n• Col H (chargability %) → stored in dynamicFields.chargeabilityPercent\n• Col J (person hours) → stored in dynamicFields.personHoursSold\n• Col O (classification) → stored in dynamicFields.classification",
header: "Nexus Notes",
doc: "How data maps to Nexus:\n• Col C (short code) → shortCode (unique key)\n• Col B → name\n• BD/CH/UN → OrderType: BD / CHARGEABLE / INTERNAL\n• Internal/External → allocationType: INT / EXT\n• Resource Costs (col I) × 100 = budgetCents in Nexus\n• Col H (chargability %) → stored in dynamicFields.chargeabilityPercent\n• Col J (person hours) → stored in dynamicFields.personHoursSold\n• Col O (classification) → stored in dynamicFields.classification",
},
];
@@ -286,21 +286,21 @@ async function main() {
const projExistingDocs = [
"Client Unit tag. e.g. [DAI]=Daimler, [PAG]=Porsche AG, [BMW], [JLR]=Jaguar Land Rover. Stored in dynamicFields.clientUnit.",
"Full project name → maps to CapaKraken 'name' field.",
"Short internal project code (5-6 chars) → maps to CapaKraken 'shortCode' (unique key). e.g. JLFJFL",
"Full project name → maps to Nexus 'name' field.",
"Short internal project code (5-6 chars) → maps to Nexus 'shortCode' (unique key). e.g. JLFJFL",
"'yes'/'no' — whether the project is formally ordered. Drives status: yes+100% → ACTIVE/COMPLETED.",
"Order type: BD=Business Development, CH=Chargeable, UN=Internal/Unordered. Maps to CapaKraken OrderType.",
"Win probability 0-100. Used in CapaKraken 'winProbability' field for pipeline forecasting.",
"Allocation type: Internal → INT, External → EXT. Maps to CapaKraken 'allocationType' field.",
"Order type: BD=Business Development, CH=Chargeable, UN=Internal/Unordered. Maps to Nexus OrderType.",
"Win probability 0-100. Used in Nexus 'winProbability' field for pipeline forecasting.",
"Allocation type: Internal → INT, External → EXT. Maps to Nexus 'allocationType' field.",
"Chargeability % as decimal. Stored in dynamicFields.chargeabilityPercent.",
"Planned resource cost in EUR. Multiply × 100 for CapaKraken budgetCents. e.g. 78799 → 7879900 cents.",
"Planned resource cost in EUR. Multiply × 100 for Nexus budgetCents. e.g. 78799 → 7879900 cents.",
"Person hours planned/sold. Stored in dynamicFields.personHoursSold for budget tracking.",
"Team staffing (empty in source). Would list assigned EIDs. Handled by Allocations in CapaKraken.",
"Team staffing (empty in source). Would list assigned EIDs. Handled by Allocations in Nexus.",
"Day project sold (empty in source). Could be stored as dynamicFields.dateSold.",
"Project start date (empty in source → synthesized in col Q). Maps to CapaKraken 'startDate'.",
"Project end date (empty in source → synthesized in col R). Maps to CapaKraken 'endDate'.",
"Project start date (empty in source → synthesized in col Q). Maps to Nexus 'startDate'.",
"Project end date (empty in source → synthesized in col R). Maps to Nexus 'endDate'.",
"Confidentiality: Confidential / Not Confidential. Stored in dynamicFields.classification.",
"Responsible EID / Owner (empty in source). Would map to a PM allocation in CapaKraken.",
"Responsible EID / Owner (empty in source). Would map to a PM allocation in Nexus.",
];
for (let c = 1; c <= 16; c++) {
const doc = projExistingDocs[c - 1];
@@ -329,15 +329,15 @@ async function main() {
projSheet.getColumn(19).width = 16;
projSheet.getColumn(20).width = 50;
// ─── Add a "CapaKraken Data Model" sheet ──────────────────────────────────
// ─── Add a "Nexus Data Model" sheet ──────────────────────────────────
let modelSheet = workbook.getWorksheet("CapaKraken Data Model");
let modelSheet = workbook.getWorksheet("Nexus Data Model");
if (!modelSheet) {
modelSheet = workbook.addWorksheet("CapaKraken Data Model");
modelSheet = workbook.addWorksheet("Nexus Data Model");
}
const modelData = [
["CapaKraken Data Model — Field Reference", "", "", "", ""],
["Nexus Data Model — Field Reference", "", "", "", ""],
["", "", "", "", ""],
["RESOURCE FIELDS", "", "", "", ""],
["Field", "Type", "Required", "Example", "Description"],
@@ -393,7 +393,7 @@ async function main() {
// Style title row
const titleCell = modelSheet.getCell(1, 1);
titleCell.font = { bold: true, size: 14, color: { argb: "FF4F46E5" } };
titleCell.value = "CapaKraken Data Model — Field Reference";
titleCell.value = "Nexus Data Model — Field Reference";
// Style section headers and field headers
const sectionRows = [3, 17, 31];
@@ -421,7 +421,7 @@ async function main() {
console.log(`✅ Excel updated: ${EXCEL_PATH}`);
console.log(" - EID_Informationen: added Display Name, Email, Skills, Notes columns");
console.log(" - Projektinfomartionen: added Start Date, End Date, Status, Notes columns");
console.log(" - Added new sheet: 'CapaKraken Data Model' (field reference)");
console.log(" - Added new sheet: 'Nexus Data Model' (field reference)");
}
main().catch((err) => {