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:
@@ -27,6 +27,7 @@ import { useViewPrefs } from "~/hooks/useViewPrefs.js";
|
||||
import { useRowOrder } from "~/hooks/useRowOrder.js";
|
||||
import { SortableColumnHeader } from "~/components/ui/SortableColumnHeader.js";
|
||||
import { DraggableTableRow } from "~/components/ui/DraggableTableRow.js";
|
||||
import { ShoringBadge } from "~/components/projects/ShoringIndicator.js";
|
||||
|
||||
import { PROJECT_STATUS_BADGE as STATUS_COLORS, ORDER_TYPE_BADGE as ORDER_TYPE_COLORS } from "~/lib/status-styles.js";
|
||||
|
||||
@@ -453,6 +454,12 @@ export function ProjectsClient() {
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
case "shoring":
|
||||
return (
|
||||
<td key={col.key} className="px-4 py-3 text-center">
|
||||
<ShoringBadge projectId={project.id} />
|
||||
</td>
|
||||
);
|
||||
case "responsible":
|
||||
return <td key={col.key} className="px-4 py-3 text-sm text-gray-500 dark:text-gray-400">—</td>;
|
||||
default:
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ProjectAssignmentsTable } from "~/components/projects/ProjectAssignment
|
||||
import { PROJECT_STATUS_BADGE as STATUS_COLORS, ORDER_TYPE_BADGE as ORDER_TYPE_COLORS } from "~/lib/status-styles.js";
|
||||
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
||||
import { CoverArtSection } from "~/components/projects/CoverArtSection.js";
|
||||
import { ShoringIndicator } from "~/components/projects/ShoringIndicator.js";
|
||||
|
||||
const EDIT_ROLES = new Set(["ADMIN", "MANAGER"]);
|
||||
|
||||
@@ -133,6 +134,9 @@ export default async function ProjectDetailPage({ params }: ProjectDetailPagePro
|
||||
{/* Budget status card (client component) */}
|
||||
<BudgetStatusCard projectId={project.id} />
|
||||
|
||||
{/* Nearshore ratio indicator (client component) */}
|
||||
<ShoringIndicator projectId={project.id} />
|
||||
|
||||
{/* Assignments table (client component with delete action) */}
|
||||
<ProjectAssignmentsTable assignments={project.assignments as never} />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user