feat(platform): checkpoint current implementation state
This commit is contained in:
@@ -10,6 +10,51 @@ import { WidgetFilterBar, type WidgetFilter } from "~/components/dashboard/Widge
|
||||
import { useWidgetFilterOptions } from "~/hooks/useWidgetFilterOptions.js";
|
||||
import { formatMoney } from "~/lib/format.js";
|
||||
|
||||
type ProjectHealthRow = {
|
||||
id: string;
|
||||
projectName: string;
|
||||
shortCode: string;
|
||||
status: string;
|
||||
clientId: string | null;
|
||||
clientName: string | null;
|
||||
budgetHealth: number;
|
||||
staffingHealth: number;
|
||||
timelineHealth: number;
|
||||
compositeScore: number;
|
||||
budgetCents?: number | null;
|
||||
spentCents?: number;
|
||||
remainingBudgetCents?: number | null;
|
||||
budgetUtilizationPercent?: number | null;
|
||||
demandHeadcountTotal?: number;
|
||||
demandHeadcountFilled?: number;
|
||||
demandHeadcountOpen?: number;
|
||||
demandRequirementCount?: number;
|
||||
plannedEndDate?: string | Date | null;
|
||||
daysUntilEndDate?: number | null;
|
||||
timelineStatus?: "ON_TRACK" | "DUE_SOON" | "OVERDUE" | "UNSCHEDULED" | null;
|
||||
calendarLocations?: Array<{
|
||||
countryCode?: string | null;
|
||||
countryName?: string | null;
|
||||
federalState?: string | null;
|
||||
metroCityName?: string | null;
|
||||
assignmentCount: number;
|
||||
spentCents: number;
|
||||
}>;
|
||||
derivation?: {
|
||||
periodStart: string;
|
||||
periodEnd: string;
|
||||
calendarContextCount: number;
|
||||
holidayAwareAssignmentCount: number;
|
||||
fallbackAssignmentCount: number;
|
||||
baseSpentCents: number;
|
||||
adjustedSpentCents: number;
|
||||
publicHolidayDayEquivalent: number;
|
||||
publicHolidayCostDeductionCents: number;
|
||||
absenceDayEquivalent: number;
|
||||
absenceCostDeductionCents: number;
|
||||
} | null;
|
||||
};
|
||||
|
||||
function healthDot(value: number): string {
|
||||
if (value >= 70) return "bg-green-500";
|
||||
if (value >= 40) return "bg-amber-400";
|
||||
@@ -69,6 +114,11 @@ function formatLocation(location: {
|
||||
return parts.length > 0 ? parts.join(" / ") : "No calendar context";
|
||||
}
|
||||
|
||||
function formatDayEquivalent(value?: number | null): string {
|
||||
if (value == null) return "—";
|
||||
return Number.isInteger(value) ? String(value) : value.toFixed(1);
|
||||
}
|
||||
|
||||
export function ProjectHealthWidget({ config, onConfigChange }: WidgetProps) {
|
||||
const showDetails = config.showDetails === true;
|
||||
const { clients } = useWidgetFilterOptions({ clients: true });
|
||||
@@ -90,7 +140,7 @@ export function ProjectHealthWidget({ config, onConfigChange }: WidgetProps) {
|
||||
const clientId = (config.clientId as string) ?? "";
|
||||
|
||||
const rows = useMemo(() => {
|
||||
const all = data ?? [];
|
||||
const all = (data ?? []) as ProjectHealthRow[];
|
||||
return all.filter((r) => {
|
||||
if (search && !r.projectName.toLowerCase().includes(search) && !r.shortCode.toLowerCase().includes(search)) return false;
|
||||
if (clientId && r.clientId !== clientId) return false;
|
||||
@@ -174,6 +224,22 @@ export function ProjectHealthWidget({ config, onConfigChange }: WidgetProps) {
|
||||
<div>
|
||||
Timeline: {formatShortDate(row.plannedEndDate)} · {formatTimeline(row.daysUntilEndDate, row.timelineStatus)}
|
||||
</div>
|
||||
{row.derivation ? (
|
||||
<>
|
||||
<div>
|
||||
Spend basis: {row.derivation.calendarContextCount} calendar bases · {row.derivation.holidayAwareAssignmentCount} holiday-aware
|
||||
{row.derivation.fallbackAssignmentCount > 0 ? ` · ${row.derivation.fallbackAssignmentCount} fallback` : ""}
|
||||
</div>
|
||||
<div>
|
||||
Base {formatMoney(row.derivation.baseSpentCents)} {"->"} Effective {formatMoney(row.derivation.adjustedSpentCents)}
|
||||
</div>
|
||||
<div>
|
||||
Holidays -{formatMoney(row.derivation.publicHolidayCostDeductionCents)} ({formatDayEquivalent(row.derivation.publicHolidayDayEquivalent)}d)
|
||||
{" · "}
|
||||
Absence -{formatMoney(row.derivation.absenceCostDeductionCents)} ({formatDayEquivalent(row.derivation.absenceDayEquivalent)}d)
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{(row.calendarLocations ?? []).length > 0 ? (
|
||||
<div>
|
||||
Calendar basis: {(row.calendarLocations ?? [])
|
||||
|
||||
Reference in New Issue
Block a user