@burdenoff/microfe-crewfoundry 2026.1102.2 → 2026.1107.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/dist/crewfoundry/components/WorkforceForecastCard.d.ts +17 -0
  2. package/dist/crewfoundry/components/WorkforceForecastCard.d.ts.map +1 -0
  3. package/dist/crewfoundry/components/WorkforceForecastCard.js +222 -0
  4. package/dist/crewfoundry/components/WorkforceForecastCard.js.map +1 -0
  5. package/dist/crewfoundry/components/field/DispatchBoardCard.d.ts.map +1 -1
  6. package/dist/crewfoundry/components/field/DispatchBoardCard.js +5 -2
  7. package/dist/crewfoundry/components/field/DispatchBoardCard.js.map +1 -1
  8. package/dist/crewfoundry/components/field/MyDispatchItem.js +2 -2
  9. package/dist/crewfoundry/components/field/MyDispatchItem.js.map +1 -1
  10. package/dist/crewfoundry/components/field/MyShiftItem.d.ts.map +1 -1
  11. package/dist/crewfoundry/components/field/MyShiftItem.js +5 -2
  12. package/dist/crewfoundry/components/field/MyShiftItem.js.map +1 -1
  13. package/dist/crewfoundry/components/field/ShiftsCard.d.ts.map +1 -1
  14. package/dist/crewfoundry/components/field/ShiftsCard.js +5 -2
  15. package/dist/crewfoundry/components/field/ShiftsCard.js.map +1 -1
  16. package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.d.ts +3 -1
  17. package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.d.ts.map +1 -1
  18. package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js +154 -145
  19. package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js.map +1 -1
  20. package/dist/crewfoundry/store/crewfoundryStore.d.ts +57 -0
  21. package/dist/crewfoundry/store/crewfoundryStore.d.ts.map +1 -1
  22. package/dist/crewfoundry/store/crewfoundryStore.js +96 -70
  23. package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
  24. package/package.json +1 -1
@@ -0,0 +1,17 @@
1
+ import { CrewWorkforceForecast } from '../store/crewfoundryStore';
2
+
3
+ interface WorkforceForecastCardProps {
4
+ forecast: CrewWorkforceForecast | null;
5
+ loading: boolean;
6
+ error: string | null;
7
+ onRetry: () => void;
8
+ }
9
+ /**
10
+ * BOFF-10826 — trained attrition model status and the 3/6/12-month headcount
11
+ * and cost forecast. Rendered next to the deterministic BOFF-4208 sections;
12
+ * it has its own loading and error state so a forecast failure never hides
13
+ * the rest of the dashboard.
14
+ */
15
+ export declare function WorkforceForecastCard({ forecast, loading, error, onRetry, }: WorkforceForecastCardProps): import("react").JSX.Element;
16
+ export {};
17
+ //# sourceMappingURL=WorkforceForecastCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkforceForecastCard.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/components/WorkforceForecastCard.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAIV,qBAAqB,EACtB,MAAM,2BAA2B,CAAC;AAoHnC,UAAU,0BAA0B;IAClC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,OAAO,EACP,KAAK,EACL,OAAO,GACR,EAAE,0BAA0B,+BAqF5B"}
@@ -0,0 +1,222 @@
1
+ import { jsx as e, jsxs as t } from "react/jsx-runtime";
2
+ import { Badge as n, Button as r, Card as i, CardContent as a, CardDescription as o, CardHeader as s, CardTitle as c, ResponsiveTable as l, Spinner as u } from "@burdenoff/fe-libs/ui";
3
+ //#region src/crewfoundry/components/WorkforceForecastCard.tsx
4
+ var d = 5, f = {
5
+ ACTIVE: "Trained model in use",
6
+ HEURISTIC_PREFERRED: "Heuristic in use: model lost the backtest",
7
+ INSUFFICIENT_DATA: "Heuristic in use: not enough history yet"
8
+ }, p = {
9
+ ACTIVE: "success",
10
+ HEURISTIC_PREFERRED: "secondary",
11
+ INSUFFICIENT_DATA: "outline"
12
+ };
13
+ function m(e) {
14
+ let t = e < 0 ? "-" : "", n = Math.abs(e);
15
+ return n >= 1e6 ? `${t}$${(n / 1e6).toFixed(1)}M` : n >= 1e3 ? `${t}$${(n / 1e3).toFixed(0)}K` : `${t}$${Math.round(n)}`;
16
+ }
17
+ function h(e) {
18
+ return Number.isInteger(e) ? String(e) : e.toFixed(1);
19
+ }
20
+ var g = [
21
+ {
22
+ key: "months",
23
+ header: "Horizon",
24
+ priority: "primary",
25
+ cell: (e) => /* @__PURE__ */ t("span", {
26
+ className: "font-medium",
27
+ children: [e.months, " months"]
28
+ })
29
+ },
30
+ {
31
+ key: "headcount",
32
+ header: "Headcount (90% range)",
33
+ priority: "primary",
34
+ cell: (e) => /* @__PURE__ */ t("span", {
35
+ className: "tabular-nums md:block md:text-right",
36
+ children: [
37
+ h(e.projectedHeadcount),
38
+ " ",
39
+ /* @__PURE__ */ t("span", {
40
+ className: "text-xs text-text-secondary",
41
+ children: [
42
+ "(",
43
+ h(e.low),
44
+ " to ",
45
+ h(e.high),
46
+ ")"
47
+ ]
48
+ })
49
+ ]
50
+ })
51
+ },
52
+ {
53
+ key: "flows",
54
+ header: "Hires / exits",
55
+ priority: "secondary",
56
+ cell: (e) => /* @__PURE__ */ t("span", {
57
+ className: "tabular-nums md:block md:text-right",
58
+ children: [
59
+ "+",
60
+ h(e.expectedHires),
61
+ " / -",
62
+ h(e.expectedExits)
63
+ ]
64
+ })
65
+ },
66
+ {
67
+ key: "monthly",
68
+ header: "Monthly cost",
69
+ priority: "secondary",
70
+ cell: (t) => /* @__PURE__ */ e("span", {
71
+ className: "tabular-nums md:block md:text-right",
72
+ children: m(t.projectedMonthlyCost)
73
+ })
74
+ },
75
+ {
76
+ key: "cumulative",
77
+ header: "Cumulative cost",
78
+ priority: "secondary",
79
+ cell: (t) => /* @__PURE__ */ e("span", {
80
+ className: "tabular-nums md:block md:text-right",
81
+ children: m(t.cumulativeCost)
82
+ })
83
+ }
84
+ ], _ = [
85
+ {
86
+ key: "employee",
87
+ header: "Employee",
88
+ priority: "primary",
89
+ cell: (n) => /* @__PURE__ */ t("div", { children: [/* @__PURE__ */ e("p", {
90
+ className: "font-medium",
91
+ children: n.employeeName
92
+ }), /* @__PURE__ */ e("p", {
93
+ className: "text-xs text-text-secondary",
94
+ children: n.department
95
+ })] })
96
+ },
97
+ {
98
+ key: "probability",
99
+ header: "Exit probability",
100
+ priority: "primary",
101
+ cell: (t) => /* @__PURE__ */ e("span", {
102
+ className: "tabular-nums md:block md:text-right",
103
+ children: t.predictedExitProbability === null ? "—" : `${Math.round(t.predictedExitProbability * 100)}%`
104
+ })
105
+ },
106
+ {
107
+ key: "drivers",
108
+ header: "Drivers",
109
+ priority: "secondary",
110
+ cell: (t) => /* @__PURE__ */ e("span", {
111
+ className: "text-sm text-text-secondary",
112
+ children: t.drivers.length > 0 ? t.drivers.join(", ") : "No risk signals"
113
+ })
114
+ }
115
+ ];
116
+ function v({ forecast: v, loading: y, error: b, onRetry: x }) {
117
+ let S = v?.attrition ?? null, C = v?.headcount ?? null, w = S?.model.status === "ACTIVE", T = w ? S.predictions.slice(0, d) : [];
118
+ return /* @__PURE__ */ t(i, {
119
+ "data-testid": "workforce-forecast",
120
+ children: [/* @__PURE__ */ t(s, { children: [/* @__PURE__ */ e(c, { children: "Workforce forecast" }), /* @__PURE__ */ e(o, { children: "Headcount and payroll cost projected from your own hiring velocity and exit rate, and an attrition model trained on this workspace's history only. The model replaces the heuristic only when it beats it on a held-out period." })] }), /* @__PURE__ */ t(a, {
121
+ className: "space-y-5",
122
+ children: [
123
+ b ? /* @__PURE__ */ t("div", {
124
+ className: "flex flex-wrap items-center justify-between gap-3",
125
+ children: [/* @__PURE__ */ e("p", {
126
+ className: "text-sm text-status-error-text",
127
+ children: b
128
+ }), /* @__PURE__ */ e(r, {
129
+ variant: "outline",
130
+ size: "sm",
131
+ onClick: x,
132
+ children: "Try again"
133
+ })]
134
+ }) : null,
135
+ !v && y ? /* @__PURE__ */ e("div", {
136
+ className: "flex min-h-[8rem] items-center justify-center",
137
+ children: /* @__PURE__ */ e(u, { size: "md" })
138
+ }) : null,
139
+ C ? /* @__PURE__ */ t("section", {
140
+ className: "space-y-3",
141
+ "data-testid": "headcount-forecast",
142
+ children: [
143
+ /* @__PURE__ */ t("div", {
144
+ className: "flex flex-wrap items-center gap-2",
145
+ children: [/* @__PURE__ */ e("h3", {
146
+ className: "text-sm font-semibold",
147
+ children: "Headcount and cost"
148
+ }), C.status === "LIMITED_HISTORY" ? /* @__PURE__ */ e(n, {
149
+ variant: "outline",
150
+ children: "Limited history"
151
+ }) : null]
152
+ }),
153
+ /* @__PURE__ */ t("p", {
154
+ className: "text-sm text-text-secondary",
155
+ children: [
156
+ C.currentHeadcount,
157
+ " active today, ",
158
+ C.hiresPerMonth.toFixed(1),
159
+ " hires a month and a ",
160
+ (C.monthlyExitRate * 100).toFixed(1),
161
+ "% monthly exit rate over the last ",
162
+ h(C.observationMonths),
163
+ " months. Current payroll",
164
+ " ",
165
+ m(C.currentMonthlyCost),
166
+ " a month; ",
167
+ C.openRequisitions,
168
+ " open role",
169
+ C.openRequisitions === 1 ? "" : "s",
170
+ " shown for context."
171
+ ]
172
+ }),
173
+ /* @__PURE__ */ e(l, {
174
+ columns: g,
175
+ data: C.horizons,
176
+ rowKey: (e) => String(e.months)
177
+ })
178
+ ]
179
+ }) : null,
180
+ S ? /* @__PURE__ */ t("section", {
181
+ className: "space-y-3",
182
+ "data-testid": "attrition-model",
183
+ children: [
184
+ /* @__PURE__ */ t("div", {
185
+ className: "flex flex-wrap items-center gap-2",
186
+ children: [/* @__PURE__ */ e("h3", {
187
+ className: "text-sm font-semibold",
188
+ children: "Attrition model"
189
+ }), /* @__PURE__ */ e(n, {
190
+ variant: p[S.model.status],
191
+ "data-testid": "attrition-model-status",
192
+ children: f[S.model.status]
193
+ })]
194
+ }),
195
+ /* @__PURE__ */ e("p", {
196
+ className: "text-sm text-text-secondary",
197
+ children: S.model.reason
198
+ }),
199
+ w && T.length > 0 ? /* @__PURE__ */ e(l, {
200
+ columns: _,
201
+ data: T,
202
+ rowKey: (e) => e.employeeId
203
+ }) : null,
204
+ /* @__PURE__ */ t("p", {
205
+ className: "text-xs text-text-secondary",
206
+ children: [
207
+ "Probability of leaving within ",
208
+ S.model.horizonDays,
209
+ " days.",
210
+ S.model.excludedFeatures.length > 0 ? ` Never used: ${S.model.excludedFeatures.join("; ")}.` : null
211
+ ]
212
+ })
213
+ ]
214
+ }) : null
215
+ ]
216
+ })]
217
+ });
218
+ }
219
+ //#endregion
220
+ export { v as WorkforceForecastCard };
221
+
222
+ //# sourceMappingURL=WorkforceForecastCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkforceForecastCard.js","names":[],"sources":["../../../src/crewfoundry/components/WorkforceForecastCard.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n ResponsiveTable,\n type ResponsiveTableColumn,\n Spinner,\n} from '@burdenoff/fe-libs/ui';\nimport type {\n CrewAttritionModelStatus,\n CrewAttritionPrediction,\n CrewHeadcountForecastHorizon,\n CrewWorkforceForecast,\n} from '../store/crewfoundryStore';\n\n/** Most at-risk employees listed when the trained model is in use. */\nconst MAX_PREDICTION_ROWS = 5;\n\nconst MODEL_STATUS_LABEL: Record<CrewAttritionModelStatus, string> = {\n ACTIVE: 'Trained model in use',\n HEURISTIC_PREFERRED: 'Heuristic in use: model lost the backtest',\n INSUFFICIENT_DATA: 'Heuristic in use: not enough history yet',\n};\n\nconst MODEL_STATUS_VARIANT: Record<CrewAttritionModelStatus, 'success' | 'secondary' | 'outline'> =\n {\n ACTIVE: 'success',\n HEURISTIC_PREFERRED: 'secondary',\n INSUFFICIENT_DATA: 'outline',\n };\n\nfunction formatMoney(value: number): string {\n const sign = value < 0 ? '-' : '';\n const abs = Math.abs(value);\n if (abs >= 1_000_000) return `${sign}$${(abs / 1_000_000).toFixed(1)}M`;\n if (abs >= 1_000) return `${sign}$${(abs / 1_000).toFixed(0)}K`;\n return `${sign}$${Math.round(abs)}`;\n}\n\nfunction formatCount(value: number): string {\n return Number.isInteger(value) ? String(value) : value.toFixed(1);\n}\n\nconst horizonColumns: ResponsiveTableColumn<CrewHeadcountForecastHorizon>[] = [\n {\n key: 'months',\n header: 'Horizon',\n priority: 'primary',\n cell: (row) => <span className=\"font-medium\">{row.months} months</span>,\n },\n {\n key: 'headcount',\n header: 'Headcount (90% range)',\n priority: 'primary',\n cell: (row) => (\n <span className=\"tabular-nums md:block md:text-right\">\n {formatCount(row.projectedHeadcount)}{' '}\n <span className=\"text-xs text-text-secondary\">\n ({formatCount(row.low)} to {formatCount(row.high)})\n </span>\n </span>\n ),\n },\n {\n key: 'flows',\n header: 'Hires / exits',\n priority: 'secondary',\n cell: (row) => (\n <span className=\"tabular-nums md:block md:text-right\">\n +{formatCount(row.expectedHires)} / -{formatCount(row.expectedExits)}\n </span>\n ),\n },\n {\n key: 'monthly',\n header: 'Monthly cost',\n priority: 'secondary',\n cell: (row) => (\n <span className=\"tabular-nums md:block md:text-right\">\n {formatMoney(row.projectedMonthlyCost)}\n </span>\n ),\n },\n {\n key: 'cumulative',\n header: 'Cumulative cost',\n priority: 'secondary',\n cell: (row) => (\n <span className=\"tabular-nums md:block md:text-right\">{formatMoney(row.cumulativeCost)}</span>\n ),\n },\n];\n\nconst predictionColumns: ResponsiveTableColumn<CrewAttritionPrediction>[] = [\n {\n key: 'employee',\n header: 'Employee',\n priority: 'primary',\n cell: (row) => (\n <div>\n <p className=\"font-medium\">{row.employeeName}</p>\n <p className=\"text-xs text-text-secondary\">{row.department}</p>\n </div>\n ),\n },\n {\n key: 'probability',\n header: 'Exit probability',\n priority: 'primary',\n cell: (row) => (\n <span className=\"tabular-nums md:block md:text-right\">\n {row.predictedExitProbability === null\n ? '—'\n : `${Math.round(row.predictedExitProbability * 100)}%`}\n </span>\n ),\n },\n {\n key: 'drivers',\n header: 'Drivers',\n priority: 'secondary',\n cell: (row) => (\n <span className=\"text-sm text-text-secondary\">\n {row.drivers.length > 0 ? row.drivers.join(', ') : 'No risk signals'}\n </span>\n ),\n },\n];\n\ninterface WorkforceForecastCardProps {\n forecast: CrewWorkforceForecast | null;\n loading: boolean;\n error: string | null;\n onRetry: () => void;\n}\n\n/**\n * BOFF-10826 — trained attrition model status and the 3/6/12-month headcount\n * and cost forecast. Rendered next to the deterministic BOFF-4208 sections;\n * it has its own loading and error state so a forecast failure never hides\n * the rest of the dashboard.\n */\nexport function WorkforceForecastCard({\n forecast,\n loading,\n error,\n onRetry,\n}: WorkforceForecastCardProps) {\n const attrition = forecast?.attrition ?? null;\n const headcount = forecast?.headcount ?? null;\n const modelActive = attrition?.model.status === 'ACTIVE';\n const topPredictions = modelActive ? attrition.predictions.slice(0, MAX_PREDICTION_ROWS) : [];\n\n return (\n <Card data-testid=\"workforce-forecast\">\n <CardHeader>\n <CardTitle>Workforce forecast</CardTitle>\n <CardDescription>\n Headcount and payroll cost projected from your own hiring velocity and exit rate, and an\n attrition model trained on this workspace&apos;s history only. The model replaces the\n heuristic only when it beats it on a held-out period.\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {error ? (\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <p className=\"text-sm text-status-error-text\">{error}</p>\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry}>\n Try again\n </Button>\n </div>\n ) : null}\n\n {!forecast && loading ? (\n <div className=\"flex min-h-[8rem] items-center justify-center\">\n <Spinner size=\"md\" />\n </div>\n ) : null}\n\n {headcount ? (\n <section className=\"space-y-3\" data-testid=\"headcount-forecast\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"text-sm font-semibold\">Headcount and cost</h3>\n {headcount.status === 'LIMITED_HISTORY' ? (\n <Badge variant=\"outline\">Limited history</Badge>\n ) : null}\n </div>\n <p className=\"text-sm text-text-secondary\">\n {headcount.currentHeadcount} active today, {headcount.hiresPerMonth.toFixed(1)} hires\n a month and a {(headcount.monthlyExitRate * 100).toFixed(1)}% monthly exit rate over\n the last {formatCount(headcount.observationMonths)} months. Current payroll{' '}\n {formatMoney(headcount.currentMonthlyCost)} a month; {headcount.openRequisitions} open\n role{headcount.openRequisitions === 1 ? '' : 's'} shown for context.\n </p>\n <ResponsiveTable\n columns={horizonColumns}\n data={headcount.horizons}\n rowKey={(row) => String(row.months)}\n />\n </section>\n ) : null}\n\n {attrition ? (\n <section className=\"space-y-3\" data-testid=\"attrition-model\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"text-sm font-semibold\">Attrition model</h3>\n <Badge\n variant={MODEL_STATUS_VARIANT[attrition.model.status]}\n data-testid=\"attrition-model-status\"\n >\n {MODEL_STATUS_LABEL[attrition.model.status]}\n </Badge>\n </div>\n <p className=\"text-sm text-text-secondary\">{attrition.model.reason}</p>\n {modelActive && topPredictions.length > 0 ? (\n <ResponsiveTable\n columns={predictionColumns}\n data={topPredictions}\n rowKey={(row) => row.employeeId}\n />\n ) : null}\n <p className=\"text-xs text-text-secondary\">\n Probability of leaving within {attrition.model.horizonDays} days.\n {attrition.model.excludedFeatures.length > 0\n ? ` Never used: ${attrition.model.excludedFeatures.join('; ')}.`\n : null}\n </p>\n </section>\n ) : null}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DispatchBoardCard.d.ts","sourceRoot":"","sources":["../../../../src/crewfoundry/components/field/DispatchBoardCard.tsx"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAS5B,UAAU,sBAAsB;IAC9B,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAC3C,YAAY,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACrE,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;CAC5C;AAwDD,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,EAAE,sBAAsB,+BAiDnF"}
1
+ {"version":3,"file":"DispatchBoardCard.d.ts","sourceRoot":"","sources":["../../../../src/crewfoundry/components/field/DispatchBoardCard.tsx"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAS5B,UAAU,sBAAsB;IAC9B,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAC3C,YAAY,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACrE,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;CAC5C;AA0DD,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,EAAE,sBAAsB,+BAiDnF"}
@@ -20,9 +20,12 @@ function v({ dispatch: e, canAssign: n, onAssign: d, onTransition: f, onRemove:
20
20
  /* @__PURE__ */ l("div", {
21
21
  className: "flex items-start justify-between gap-2",
22
22
  children: [/* @__PURE__ */ c("p", {
23
- className: "font-medium text-text-primary",
23
+ className: "min-w-0 break-words font-medium text-text-primary",
24
24
  children: e.title
25
- }), /* @__PURE__ */ c(t, { priority: e.priority })]
25
+ }), /* @__PURE__ */ c("span", {
26
+ className: "shrink-0",
27
+ children: /* @__PURE__ */ c(t, { priority: e.priority })
28
+ })]
26
29
  }),
27
30
  /* @__PURE__ */ l("p", {
28
31
  className: "text-xs text-text-muted",
@@ -1 +1 @@
1
- {"version":3,"file":"DispatchBoardCard.js","names":[],"sources":["../../../../src/crewfoundry/components/field/DispatchBoardCard.tsx"],"sourcesContent":["import {\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Trash2, UserCheck } from 'lucide-react';\nimport {\n type CrewDispatch,\n type DispatchStatus,\n MANAGER_NEXT_STATUS,\n STATUS_ACTION_LABEL,\n} from '../../lib/fieldOps';\nimport { formatEnumLabel } from '../../utils/formatEnumLabel';\nimport { DispatchPriorityBadge, DispatchStatusBadge } from './FieldBadges';\nimport { formatWhen } from './fieldFormat';\n\nconst BOARD_COLUMNS: DispatchStatus[] = ['OPEN', 'ASSIGNED', 'EN_ROUTE', 'ON_SITE'];\nconst isClosed = (dispatch: CrewDispatch): boolean =>\n dispatch.status === 'COMPLETED' || dispatch.status === 'CANCELLED';\n\ninterface DispatchBoardCardProps {\n dispatches: CrewDispatch[];\n canAssign: boolean;\n onAssign: (dispatch: CrewDispatch) => void;\n onTransition: (dispatch: CrewDispatch, next: DispatchStatus) => void;\n onRemove: (dispatch: CrewDispatch) => void;\n}\n\nfunction DispatchTile({\n dispatch,\n canAssign,\n onAssign,\n onTransition,\n onRemove,\n}: Omit<DispatchBoardCardProps, 'dispatches'> & { dispatch: CrewDispatch }) {\n const assignable = dispatch.status === 'OPEN' || dispatch.status === 'ASSIGNED';\n return (\n <div className=\"space-y-2 rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"font-medium text-text-primary\">{dispatch.title}</p>\n <DispatchPriorityBadge priority={dispatch.priority} />\n </div>\n <p className=\"text-xs text-text-muted\">\n {dispatch.employeeName ?? 'Unassigned'}\n {dispatch.siteName ? ` · ${dispatch.siteName}` : ''}\n {dispatch.dueAt ? ` · due ${formatWhen(dispatch.dueAt)}` : ''}\n </p>\n <div className=\"flex flex-wrap gap-1.5\">\n {assignable ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={!canAssign}\n onClick={() => onAssign(dispatch)}\n >\n <UserCheck className=\"mr-1 h-3.5 w-3.5\" />\n {dispatch.status === 'OPEN' ? 'Assign' : 'Reassign'}\n </Button>\n ) : null}\n {MANAGER_NEXT_STATUS[dispatch.status].map((next) => (\n <Button\n key={next}\n size=\"sm\"\n variant={next === 'CANCELLED' ? 'ghost' : 'outline'}\n onClick={() => onTransition(dispatch, next)}\n >\n {STATUS_ACTION_LABEL[next]}\n </Button>\n ))}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={`Remove ${dispatch.title}`}\n onClick={() => onRemove(dispatch)}\n >\n <Trash2 className=\"h-3.5 w-3.5 text-status-error-fg\" />\n </Button>\n </div>\n </div>\n );\n}\n\n/** Work-order board by status, plus the most recent closed work orders (BOFF-4205). */\nexport function DispatchBoardCard({ dispatches, ...actions }: DispatchBoardCardProps) {\n const closed = dispatches.filter(isClosed);\n return (\n <Card data-testid=\"field-dispatch-board\">\n <CardHeader>\n <CardTitle className=\"text-base\">Work orders</CardTitle>\n <CardDescription>\n {dispatches.filter((dispatch) => dispatch.status === 'COMPLETED').length} completed ·{' '}\n {dispatches.length - closed.length} in flight\n </CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-4\">\n {BOARD_COLUMNS.map((column) => {\n const items = dispatches.filter((dispatch) => dispatch.status === column);\n return (\n <section key={column} aria-label={formatEnumLabel(column)} className=\"space-y-2\">\n <h2 className=\"text-sm font-medium text-text-secondary\">\n {formatEnumLabel(column)} ({items.length})\n </h2>\n {items.map((dispatch) => (\n <DispatchTile key={dispatch.id} dispatch={dispatch} {...actions} />\n ))}\n </section>\n );\n })}\n </div>\n {closed.length > 0 ? (\n <div className=\"mt-4 space-y-1\">\n <h2 className=\"text-sm font-medium text-text-secondary\">Closed</h2>\n <ul className=\"space-y-1\">\n {closed.slice(0, 10).map((dispatch) => (\n <li\n key={dispatch.id}\n className=\"flex flex-wrap items-center gap-2 text-sm text-text-secondary\"\n >\n <DispatchStatusBadge status={dispatch.status} />\n <span className=\"text-text-primary\">{dispatch.title}</span>\n <span>{dispatch.employeeName ?? ''}</span>\n <span>{formatWhen(dispatch.completedAt ?? dispatch.createdAt)}</span>\n {dispatch.completionNotes ? <span>· {dispatch.completionNotes}</span> : null}\n </li>\n ))}\n </ul>\n </div>\n ) : null}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DispatchBoardCard.js","names":[],"sources":["../../../../src/crewfoundry/components/field/DispatchBoardCard.tsx"],"sourcesContent":["import {\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Trash2, UserCheck } from 'lucide-react';\nimport {\n type CrewDispatch,\n type DispatchStatus,\n MANAGER_NEXT_STATUS,\n STATUS_ACTION_LABEL,\n} from '../../lib/fieldOps';\nimport { formatEnumLabel } from '../../utils/formatEnumLabel';\nimport { DispatchPriorityBadge, DispatchStatusBadge } from './FieldBadges';\nimport { formatWhen } from './fieldFormat';\n\nconst BOARD_COLUMNS: DispatchStatus[] = ['OPEN', 'ASSIGNED', 'EN_ROUTE', 'ON_SITE'];\nconst isClosed = (dispatch: CrewDispatch): boolean =>\n dispatch.status === 'COMPLETED' || dispatch.status === 'CANCELLED';\n\ninterface DispatchBoardCardProps {\n dispatches: CrewDispatch[];\n canAssign: boolean;\n onAssign: (dispatch: CrewDispatch) => void;\n onTransition: (dispatch: CrewDispatch, next: DispatchStatus) => void;\n onRemove: (dispatch: CrewDispatch) => void;\n}\n\nfunction DispatchTile({\n dispatch,\n canAssign,\n onAssign,\n onTransition,\n onRemove,\n}: Omit<DispatchBoardCardProps, 'dispatches'> & { dispatch: CrewDispatch }) {\n const assignable = dispatch.status === 'OPEN' || dispatch.status === 'ASSIGNED';\n return (\n <div className=\"space-y-2 rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"min-w-0 break-words font-medium text-text-primary\">{dispatch.title}</p>\n <span className=\"shrink-0\">\n <DispatchPriorityBadge priority={dispatch.priority} />\n </span>\n </div>\n <p className=\"text-xs text-text-muted\">\n {dispatch.employeeName ?? 'Unassigned'}\n {dispatch.siteName ? ` · ${dispatch.siteName}` : ''}\n {dispatch.dueAt ? ` · due ${formatWhen(dispatch.dueAt)}` : ''}\n </p>\n <div className=\"flex flex-wrap gap-1.5\">\n {assignable ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={!canAssign}\n onClick={() => onAssign(dispatch)}\n >\n <UserCheck className=\"mr-1 h-3.5 w-3.5\" />\n {dispatch.status === 'OPEN' ? 'Assign' : 'Reassign'}\n </Button>\n ) : null}\n {MANAGER_NEXT_STATUS[dispatch.status].map((next) => (\n <Button\n key={next}\n size=\"sm\"\n variant={next === 'CANCELLED' ? 'ghost' : 'outline'}\n onClick={() => onTransition(dispatch, next)}\n >\n {STATUS_ACTION_LABEL[next]}\n </Button>\n ))}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={`Remove ${dispatch.title}`}\n onClick={() => onRemove(dispatch)}\n >\n <Trash2 className=\"h-3.5 w-3.5 text-status-error-fg\" />\n </Button>\n </div>\n </div>\n );\n}\n\n/** Work-order board by status, plus the most recent closed work orders (BOFF-4205). */\nexport function DispatchBoardCard({ dispatches, ...actions }: DispatchBoardCardProps) {\n const closed = dispatches.filter(isClosed);\n return (\n <Card data-testid=\"field-dispatch-board\">\n <CardHeader>\n <CardTitle className=\"text-base\">Work orders</CardTitle>\n <CardDescription>\n {dispatches.filter((dispatch) => dispatch.status === 'COMPLETED').length} completed ·{' '}\n {dispatches.length - closed.length} in flight\n </CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-4\">\n {BOARD_COLUMNS.map((column) => {\n const items = dispatches.filter((dispatch) => dispatch.status === column);\n return (\n <section key={column} aria-label={formatEnumLabel(column)} className=\"space-y-2\">\n <h2 className=\"text-sm font-medium text-text-secondary\">\n {formatEnumLabel(column)} ({items.length})\n </h2>\n {items.map((dispatch) => (\n <DispatchTile key={dispatch.id} dispatch={dispatch} {...actions} />\n ))}\n </section>\n );\n })}\n </div>\n {closed.length > 0 ? (\n <div className=\"mt-4 space-y-1\">\n <h2 className=\"text-sm font-medium text-text-secondary\">Closed</h2>\n <ul className=\"space-y-1\">\n {closed.slice(0, 10).map((dispatch) => (\n <li\n key={dispatch.id}\n className=\"flex flex-wrap items-center gap-2 text-sm text-text-secondary\"\n >\n <DispatchStatusBadge status={dispatch.status} />\n <span className=\"text-text-primary\">{dispatch.title}</span>\n <span>{dispatch.employeeName ?? ''}</span>\n <span>{formatWhen(dispatch.completedAt ?? dispatch.createdAt)}</span>\n {dispatch.completionNotes ? <span>· {dispatch.completionNotes}</span> : null}\n </li>\n ))}\n </ul>\n </div>\n ) : null}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
@@ -12,10 +12,10 @@ function c({ dispatch: c, focused: l, busy: u, onAdvance: d }) {
12
12
  /* @__PURE__ */ o("div", {
13
13
  className: "flex flex-wrap items-center justify-between gap-2",
14
14
  children: [/* @__PURE__ */ a("p", {
15
- className: "font-medium text-text-primary",
15
+ className: "min-w-0 break-words font-medium text-text-primary",
16
16
  children: c.title
17
17
  }), /* @__PURE__ */ o("div", {
18
- className: "flex gap-1.5",
18
+ className: "flex shrink-0 gap-1.5",
19
19
  children: [/* @__PURE__ */ a(e, { priority: c.priority }), /* @__PURE__ */ a(t, { status: c.status })]
20
20
  })]
21
21
  }),
@@ -1 +1 @@
1
- {"version":3,"file":"MyDispatchItem.js","names":[],"sources":["../../../../src/crewfoundry/components/field/MyDispatchItem.tsx"],"sourcesContent":["import { Button } from '@burdenoff/fe-libs/ui';\nimport {\n type CrewDispatch,\n type DispatchStatus,\n STATUS_ACTION_LABEL,\n WORKER_NEXT_STATUS,\n} from '../../lib/fieldOps';\nimport { DispatchPriorityBadge, DispatchStatusBadge } from './FieldBadges';\nimport { formatWhen } from './fieldFormat';\n\ninterface MyDispatchItemProps {\n dispatch: CrewDispatch;\n focused: boolean;\n busy: boolean;\n onAdvance: (dispatch: CrewDispatch, next: DispatchStatus) => void;\n}\n\n/** One of the worker's own work orders with the moves they may make (BOFF-4205). */\nexport function MyDispatchItem({ dispatch, focused, busy, onAdvance }: MyDispatchItemProps) {\n return (\n <li\n data-focused={focused ? 'true' : undefined}\n className={`space-y-2 rounded-[var(--radius-card)] border bg-bg-surface p-4 ${\n focused ? 'border-border-strong' : 'border-border-subtle'\n }`}\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <p className=\"font-medium text-text-primary\">{dispatch.title}</p>\n <div className=\"flex gap-1.5\">\n <DispatchPriorityBadge priority={dispatch.priority} />\n <DispatchStatusBadge status={dispatch.status} />\n </div>\n </div>\n {dispatch.description ? (\n <p className=\"text-sm text-text-secondary\">{dispatch.description}</p>\n ) : null}\n <p className=\"text-xs text-text-muted\">\n {[dispatch.siteName, dispatch.siteAddress].filter(Boolean).join(' · ') || 'No site given'}\n {dispatch.dueAt ? ` · due ${formatWhen(dispatch.dueAt)}` : ''}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {WORKER_NEXT_STATUS[dispatch.status].map((next) => (\n <Button\n key={next}\n size=\"sm\"\n variant={next === 'COMPLETED' ? 'default' : 'outline'}\n disabled={busy}\n onClick={() => onAdvance(dispatch, next)}\n >\n {STATUS_ACTION_LABEL[next]}\n </Button>\n ))}\n </div>\n </li>\n );\n}\n"],"mappings":";;;;;;AAkBA,SAAgB,EAAe,EAAE,aAAU,YAAS,SAAM,gBAAkC;AAC1F,QACE,kBAAC,MAAD;EACE,gBAAc,IAAU,SAAS,KAAA;EACjC,WAAW,mEACT,IAAU,yBAAyB;YAHvC;GAME,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAiC,EAAS;KAAU,CAAA,EACjE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAuB,UAAU,EAAS,UAAY,CAAA,EACtD,kBAAC,GAAD,EAAqB,QAAQ,EAAS,QAAU,CAAA,CAC5C;OACF;;GACL,EAAS,cACR,kBAAC,KAAD;IAAG,WAAU;cAA+B,EAAS;IAAgB,CAAA,GACnE;GACJ,kBAAC,KAAD;IAAG,WAAU;cAAb,CACG,CAAC,EAAS,UAAU,EAAS,YAAY,CAAC,OAAO,QAAQ,CAAC,KAAK,MAAM,IAAI,iBACzE,EAAS,QAAQ,UAAU,EAAW,EAAS,MAAM,KAAK,GACzD;;GACJ,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAmB,EAAS,QAAQ,KAAK,MACxC,kBAAC,GAAD;KAEE,MAAK;KACL,SAAS,MAAS,cAAc,YAAY;KAC5C,UAAU;KACV,eAAe,EAAU,GAAU,EAAK;eAEvC,EAAoB;KACd,EAPF,EAOE,CACT;IACE,CAAA;GACH"}
1
+ {"version":3,"file":"MyDispatchItem.js","names":[],"sources":["../../../../src/crewfoundry/components/field/MyDispatchItem.tsx"],"sourcesContent":["import { Button } from '@burdenoff/fe-libs/ui';\nimport {\n type CrewDispatch,\n type DispatchStatus,\n STATUS_ACTION_LABEL,\n WORKER_NEXT_STATUS,\n} from '../../lib/fieldOps';\nimport { DispatchPriorityBadge, DispatchStatusBadge } from './FieldBadges';\nimport { formatWhen } from './fieldFormat';\n\ninterface MyDispatchItemProps {\n dispatch: CrewDispatch;\n focused: boolean;\n busy: boolean;\n onAdvance: (dispatch: CrewDispatch, next: DispatchStatus) => void;\n}\n\n/** One of the worker's own work orders with the moves they may make (BOFF-4205). */\nexport function MyDispatchItem({ dispatch, focused, busy, onAdvance }: MyDispatchItemProps) {\n return (\n <li\n data-focused={focused ? 'true' : undefined}\n className={`space-y-2 rounded-[var(--radius-card)] border bg-bg-surface p-4 ${\n focused ? 'border-border-strong' : 'border-border-subtle'\n }`}\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <p className=\"min-w-0 break-words font-medium text-text-primary\">{dispatch.title}</p>\n <div className=\"flex shrink-0 gap-1.5\">\n <DispatchPriorityBadge priority={dispatch.priority} />\n <DispatchStatusBadge status={dispatch.status} />\n </div>\n </div>\n {dispatch.description ? (\n <p className=\"text-sm text-text-secondary\">{dispatch.description}</p>\n ) : null}\n <p className=\"text-xs text-text-muted\">\n {[dispatch.siteName, dispatch.siteAddress].filter(Boolean).join(' · ') || 'No site given'}\n {dispatch.dueAt ? ` · due ${formatWhen(dispatch.dueAt)}` : ''}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {WORKER_NEXT_STATUS[dispatch.status].map((next) => (\n <Button\n key={next}\n size=\"sm\"\n variant={next === 'COMPLETED' ? 'default' : 'outline'}\n disabled={busy}\n onClick={() => onAdvance(dispatch, next)}\n >\n {STATUS_ACTION_LABEL[next]}\n </Button>\n ))}\n </div>\n </li>\n );\n}\n"],"mappings":";;;;;;AAkBA,SAAgB,EAAe,EAAE,aAAU,YAAS,SAAM,gBAAkC;AAC1F,QACE,kBAAC,MAAD;EACE,gBAAc,IAAU,SAAS,KAAA;EACjC,WAAW,mEACT,IAAU,yBAAyB;YAHvC;GAME,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAqD,EAAS;KAAU,CAAA,EACrF,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAuB,UAAU,EAAS,UAAY,CAAA,EACtD,kBAAC,GAAD,EAAqB,QAAQ,EAAS,QAAU,CAAA,CAC5C;OACF;;GACL,EAAS,cACR,kBAAC,KAAD;IAAG,WAAU;cAA+B,EAAS;IAAgB,CAAA,GACnE;GACJ,kBAAC,KAAD;IAAG,WAAU;cAAb,CACG,CAAC,EAAS,UAAU,EAAS,YAAY,CAAC,OAAO,QAAQ,CAAC,KAAK,MAAM,IAAI,iBACzE,EAAS,QAAQ,UAAU,EAAW,EAAS,MAAM,KAAK,GACzD;;GACJ,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAmB,EAAS,QAAQ,KAAK,MACxC,kBAAC,GAAD;KAEE,MAAK;KACL,SAAS,MAAS,cAAc,YAAY;KAC5C,UAAU;KACV,eAAe,EAAU,GAAU,EAAK;eAEvC,EAAoB;KACd,EAPF,EAOE,CACT;IACE,CAAA;GACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"MyShiftItem.d.ts","sourceRoot":"","sources":["../../../../src/crewfoundry/components/field/MyShiftItem.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAuC,MAAM,oBAAoB,CAAC;AAIzF,UAAU,gBAAgB;IACxB,KAAK,EAAE,SAAS,CAAC;IACjB,GAAG,EAAE,IAAI,CAAC;IACV,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACtC,UAAU,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACxC;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,GAAG,EACH,OAAO,EACP,IAAI,EACJ,SAAS,EACT,UAAU,GACX,EAAE,gBAAgB,+BA0ElB"}
1
+ {"version":3,"file":"MyShiftItem.d.ts","sourceRoot":"","sources":["../../../../src/crewfoundry/components/field/MyShiftItem.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAuC,MAAM,oBAAoB,CAAC;AAIzF,UAAU,gBAAgB;IACxB,KAAK,EAAE,SAAS,CAAC;IACjB,GAAG,EAAE,IAAI,CAAC;IACV,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACtC,UAAU,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACxC;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,GAAG,EACH,OAAO,EACP,IAAI,EACJ,SAAS,EACT,UAAU,GACX,EAAE,gBAAgB,+BA4ElB"}
@@ -14,9 +14,12 @@ function h({ shift: h, now: g, focused: _, busy: v, onCheckIn: y, onCheckOut: b
14
14
  /* @__PURE__ */ p("div", {
15
15
  className: "flex flex-wrap items-center justify-between gap-2",
16
16
  children: [/* @__PURE__ */ f("p", {
17
- className: "font-medium text-text-primary",
17
+ className: "min-w-0 break-words font-medium text-text-primary",
18
18
  children: h.title
19
- }), /* @__PURE__ */ f(e, { status: h.status })]
19
+ }), /* @__PURE__ */ f("span", {
20
+ className: "shrink-0",
21
+ children: /* @__PURE__ */ f(e, { status: h.status })
22
+ })]
20
23
  }),
21
24
  /* @__PURE__ */ p("div", {
22
25
  className: "space-y-1 text-sm text-text-secondary",
@@ -1 +1 @@
1
- {"version":3,"file":"MyShiftItem.js","names":[],"sources":["../../../../src/crewfoundry/components/field/MyShiftItem.tsx"],"sourcesContent":["import { Button } from '@burdenoff/fe-libs/ui';\nimport { Clock, LocateFixed, LogIn, LogOut, MapPin, Navigation } from 'lucide-react';\nimport { type CrewShift, checkInAvailability, formatDistance } from '../../lib/fieldOps';\nimport { ShiftStatusBadge } from './FieldBadges';\nimport { formatTime, formatWhen, mapsLink } from './fieldFormat';\n\ninterface MyShiftItemProps {\n shift: CrewShift;\n now: Date;\n focused: boolean;\n busy: boolean;\n onCheckIn: (shift: CrewShift) => void;\n onCheckOut: (shift: CrewShift) => void;\n}\n\n/** One of the worker's own shifts with its check-in / check-out action (BOFF-4205). */\nexport function MyShiftItem({\n shift,\n now,\n focused,\n busy,\n onCheckIn,\n onCheckOut,\n}: MyShiftItemProps) {\n const availability = checkInAvailability(shift, now);\n const link = mapsLink(shift.siteLatitude, shift.siteLongitude, shift.siteAddress);\n return (\n <li\n data-focused={focused ? 'true' : undefined}\n className={`space-y-3 rounded-[var(--radius-card)] border bg-bg-surface p-4 ${\n focused ? 'border-border-strong' : 'border-border-subtle'\n }`}\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <p className=\"font-medium text-text-primary\">{shift.title}</p>\n <ShiftStatusBadge status={shift.status} />\n </div>\n <div className=\"space-y-1 text-sm text-text-secondary\">\n <p className=\"flex items-center gap-1.5\">\n <Clock className=\"h-4 w-4\" />\n {formatWhen(shift.startsAt)} – {formatTime(shift.endsAt)}\n </p>\n {shift.siteName || shift.siteAddress ? (\n <p className=\"flex flex-wrap items-center gap-1.5\">\n <MapPin className=\"h-4 w-4\" />\n {[shift.siteName, shift.siteAddress].filter(Boolean).join(' · ')}\n {link ? (\n <a\n href={link}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-0.5 text-text-link underline\"\n >\n <Navigation className=\"h-3.5 w-3.5\" />\n Directions\n </a>\n ) : null}\n </p>\n ) : null}\n {shift.checkedInAt ? (\n <p className=\"text-xs text-text-muted\">\n Checked in at {formatTime(shift.checkedInAt)}\n {shift.checkInDistanceMeters !== null\n ? ` · ${formatDistance(shift.checkInDistanceMeters)} from the site`\n : ''}\n </p>\n ) : null}\n {shift.notes ? <p className=\"text-xs text-text-muted\">{shift.notes}</p> : null}\n </div>\n {availability.kind === 'check-in' ? (\n <Button className=\"w-full sm:w-auto\" disabled={busy} onClick={() => onCheckIn(shift)}>\n {busy ? (\n <LocateFixed className=\"mr-1 h-4 w-4 animate-pulse\" />\n ) : (\n <LogIn className=\"mr-1 h-4 w-4\" />\n )}\n Check in here\n </Button>\n ) : null}\n {availability.kind === 'check-out' ? (\n <Button\n className=\"w-full sm:w-auto\"\n variant=\"outline\"\n disabled={busy}\n onClick={() => onCheckOut(shift)}\n >\n <LogOut className=\"mr-1 h-4 w-4\" />\n Check out\n </Button>\n ) : null}\n {availability.kind === 'too-early' ? (\n <p className=\"text-xs text-text-muted\">\n Check-in opens at {formatTime(availability.opensAt)}\n </p>\n ) : null}\n </li>\n );\n}\n"],"mappings":";;;;;;;AAgBA,SAAgB,EAAY,EAC1B,UACA,QACA,YACA,SACA,cACA,iBACmB;CACnB,IAAM,IAAe,EAAoB,GAAO,EAAI,EAC9C,IAAO,EAAS,EAAM,cAAc,EAAM,eAAe,EAAM,YAAY;AACjF,QACE,kBAAC,MAAD;EACE,gBAAc,IAAU,SAAS,KAAA;EACjC,WAAW,mEACT,IAAU,yBAAyB;YAHvC;GAME,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAiC,EAAM;KAAU,CAAA,EAC9D,kBAAC,GAAD,EAAkB,QAAQ,EAAM,QAAU,CAAA,CACtC;;GACN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OAC5B,EAAW,EAAM,SAAS;OAAC;OAAI,EAAW,EAAM,OAAO;OACtD;;KACH,EAAM,YAAY,EAAM,cACvB,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;OAC7B,CAAC,EAAM,UAAU,EAAM,YAAY,CAAC,OAAO,QAAQ,CAAC,KAAK,MAAM;OAC/D,IACC,kBAAC,KAAD;QACE,MAAM;QACN,QAAO;QACP,KAAI;QACJ,WAAU;kBAJZ,CAME,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA,EAAA,aAEpC;YACF;OACF;UACF;KACH,EAAM,cACL,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAAuC;OACtB,EAAW,EAAM,YAAY;OAC3C,EAAM,0BAA0B,OAE7B,KADA,MAAM,EAAe,EAAM,sBAAsB,CAAC;OAEpD;UACF;KACH,EAAM,QAAQ,kBAAC,KAAD;MAAG,WAAU;gBAA2B,EAAM;MAAU,CAAA,GAAG;KACtE;;GACL,EAAa,SAAS,aACrB,kBAAC,GAAD;IAAQ,WAAU;IAAmB,UAAU;IAAM,eAAe,EAAU,EAAM;cAApF,CACG,IACC,kBAAC,GAAD,EAAa,WAAU,8BAA+B,CAAA,GAEtD,kBAAC,GAAD,EAAO,WAAU,gBAAiB,CAAA,EAClC,gBAEK;QACP;GACH,EAAa,SAAS,cACrB,kBAAC,GAAD;IACE,WAAU;IACV,SAAQ;IACR,UAAU;IACV,eAAe,EAAW,EAAM;cAJlC,CAME,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,YAE5B;QACP;GACH,EAAa,SAAS,cACrB,kBAAC,KAAD;IAAG,WAAU;cAAb,CAAuC,sBAClB,EAAW,EAAa,QAAQ,CACjD;QACF;GACD"}
1
+ {"version":3,"file":"MyShiftItem.js","names":[],"sources":["../../../../src/crewfoundry/components/field/MyShiftItem.tsx"],"sourcesContent":["import { Button } from '@burdenoff/fe-libs/ui';\nimport { Clock, LocateFixed, LogIn, LogOut, MapPin, Navigation } from 'lucide-react';\nimport { type CrewShift, checkInAvailability, formatDistance } from '../../lib/fieldOps';\nimport { ShiftStatusBadge } from './FieldBadges';\nimport { formatTime, formatWhen, mapsLink } from './fieldFormat';\n\ninterface MyShiftItemProps {\n shift: CrewShift;\n now: Date;\n focused: boolean;\n busy: boolean;\n onCheckIn: (shift: CrewShift) => void;\n onCheckOut: (shift: CrewShift) => void;\n}\n\n/** One of the worker's own shifts with its check-in / check-out action (BOFF-4205). */\nexport function MyShiftItem({\n shift,\n now,\n focused,\n busy,\n onCheckIn,\n onCheckOut,\n}: MyShiftItemProps) {\n const availability = checkInAvailability(shift, now);\n const link = mapsLink(shift.siteLatitude, shift.siteLongitude, shift.siteAddress);\n return (\n <li\n data-focused={focused ? 'true' : undefined}\n className={`space-y-3 rounded-[var(--radius-card)] border bg-bg-surface p-4 ${\n focused ? 'border-border-strong' : 'border-border-subtle'\n }`}\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <p className=\"min-w-0 break-words font-medium text-text-primary\">{shift.title}</p>\n <span className=\"shrink-0\">\n <ShiftStatusBadge status={shift.status} />\n </span>\n </div>\n <div className=\"space-y-1 text-sm text-text-secondary\">\n <p className=\"flex items-center gap-1.5\">\n <Clock className=\"h-4 w-4\" />\n {formatWhen(shift.startsAt)} – {formatTime(shift.endsAt)}\n </p>\n {shift.siteName || shift.siteAddress ? (\n <p className=\"flex flex-wrap items-center gap-1.5\">\n <MapPin className=\"h-4 w-4\" />\n {[shift.siteName, shift.siteAddress].filter(Boolean).join(' · ')}\n {link ? (\n <a\n href={link}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-0.5 text-text-link underline\"\n >\n <Navigation className=\"h-3.5 w-3.5\" />\n Directions\n </a>\n ) : null}\n </p>\n ) : null}\n {shift.checkedInAt ? (\n <p className=\"text-xs text-text-muted\">\n Checked in at {formatTime(shift.checkedInAt)}\n {shift.checkInDistanceMeters !== null\n ? ` · ${formatDistance(shift.checkInDistanceMeters)} from the site`\n : ''}\n </p>\n ) : null}\n {shift.notes ? <p className=\"text-xs text-text-muted\">{shift.notes}</p> : null}\n </div>\n {availability.kind === 'check-in' ? (\n <Button className=\"w-full sm:w-auto\" disabled={busy} onClick={() => onCheckIn(shift)}>\n {busy ? (\n <LocateFixed className=\"mr-1 h-4 w-4 animate-pulse\" />\n ) : (\n <LogIn className=\"mr-1 h-4 w-4\" />\n )}\n Check in here\n </Button>\n ) : null}\n {availability.kind === 'check-out' ? (\n <Button\n className=\"w-full sm:w-auto\"\n variant=\"outline\"\n disabled={busy}\n onClick={() => onCheckOut(shift)}\n >\n <LogOut className=\"mr-1 h-4 w-4\" />\n Check out\n </Button>\n ) : null}\n {availability.kind === 'too-early' ? (\n <p className=\"text-xs text-text-muted\">\n Check-in opens at {formatTime(availability.opensAt)}\n </p>\n ) : null}\n </li>\n );\n}\n"],"mappings":";;;;;;;AAgBA,SAAgB,EAAY,EAC1B,UACA,QACA,YACA,SACA,cACA,iBACmB;CACnB,IAAM,IAAe,EAAoB,GAAO,EAAI,EAC9C,IAAO,EAAS,EAAM,cAAc,EAAM,eAAe,EAAM,YAAY;AACjF,QACE,kBAAC,MAAD;EACE,gBAAc,IAAU,SAAS,KAAA;EACjC,WAAW,mEACT,IAAU,yBAAyB;YAHvC;GAME,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAqD,EAAM;KAAU,CAAA,EAClF,kBAAC,QAAD;KAAM,WAAU;eACd,kBAAC,GAAD,EAAkB,QAAQ,EAAM,QAAU,CAAA;KACrC,CAAA,CACH;;GACN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OAC5B,EAAW,EAAM,SAAS;OAAC;OAAI,EAAW,EAAM,OAAO;OACtD;;KACH,EAAM,YAAY,EAAM,cACvB,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;OAC7B,CAAC,EAAM,UAAU,EAAM,YAAY,CAAC,OAAO,QAAQ,CAAC,KAAK,MAAM;OAC/D,IACC,kBAAC,KAAD;QACE,MAAM;QACN,QAAO;QACP,KAAI;QACJ,WAAU;kBAJZ,CAME,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA,EAAA,aAEpC;YACF;OACF;UACF;KACH,EAAM,cACL,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAAuC;OACtB,EAAW,EAAM,YAAY;OAC3C,EAAM,0BAA0B,OAE7B,KADA,MAAM,EAAe,EAAM,sBAAsB,CAAC;OAEpD;UACF;KACH,EAAM,QAAQ,kBAAC,KAAD;MAAG,WAAU;gBAA2B,EAAM;MAAU,CAAA,GAAG;KACtE;;GACL,EAAa,SAAS,aACrB,kBAAC,GAAD;IAAQ,WAAU;IAAmB,UAAU;IAAM,eAAe,EAAU,EAAM;cAApF,CACG,IACC,kBAAC,GAAD,EAAa,WAAU,8BAA+B,CAAA,GAEtD,kBAAC,GAAD,EAAO,WAAU,gBAAiB,CAAA,EAClC,gBAEK;QACP;GACH,EAAa,SAAS,cACrB,kBAAC,GAAD;IACE,WAAU;IACV,SAAQ;IACR,UAAU;IACV,eAAe,EAAW,EAAM;cAJlC,CAME,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,YAE5B;QACP;GACH,EAAa,SAAS,cACrB,kBAAC,KAAD;IAAG,WAAU;cAAb,CAAuC,sBAClB,EAAW,EAAa,QAAQ,CACjD;QACF;GACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"ShiftsCard.d.ts","sourceRoot":"","sources":["../../../../src/crewfoundry/components/field/ShiftsCard.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,SAAS,EAAkB,MAAM,oBAAoB,CAAC;AAIpE,UAAU,eAAe;IACvB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACtC;AAiBD,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,eAAe,+BA+DnF"}
1
+ {"version":3,"file":"ShiftsCard.d.ts","sourceRoot":"","sources":["../../../../src/crewfoundry/components/field/ShiftsCard.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,SAAS,EAAkB,MAAM,oBAAoB,CAAC;AAIpE,UAAU,eAAe;IACvB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACtC;AAiBD,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,eAAe,+BAmEnF"}
@@ -26,9 +26,12 @@ function _({ shifts: t, onRemind: _, onCancel: v, onRemove: y }) {
26
26
  /* @__PURE__ */ c("div", {
27
27
  className: "flex flex-wrap items-center gap-2",
28
28
  children: [/* @__PURE__ */ s("p", {
29
- className: "font-medium text-text-primary",
29
+ className: "min-w-0 break-words font-medium text-text-primary",
30
30
  children: t.title
31
- }), /* @__PURE__ */ s(e, { status: t.status })]
31
+ }), /* @__PURE__ */ s("span", {
32
+ className: "shrink-0",
33
+ children: /* @__PURE__ */ s(e, { status: t.status })
34
+ })]
32
35
  }),
33
36
  /* @__PURE__ */ c("p", {
34
37
  className: "text-sm text-text-secondary",
@@ -1 +1 @@
1
- {"version":3,"file":"ShiftsCard.js","names":[],"sources":["../../../../src/crewfoundry/components/field/ShiftsCard.tsx"],"sourcesContent":["import {\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { BellRing, Trash2, XCircle } from 'lucide-react';\nimport { type CrewShift, formatDistance } from '../../lib/fieldOps';\nimport { ShiftStatusBadge } from './FieldBadges';\nimport { formatTime, formatWhen } from './fieldFormat';\n\ninterface ShiftsCardProps {\n shifts: CrewShift[];\n onRemind: (shift: CrewShift) => void;\n onCancel: (shift: CrewShift) => void;\n onRemove: (shift: CrewShift) => void;\n}\n\nfunction shiftDetail(shift: CrewShift): string {\n const parts = [shift.siteName ?? 'No site'];\n if (shift.siteLatitude !== null) parts.push(`${shift.geofenceRadiusMeters} m check-in radius`);\n if (shift.checkedInAt) {\n parts.push(\n `checked in ${formatTime(shift.checkedInAt)} (${formatDistance(shift.checkInDistanceMeters)} from site)`\n );\n }\n if (shift.checkedOutAt) parts.push(`out ${formatTime(shift.checkedOutAt)}`);\n if (shift.timeEntry && shift.status === 'COMPLETED')\n parts.push(`${shift.timeEntry.totalHours} h`);\n if (!shift.assigneeUserId) parts.push('no worker login linked');\n return parts.join(' · ');\n}\n\n/** Dispatcher list of shifts with reminder / cancel / remove actions (BOFF-4205). */\nexport function ShiftsCard({ shifts, onRemind, onCancel, onRemove }: ShiftsCardProps) {\n return (\n <Card data-testid=\"field-shifts\">\n <CardHeader>\n <CardTitle className=\"text-base\">Shifts</CardTitle>\n <CardDescription>The past week through the next four weeks.</CardDescription>\n </CardHeader>\n <CardContent>\n {shifts.length > 0 ? (\n <ul className=\"space-y-2\" aria-label=\"Shifts\">\n {shifts.map((shift) => (\n <li\n key={shift.id}\n className=\"flex flex-col gap-3 rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-3 md:flex-row md:items-center md:justify-between\"\n >\n <div className=\"min-w-0 space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <p className=\"font-medium text-text-primary\">{shift.title}</p>\n <ShiftStatusBadge status={shift.status} />\n </div>\n <p className=\"text-sm text-text-secondary\">\n {shift.employeeName} · {formatWhen(shift.startsAt)} – {formatTime(shift.endsAt)}\n </p>\n <p className=\"text-xs text-text-muted\">{shiftDetail(shift)}</p>\n </div>\n {shift.status === 'SCHEDULED' ? (\n <div className=\"flex flex-wrap gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={!shift.assigneeUserId}\n onClick={() => onRemind(shift)}\n >\n <BellRing className=\"mr-1 h-3.5 w-3.5\" />\n Remind\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => onCancel(shift)}>\n <XCircle className=\"mr-1 h-3.5 w-3.5\" />\n Cancel\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={`Remove ${shift.title}`}\n onClick={() => onRemove(shift)}\n >\n <Trash2 className=\"h-3.5 w-3.5 text-status-error-fg\" />\n </Button>\n </div>\n ) : null}\n </li>\n ))}\n </ul>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No shifts scheduled\"\n description=\"Schedule a shift to give a worker a site, a time window and a check-in geofence.\"\n />\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":";;;;;;;AAqBA,SAAS,EAAY,GAA0B;CAC7C,IAAM,IAAQ,CAAC,EAAM,YAAY,UAAU;AAW3C,QAVI,EAAM,iBAAiB,QAAM,EAAM,KAAK,GAAG,EAAM,qBAAqB,oBAAoB,EAC1F,EAAM,eACR,EAAM,KACJ,cAAc,EAAW,EAAM,YAAY,CAAC,IAAI,EAAe,EAAM,sBAAsB,CAAC,aAC7F,EAEC,EAAM,gBAAc,EAAM,KAAK,OAAO,EAAW,EAAM,aAAa,GAAG,EACvE,EAAM,aAAa,EAAM,WAAW,eACtC,EAAM,KAAK,GAAG,EAAM,UAAU,WAAW,IAAI,EAC1C,EAAM,kBAAgB,EAAM,KAAK,yBAAyB,EACxD,EAAM,KAAK,MAAM;;AAI1B,SAAgB,EAAW,EAAE,WAAQ,aAAU,aAAU,eAA6B;AACpF,QACE,kBAAC,GAAD;EAAM,eAAY;YAAlB,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GAAW,WAAU;aAAY;GAAkB,CAAA,EACnD,kBAAC,GAAD,EAAA,UAAiB,8CAA4D,CAAA,CAClE,EAAA,CAAA,EACb,kBAAC,GAAD,EAAA,UACG,EAAO,SAAS,IACf,kBAAC,MAAD;GAAI,WAAU;GAAY,cAAW;aAClC,EAAO,KAAK,MACX,kBAAC,MAAD;IAEE,WAAU;cAFZ,CAIE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAiC,EAAM;QAAU,CAAA,EAC9D,kBAAC,GAAD,EAAkB,QAAQ,EAAM,QAAU,CAAA,CACtC;;MACN,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAM;QAAa;QAAI,EAAW,EAAM,SAAS;QAAC;QAAI,EAAW,EAAM,OAAO;QAC7E;;MACJ,kBAAC,KAAD;OAAG,WAAU;iBAA2B,EAAY,EAAM;OAAK,CAAA;MAC3D;QACL,EAAM,WAAW,cAChB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,MAAK;OACL,SAAQ;OACR,UAAU,CAAC,EAAM;OACjB,eAAe,EAAS,EAAM;iBAJhC,CAME,kBAAC,GAAD,EAAU,WAAU,oBAAqB,CAAA,EAAA,SAElC;;MACT,kBAAC,GAAD;OAAQ,MAAK;OAAK,SAAQ;OAAU,eAAe,EAAS,EAAM;iBAAlE,CACE,kBAAC,GAAD,EAAS,WAAU,oBAAqB,CAAA,EAAA,SAEjC;;MACT,kBAAC,GAAD;OACE,MAAK;OACL,SAAQ;OACR,cAAY,UAAU,EAAM;OAC5B,eAAe,EAAS,EAAM;iBAE9B,kBAAC,GAAD,EAAQ,WAAU,oCAAqC,CAAA;OAChD,CAAA;MACL;SACJ,KACD;MAtCE,EAAM,GAsCR,CACL;GACC,CAAA,GAEL,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA,EAEQ,CAAA,CACT"}
1
+ {"version":3,"file":"ShiftsCard.js","names":[],"sources":["../../../../src/crewfoundry/components/field/ShiftsCard.tsx"],"sourcesContent":["import {\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { BellRing, Trash2, XCircle } from 'lucide-react';\nimport { type CrewShift, formatDistance } from '../../lib/fieldOps';\nimport { ShiftStatusBadge } from './FieldBadges';\nimport { formatTime, formatWhen } from './fieldFormat';\n\ninterface ShiftsCardProps {\n shifts: CrewShift[];\n onRemind: (shift: CrewShift) => void;\n onCancel: (shift: CrewShift) => void;\n onRemove: (shift: CrewShift) => void;\n}\n\nfunction shiftDetail(shift: CrewShift): string {\n const parts = [shift.siteName ?? 'No site'];\n if (shift.siteLatitude !== null) parts.push(`${shift.geofenceRadiusMeters} m check-in radius`);\n if (shift.checkedInAt) {\n parts.push(\n `checked in ${formatTime(shift.checkedInAt)} (${formatDistance(shift.checkInDistanceMeters)} from site)`\n );\n }\n if (shift.checkedOutAt) parts.push(`out ${formatTime(shift.checkedOutAt)}`);\n if (shift.timeEntry && shift.status === 'COMPLETED')\n parts.push(`${shift.timeEntry.totalHours} h`);\n if (!shift.assigneeUserId) parts.push('no worker login linked');\n return parts.join(' · ');\n}\n\n/** Dispatcher list of shifts with reminder / cancel / remove actions (BOFF-4205). */\nexport function ShiftsCard({ shifts, onRemind, onCancel, onRemove }: ShiftsCardProps) {\n return (\n <Card data-testid=\"field-shifts\">\n <CardHeader>\n <CardTitle className=\"text-base\">Shifts</CardTitle>\n <CardDescription>The past week through the next four weeks.</CardDescription>\n </CardHeader>\n <CardContent>\n {shifts.length > 0 ? (\n <ul className=\"space-y-2\" aria-label=\"Shifts\">\n {shifts.map((shift) => (\n <li\n key={shift.id}\n className=\"flex flex-col gap-3 rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-3 md:flex-row md:items-center md:justify-between\"\n >\n <div className=\"min-w-0 space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <p className=\"min-w-0 break-words font-medium text-text-primary\">\n {shift.title}\n </p>\n <span className=\"shrink-0\">\n <ShiftStatusBadge status={shift.status} />\n </span>\n </div>\n <p className=\"text-sm text-text-secondary\">\n {shift.employeeName} · {formatWhen(shift.startsAt)} – {formatTime(shift.endsAt)}\n </p>\n <p className=\"text-xs text-text-muted\">{shiftDetail(shift)}</p>\n </div>\n {shift.status === 'SCHEDULED' ? (\n <div className=\"flex flex-wrap gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={!shift.assigneeUserId}\n onClick={() => onRemind(shift)}\n >\n <BellRing className=\"mr-1 h-3.5 w-3.5\" />\n Remind\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => onCancel(shift)}>\n <XCircle className=\"mr-1 h-3.5 w-3.5\" />\n Cancel\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={`Remove ${shift.title}`}\n onClick={() => onRemove(shift)}\n >\n <Trash2 className=\"h-3.5 w-3.5 text-status-error-fg\" />\n </Button>\n </div>\n ) : null}\n </li>\n ))}\n </ul>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No shifts scheduled\"\n description=\"Schedule a shift to give a worker a site, a time window and a check-in geofence.\"\n />\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":";;;;;;;AAqBA,SAAS,EAAY,GAA0B;CAC7C,IAAM,IAAQ,CAAC,EAAM,YAAY,UAAU;AAW3C,QAVI,EAAM,iBAAiB,QAAM,EAAM,KAAK,GAAG,EAAM,qBAAqB,oBAAoB,EAC1F,EAAM,eACR,EAAM,KACJ,cAAc,EAAW,EAAM,YAAY,CAAC,IAAI,EAAe,EAAM,sBAAsB,CAAC,aAC7F,EAEC,EAAM,gBAAc,EAAM,KAAK,OAAO,EAAW,EAAM,aAAa,GAAG,EACvE,EAAM,aAAa,EAAM,WAAW,eACtC,EAAM,KAAK,GAAG,EAAM,UAAU,WAAW,IAAI,EAC1C,EAAM,kBAAgB,EAAM,KAAK,yBAAyB,EACxD,EAAM,KAAK,MAAM;;AAI1B,SAAgB,EAAW,EAAE,WAAQ,aAAU,aAAU,eAA6B;AACpF,QACE,kBAAC,GAAD;EAAM,eAAY;YAAlB,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GAAW,WAAU;aAAY;GAAkB,CAAA,EACnD,kBAAC,GAAD,EAAA,UAAiB,8CAA4D,CAAA,CAClE,EAAA,CAAA,EACb,kBAAC,GAAD,EAAA,UACG,EAAO,SAAS,IACf,kBAAC,MAAD;GAAI,WAAU;GAAY,cAAW;aAClC,EAAO,KAAK,MACX,kBAAC,MAAD;IAEE,WAAU;cAFZ,CAIE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM;QACL,CAAA,EACJ,kBAAC,QAAD;QAAM,WAAU;kBACd,kBAAC,GAAD,EAAkB,QAAQ,EAAM,QAAU,CAAA;QACrC,CAAA,CACH;;MACN,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAM;QAAa;QAAI,EAAW,EAAM,SAAS;QAAC;QAAI,EAAW,EAAM,OAAO;QAC7E;;MACJ,kBAAC,KAAD;OAAG,WAAU;iBAA2B,EAAY,EAAM;OAAK,CAAA;MAC3D;QACL,EAAM,WAAW,cAChB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,MAAK;OACL,SAAQ;OACR,UAAU,CAAC,EAAM;OACjB,eAAe,EAAS,EAAM;iBAJhC,CAME,kBAAC,GAAD,EAAU,WAAU,oBAAqB,CAAA,EAAA,SAElC;;MACT,kBAAC,GAAD;OAAQ,MAAK;OAAK,SAAQ;OAAU,eAAe,EAAS,EAAM;iBAAlE,CACE,kBAAC,GAAD,EAAS,WAAU,oBAAqB,CAAA,EAAA,SAEjC;;MACT,kBAAC,GAAD;OACE,MAAK;OACL,SAAQ;OACR,cAAY,UAAU,EAAM;OAC5B,eAAe,EAAS,EAAM;iBAE9B,kBAAC,GAAD,EAAQ,WAAU,oCAAqC,CAAA;OAChD,CAAA;MACL;SACJ,KACD;MA1CE,EAAM,GA0CR,CACL;GACC,CAAA,GAEL,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA,EAEQ,CAAA,CACT"}
@@ -4,7 +4,9 @@
4
4
  * `crewExecutiveDashboard` rollup, so every figure is computed from the
5
5
  * workspace's real rows by one deterministic implementation rather than
6
6
  * re-derived in the browser. Project budget and department budget figures
7
- * still come from the bootstrap store, which already owns those rows.
7
+ * still come from the bootstrap store, which already owns those rows. The
8
+ * workforce forecast card (BOFF-10826) adds the trained attrition model and
9
+ * the 3/6/12-month headcount and cost projection.
8
10
  */
9
11
  export declare function DashboardExecutiveDashboardPage(): import("react").JSX.Element;
10
12
  //# sourceMappingURL=DashboardExecutiveDashboardPage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardExecutiveDashboardPage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/DashboardExecutiveDashboardPage.tsx"],"names":[],"mappings":"AAudA;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,gCAiG9C"}
1
+ {"version":3,"file":"DashboardExecutiveDashboardPage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/DashboardExecutiveDashboardPage.tsx"],"names":[],"mappings":"AAwdA;;;;;;;;;GASG;AACH,wBAAgB,+BAA+B,gCAqH9C"}