feat: Nearshore-Ratio indicator per project

Engine (packages/engine):
- calculateShoringRatio() pure function: onshore/offshore hours,
  country breakdown, threshold check, weighted by hours not headcount
- 12 unit tests: empty, 100% onshore/offshore, mixed ratios,
  custom threshold, case-insensitive, unknown country, FTE weighting

Schema:
- Project.shoringThreshold (default 55%) — per-project configurable
- Project.onshoreCountryCode (default "DE") — configurable onshore country

API (project router):
- getShoringRatio query: loads assignments with resource.country,
  computes ratio, returns full breakdown
- update mutation: accepts shoringThreshold + onshoreCountryCode

UI:
- ShoringIndicator: stacked horizontal bar with country segments,
  severity badge (green/yellow/red), hover tooltip, dark theme
- ShoringBadge: mini colored dot + % for project list column
- ProjectModal: "Max Offshore %" number input
- Project detail: indicator after budget status card
- Project list: "Shoring" column (default hidden, toggleable)

AI Assistant:
- get_shoring_ratio tool: human-readable breakdown with threshold alert

Colors: green (<threshold-10), yellow (threshold-10 to threshold), red (>=threshold)
Default: 55% offshore threshold, "DE" as onshore country

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-26 11:45:50 +01:00
parent a9107add7b
commit 92a982b151
13 changed files with 721 additions and 42 deletions
+3 -1
View File
@@ -812,7 +812,9 @@ model Project {
startDate DateTime @db.Date
endDate DateTime @db.Date
status ProjectStatus @default(DRAFT)
responsiblePerson String?
responsiblePerson String?
shoringThreshold Int? @default(55) // Max offshore % before alert (0-100)
onshoreCountryCode String? @default("DE") // Country code considered "onshore"
color String? // Hex color for timeline display, e.g. "#3b82f6"
coverImageUrl String? @db.Text // Base64 data-URL for project cover art
coverFocusY Int @default(50) // Vertical focus point 0-100 (% from top)