@burdenoff/microfe-planmagnet 2026.930.22 → 2026.930.24

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 (68) hide show
  1. package/dist/index.js +6 -5
  2. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  3. package/dist/planmagnet/PlanMagnetRoot.js +154 -147
  4. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  6. package/dist/planmagnet/PlanMagnetRoutes.js +66 -62
  7. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  8. package/dist/planmagnet/controls/FeatureControls.d.ts +11 -0
  9. package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
  10. package/dist/planmagnet/controls/FeatureControls.js +305 -135
  11. package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
  12. package/dist/planmagnet/controls/Pickers.d.ts +18 -0
  13. package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
  14. package/dist/planmagnet/controls/Pickers.js +78 -1
  15. package/dist/planmagnet/controls/Pickers.js.map +1 -1
  16. package/dist/planmagnet/controls/PrioritizationControls.d.ts +72 -0
  17. package/dist/planmagnet/controls/PrioritizationControls.d.ts.map +1 -0
  18. package/dist/planmagnet/controls/PrioritizationControls.js +246 -0
  19. package/dist/planmagnet/controls/PrioritizationControls.js.map +1 -0
  20. package/dist/planmagnet/dependency/DependencyGraph.d.ts.map +1 -1
  21. package/dist/planmagnet/dependency/DependencyGraph.js +5 -2
  22. package/dist/planmagnet/dependency/DependencyGraph.js.map +1 -1
  23. package/dist/planmagnet/dependency/GanttChart.d.ts.map +1 -1
  24. package/dist/planmagnet/dependency/GanttChart.js +11 -5
  25. package/dist/planmagnet/dependency/GanttChart.js.map +1 -1
  26. package/dist/planmagnet/index.d.ts +1 -0
  27. package/dist/planmagnet/index.d.ts.map +1 -1
  28. package/dist/planmagnet/index.js +1 -0
  29. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  30. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +131 -125
  31. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  32. package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
  33. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +183 -99
  34. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
  35. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
  36. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +113 -97
  37. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  38. package/dist/planmagnet/pages/ProductsPrioritizationPagePage.d.ts +2 -0
  39. package/dist/planmagnet/pages/ProductsPrioritizationPagePage.d.ts.map +1 -0
  40. package/dist/planmagnet/pages/ProductsPrioritizationPagePage.js +363 -0
  41. package/dist/planmagnet/pages/ProductsPrioritizationPagePage.js.map +1 -0
  42. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  43. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +96 -79
  44. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  45. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
  46. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +133 -123
  47. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
  48. package/dist/planmagnet/pages/analytics/StatusFlowCard.js +5 -2
  49. package/dist/planmagnet/pages/analytics/StatusFlowCard.js.map +1 -1
  50. package/dist/planmagnet/pages/analytics/WorkBreakdownCard.js +5 -2
  51. package/dist/planmagnet/pages/analytics/WorkBreakdownCard.js.map +1 -1
  52. package/dist/planmagnet/product/ProductControls.d.ts +33 -0
  53. package/dist/planmagnet/product/ProductControls.d.ts.map +1 -1
  54. package/dist/planmagnet/product/ProductControls.js +593 -268
  55. package/dist/planmagnet/product/ProductControls.js.map +1 -1
  56. package/dist/planmagnet/product/routes.d.ts +10 -0
  57. package/dist/planmagnet/product/routes.d.ts.map +1 -0
  58. package/dist/planmagnet/product/routes.js +6 -0
  59. package/dist/planmagnet/product/routes.js.map +1 -0
  60. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts +10 -0
  61. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
  62. package/dist/planmagnet/roadmaps/RoadmapControls.js +235 -184
  63. package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
  64. package/dist/planmagnet/utils/statusColor.d.ts +8 -9
  65. package/dist/planmagnet/utils/statusColor.d.ts.map +1 -1
  66. package/dist/planmagnet/utils/statusColor.js +8 -40
  67. package/dist/planmagnet/utils/statusColor.js.map +1 -1
  68. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"RoadmapControls.js","names":[],"sources":["../../../src/planmagnet/roadmaps/RoadmapControls.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport {\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { BarChart3, Pencil, Plus, Trash2 } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { throwOnError, LEAVES_PAGE_CONTEXT } from '../utils/mutationResult';\nimport { mutationErrorMessage } from '../utils/graphqlErrors';\nimport { endBeforeStart, parseIntField } from '../utils/formValidation';\nimport { ConfirmButton, ErrorLine } from '../controls/ConfirmButton';\nimport { enumLabel } from '../controls/WorkItemControls';\n\n/**\n * Roadmap editing (BOFF-7941). Until 2026-09-27 a roadmap was a dead end: no page called\n * planmagnetCreateRoadmapItem / UpdateRoadmapItem / DeleteRoadmapItem, so a new roadmap stayed at 0 items and 0%\n * for good, and the Edit page for the roadmap itself was linked from nowhere.\n */\n\nexport const ROADMAP_ITEM_STATUSES = [\n 'PLANNED',\n 'IN_PROGRESS',\n 'AT_RISK',\n 'DELAYED',\n 'COMPLETED',\n 'CANCELLED',\n] as const;\n\n/** Now/Next/Later horizon (PRM-06). Not placed = no horizon at all (`null`), shown as its own option below. */\nexport const ROADMAP_HORIZONS = ['NOW', 'NEXT', 'LATER'] as const;\n/** Risk level recorded against a roadmap item (PRM-10). \"None\" (no risk assessed) is `null`, not a 4th value. */\nexport const ROADMAP_RISK_LEVELS = ['LOW', 'MEDIUM', 'HIGH'] as const;\n\nexport interface RoadmapItemValues {\n id: string;\n title: string;\n description: string | null;\n status: string;\n swimlane: string | null;\n startDate: string | null;\n endDate: string | null;\n /** Now/Next/Later placement (PRM-06). Null = not placed on any horizon yet. */\n horizon: string | null;\n /** Confidence this item lands as planned, 0-100 (PRM-10). Null = never recorded. */\n confidence: number | null;\n /** Risk level recorded against this item (PRM-10). Null = no risk assessed. */\n riskLevel: string | null;\n /** Free-text risk note, at most 1000 characters (PRM-10). Null = no risk assessed. */\n riskNote: string | null;\n}\n\n/** Every query that shows a roadmap's items, counts or progress — including the Now/Next/Later view's own item\n * list (`RoadmapNowNextLater.tsx`), so a placement/confidence/risk change from either view refreshes the other. */\nexport const ROADMAP_QUERIES = [\n 'GetRoadmapDetailPage',\n 'GetRoadmapDetail',\n 'ListRoadmapsPage',\n 'ListRoadmaps',\n 'GetRoadmapAnalytics',\n 'RoadmapNowNextLaterItems',\n];\n\nconst CREATE_ITEM = gql`\n mutation RoadmapControlsCreateItem($input: PlanmagnetCreateRoadmapItemInput!) {\n planmagnetCreateRoadmapItem(input: $input) {\n id\n }\n }\n`;\nconst UPDATE_ITEM = gql`\n mutation RoadmapControlsUpdateItem($id: ID!, $input: PlanmagnetUpdateRoadmapItemInput!) {\n planmagnetUpdateRoadmapItem(id: $id, input: $input) {\n id\n title\n description\n status\n swimlane\n startDate\n endDate\n horizon\n confidence\n riskLevel\n riskNote\n }\n }\n`;\nconst DELETE_ITEM = gql`\n mutation RoadmapControlsDeleteItem($id: ID!) {\n planmagnetDeleteRoadmapItem(id: $id)\n }\n`;\nconst DELETE_ROADMAP = gql`\n mutation RoadmapControlsDelete($id: ID!) {\n planmagnetDeleteRoadmap(id: $id)\n }\n`;\n\nconst day = (iso: string | null | undefined) => (iso ? iso.slice(0, 10) : '');\n\n/**\n * Add an item (no `item`) or edit one. Dates are sent only when they changed: the service checks the pair the item\n * would end up with, so re-sending untouched dates of an older item stored inverted would refuse a plain rename.\n */\nexport function RoadmapItemDialog({\n roadmapId,\n item,\n lanes,\n}: {\n roadmapId: string;\n item?: RoadmapItemValues;\n lanes: string[];\n}) {\n const initial = {\n title: item?.title ?? '',\n description: item?.description ?? '',\n status: item?.status ?? 'PLANNED',\n swimlane: item?.swimlane ?? '',\n startDate: day(item?.startDate),\n endDate: day(item?.endDate),\n horizon: item?.horizon ?? '',\n confidence: item?.confidence != null ? String(item.confidence) : '',\n riskLevel: item?.riskLevel ?? '',\n riskNote: item?.riskNote ?? '',\n };\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(initial);\n const [error, setError] = useState<string | null>(null);\n const options = { refetchQueries: ROADMAP_QUERIES, awaitRefetchQueries: true };\n const [create, createState] = useMutation(CREATE_ITEM, options);\n const [update, updateState] = useMutation(UPDATE_ITEM, options);\n const saving = createState.loading || updateState.loading;\n const invalidDates = endBeforeStart(form.startDate, form.endDate);\n // Client-side bound matches the server's (0-100 whole number): a BAD_USER_INPUT round trip is avoidable, not\n // the only check — the server still enforces it (extensions.field: \"confidence\" on a refusal that slips past this).\n const confidence = parseIntField(form.confidence, { min: 0, max: 100 });\n const canSave = form.title.trim() !== '' && !invalidDates && !confidence.invalid && !saving;\n const statuses = (ROADMAP_ITEM_STATUSES as readonly string[]).includes(form.status)\n ? ROADMAP_ITEM_STATUSES\n : [form.status, ...ROADMAP_ITEM_STATUSES];\n const horizons = (ROADMAP_HORIZONS as readonly string[]).includes(form.horizon)\n ? ROADMAP_HORIZONS\n : form.horizon\n ? [form.horizon, ...ROADMAP_HORIZONS]\n : ROADMAP_HORIZONS;\n const riskLevels = (ROADMAP_RISK_LEVELS as readonly string[]).includes(form.riskLevel)\n ? ROADMAP_RISK_LEVELS\n : form.riskLevel\n ? [form.riskLevel, ...ROADMAP_RISK_LEVELS]\n : ROADMAP_RISK_LEVELS;\n const idPrefix = item ? `roadmap-item-${item.id}` : 'roadmap-item-new';\n\n const save = async () => {\n setError(null);\n const common = {\n title: form.title.trim(),\n description: form.description.trim() || null,\n status: form.status,\n swimlane: form.swimlane.trim() || null,\n };\n try {\n if (item) {\n const dates = {\n ...(form.startDate !== initial.startDate && { startDate: form.startDate || null }),\n ...(form.endDate !== initial.endDate && { endDate: form.endDate || null }),\n };\n // PRM-06/PRM-10 fields: sent only when changed, same reasoning as dates above — resending an unchanged\n // horizon/confidence/riskLevel/riskNote on a plain rename risks an unrelated refusal for no reason.\n const placement = {\n ...(form.horizon !== initial.horizon && { horizon: form.horizon || null }),\n ...(form.confidence !== initial.confidence && { confidence: confidence.value ?? null }),\n ...(form.riskLevel !== initial.riskLevel && { riskLevel: form.riskLevel || null }),\n ...(form.riskNote.trim() !== initial.riskNote.trim() && {\n riskNote: form.riskNote.trim() || null,\n }),\n };\n await throwOnError(\n update({ variables: { id: item.id, input: { ...common, ...dates, ...placement } } })\n );\n } else {\n await throwOnError(\n create({\n variables: {\n input: {\n roadmapId,\n ...common,\n startDate: form.startDate || undefined,\n endDate: form.endDate || undefined,\n horizon: form.horizon || undefined,\n confidence: confidence.value,\n riskLevel: form.riskLevel || undefined,\n riskNote: form.riskNote.trim() || undefined,\n },\n },\n })\n );\n setForm(initial);\n }\n setOpen(false);\n } catch (e) {\n setError(mutationErrorMessage(e, 'The item could not be saved.'));\n }\n };\n\n const field = 'mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm';\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) setForm(initial);\n else setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n {item ? (\n <Button size=\"sm\" variant=\"ghost\" aria-label={`Edit ${item.title}`}>\n <Pencil className=\"h-4 w-4\" />\n </Button>\n ) : (\n <Button size=\"sm\" data-testid=\"roadmap-add-item\">\n <Plus className=\"mr-1 h-4 w-4\" />\n Add item\n </Button>\n )}\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {item ? 'Edit roadmap item' : 'Add roadmap item'}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Items in the same swimlane are grouped together on the roadmap.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <div>\n <label htmlFor={`${idPrefix}-title`} className=\"text-sm font-medium\">\n Title\n </label>\n <Input\n id={`${idPrefix}-title`}\n className=\"mt-1\"\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor={`${idPrefix}-lane`} className=\"text-sm font-medium\">\n Swimlane\n </label>\n <Input\n id={`${idPrefix}-lane`}\n className=\"mt-1\"\n list={`${idPrefix}-lanes`}\n placeholder=\"e.g. Q4\"\n value={form.swimlane}\n onChange={(e) => setForm({ ...form, swimlane: e.target.value })}\n />\n <datalist id={`${idPrefix}-lanes`}>\n {lanes.map((lane) => (\n <option key={lane} value={lane} />\n ))}\n </datalist>\n </div>\n <div>\n <label htmlFor={`${idPrefix}-status`} className=\"text-sm font-medium\">\n Status\n </label>\n <select\n id={`${idPrefix}-status`}\n className={field}\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n >\n {statuses.map((s) => (\n <option key={s} value={s}>\n {enumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor={`${idPrefix}-start`} className=\"text-sm font-medium\">\n Start date\n </label>\n <Input\n id={`${idPrefix}-start`}\n type=\"date\"\n className=\"mt-1\"\n value={form.startDate}\n max={form.endDate || undefined}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor={`${idPrefix}-end`} className=\"text-sm font-medium\">\n End date\n </label>\n <Input\n id={`${idPrefix}-end`}\n type=\"date\"\n className=\"mt-1\"\n value={form.endDate}\n min={form.startDate || undefined}\n onChange={(e) => setForm({ ...form, endDate: e.target.value })}\n />\n </div>\n </div>\n {invalidDates && <ErrorLine message=\"The end date is before the start date.\" />}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor={`${idPrefix}-horizon`} className=\"text-sm font-medium\">\n Horizon\n </label>\n <select\n id={`${idPrefix}-horizon`}\n data-testid=\"roadmap-item-horizon\"\n className={field}\n value={form.horizon}\n onChange={(e) => setForm({ ...form, horizon: e.target.value })}\n >\n <option value=\"\">Not placed</option>\n {horizons.map((h) => (\n <option key={h} value={h}>\n {enumLabel(h)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor={`${idPrefix}-confidence`} className=\"text-sm font-medium\">\n Confidence (0-100)\n </label>\n <Input\n id={`${idPrefix}-confidence`}\n data-testid=\"roadmap-item-confidence\"\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n max={100}\n className=\"mt-1\"\n value={form.confidence}\n onChange={(e) => setForm({ ...form, confidence: e.target.value })}\n />\n {confidence.invalid && (\n <ErrorLine message=\"Confidence must be a whole number from 0 to 100.\" />\n )}\n </div>\n </div>\n <div>\n <label htmlFor={`${idPrefix}-risk-level`} className=\"text-sm font-medium\">\n Risk level\n </label>\n <select\n id={`${idPrefix}-risk-level`}\n data-testid=\"roadmap-item-risk-level\"\n className={field}\n value={form.riskLevel}\n onChange={(e) => setForm({ ...form, riskLevel: e.target.value })}\n >\n <option value=\"\">None</option>\n {riskLevels.map((r) => (\n <option key={r} value={r}>\n {enumLabel(r)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor={`${idPrefix}-risk-note`} className=\"text-sm font-medium\">\n Risk note\n </label>\n <textarea\n id={`${idPrefix}-risk-note`}\n data-testid=\"roadmap-item-risk-note\"\n className={`${field} min-h-[56px]`}\n maxLength={1000}\n placeholder=\"Why this item is risky, and what would reduce it\"\n value={form.riskNote}\n onChange={(e) => setForm({ ...form, riskNote: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor={`${idPrefix}-description`} className=\"text-sm font-medium\">\n Description\n </label>\n <textarea\n id={`${idPrefix}-description`}\n className={`${field} min-h-[72px]`}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button onClick={() => void save()} disabled={!canSave}>\n {saving ? 'Saving…' : item ? 'Save item' : 'Add item'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nexport function RemoveRoadmapItemButton({ id, title }: { id: string; title: string }) {\n const [remove, state] = useMutation(DELETE_ITEM, {\n refetchQueries: ROADMAP_QUERIES,\n awaitRefetchQueries: true,\n });\n const [error, setError] = useState<string | null>(null);\n const onRemove = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id } }));\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The item could not be removed.');\n }\n };\n return (\n <div className=\"flex flex-col items-end\">\n <ConfirmButton\n label=\"\"\n ariaLabel={`Remove ${title}`}\n variant=\"outline\"\n icon={<Trash2 className=\"h-4 w-4\" />}\n title={`Remove \"${title}\" from the roadmap?`}\n description=\"The item is removed from this roadmap. This cannot be undone here.\"\n confirmLabel=\"Remove item\"\n destructive\n disabled={state.loading}\n onConfirm={() => void onRemove()}\n />\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n\n/** Edit (the roadmap's own fields live on the Edit page) and Delete for the roadmap header. */\nexport function RoadmapHeaderActions({ roadmapId, name }: { roadmapId: string; name: string }) {\n const navigate = usePlanMagnetNavigate();\n const [remove, state] = useMutation(DELETE_ROADMAP, {\n refetchQueries: ['ListRoadmapsPage', 'ListRoadmaps'],\n context: LEAVES_PAGE_CONTEXT,\n });\n const [error, setError] = useState<string | null>(null);\n const onDelete = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: roadmapId } }));\n navigate('/products/roadmaps-page');\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The roadmap could not be deleted.');\n }\n };\n return (\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n data-testid=\"roadmap-edit\"\n // BOFF-8045: edit-product-page now edits a real Product, not a Roadmap —\n // this button owns the roadmap's own fields, so it has its own page.\n onClick={() => navigate(detailPath('/products/edit-roadmap-page', roadmapId))}\n >\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n {/* BOFF-7983: the analytics page existed with no way in. */}\n <Button\n size=\"sm\"\n variant=\"outline\"\n data-testid=\"roadmap-analytics\"\n onClick={() => navigate(detailPath('/products/roadmap-analytics-page', roadmapId))}\n >\n <BarChart3 className=\"mr-1 h-4 w-4\" />\n Analytics\n </Button>\n <ConfirmButton\n label=\"Delete\"\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n title={`Delete ${name}?`}\n description=\"The roadmap and its items are deleted. This cannot be undone here.\"\n confirmLabel=\"Delete roadmap\"\n destructive\n disabled={state.loading}\n onConfirm={() => void onDelete()}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RoadmapControls.js","names":[],"sources":["../../../src/planmagnet/roadmaps/RoadmapControls.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport {\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { BarChart3, Pencil, Plus, Trash2 } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { throwOnError, LEAVES_PAGE_CONTEXT } from '../utils/mutationResult';\nimport { mutationErrorMessage } from '../utils/graphqlErrors';\nimport { endBeforeStart, parseIntField } from '../utils/formValidation';\nimport { ConfirmButton, ErrorLine } from '../controls/ConfirmButton';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport {\n PrioritizationScoreFields,\n QuadrantBadge,\n prioritizationScoresValid,\n type PrioritizationFormValues,\n} from '../controls/PrioritizationControls';\n\n/**\n * Roadmap editing (BOFF-7941). Until 2026-09-27 a roadmap was a dead end: no page called\n * planmagnetCreateRoadmapItem / UpdateRoadmapItem / DeleteRoadmapItem, so a new roadmap stayed at 0 items and 0%\n * for good, and the Edit page for the roadmap itself was linked from nowhere.\n */\n\nexport const ROADMAP_ITEM_STATUSES = [\n 'PLANNED',\n 'IN_PROGRESS',\n 'AT_RISK',\n 'DELAYED',\n 'COMPLETED',\n 'CANCELLED',\n] as const;\n\n/** Now/Next/Later horizon (PRM-06). Not placed = no horizon at all (`null`), shown as its own option below. */\nexport const ROADMAP_HORIZONS = ['NOW', 'NEXT', 'LATER'] as const;\n/** Risk level recorded against a roadmap item (PRM-10). \"None\" (no risk assessed) is `null`, not a 4th value. */\nexport const ROADMAP_RISK_LEVELS = ['LOW', 'MEDIUM', 'HIGH'] as const;\n\nexport interface RoadmapItemValues {\n id: string;\n title: string;\n description: string | null;\n status: string;\n swimlane: string | null;\n startDate: string | null;\n endDate: string | null;\n /** Now/Next/Later placement (PRM-06). Null = not placed on any horizon yet. */\n horizon: string | null;\n /** Confidence this item lands as planned, 0-100 (PRM-10). Null = never recorded. */\n confidence: number | null;\n /** Risk level recorded against this item (PRM-10). Null = no risk assessed. */\n riskLevel: string | null;\n /** Free-text risk note, at most 1000 characters (PRM-10). Null = no risk assessed. */\n riskNote: string | null;\n /** MoSCoW classification (PRM-27). Null = never set. */\n moscow?: string | null;\n /** Kano classification (PRM-27). Null = never set. */\n kano?: string | null;\n /** Value score 1-10 (PRM-27). Null = never set. */\n valueScore?: number | null;\n /** Effort score 1-10 (PRM-27). Null = never set. */\n effortScore?: number | null;\n /** Server-computed, never sent back (PRM-27). Null unless both scores are set. */\n valueEffortQuadrant?: string | null;\n}\n\n/** Every query that shows a roadmap's items, counts or progress — including the Now/Next/Later view's own item\n * list (`RoadmapNowNextLater.tsx`), so a placement/confidence/risk change from either view refreshes the other. */\nexport const ROADMAP_QUERIES = [\n 'GetRoadmapDetailPage',\n 'GetRoadmapDetail',\n 'ListRoadmapsPage',\n 'ListRoadmaps',\n 'GetRoadmapAnalytics',\n 'RoadmapNowNextLaterItems',\n];\n\nconst CREATE_ITEM = gql`\n mutation RoadmapControlsCreateItem($input: PlanmagnetCreateRoadmapItemInput!) {\n planmagnetCreateRoadmapItem(input: $input) {\n id\n }\n }\n`;\nconst UPDATE_ITEM = gql`\n mutation RoadmapControlsUpdateItem($id: ID!, $input: PlanmagnetUpdateRoadmapItemInput!) {\n planmagnetUpdateRoadmapItem(id: $id, input: $input) {\n id\n title\n description\n status\n swimlane\n startDate\n endDate\n horizon\n confidence\n riskLevel\n riskNote\n moscow\n kano\n valueScore\n effortScore\n valueEffortQuadrant\n }\n }\n`;\nconst DELETE_ITEM = gql`\n mutation RoadmapControlsDeleteItem($id: ID!) {\n planmagnetDeleteRoadmapItem(id: $id)\n }\n`;\nconst DELETE_ROADMAP = gql`\n mutation RoadmapControlsDelete($id: ID!) {\n planmagnetDeleteRoadmap(id: $id)\n }\n`;\n\nconst day = (iso: string | null | undefined) => (iso ? iso.slice(0, 10) : '');\n\n/**\n * Add an item (no `item`) or edit one. Dates are sent only when they changed: the service checks the pair the item\n * would end up with, so re-sending untouched dates of an older item stored inverted would refuse a plain rename.\n */\nexport function RoadmapItemDialog({\n roadmapId,\n item,\n lanes,\n}: {\n roadmapId: string;\n item?: RoadmapItemValues;\n lanes: string[];\n}) {\n const initial = {\n title: item?.title ?? '',\n description: item?.description ?? '',\n status: item?.status ?? 'PLANNED',\n swimlane: item?.swimlane ?? '',\n startDate: day(item?.startDate),\n endDate: day(item?.endDate),\n horizon: item?.horizon ?? '',\n confidence: item?.confidence != null ? String(item.confidence) : '',\n riskLevel: item?.riskLevel ?? '',\n riskNote: item?.riskNote ?? '',\n };\n // PRM-27: MoSCoW/Kano/Value-Effort scoring, seeded separately from `initial` above so the diff logic below\n // reads exactly like the horizon/confidence/riskLevel/riskNote block it sits next to.\n const initialPrioritization: PrioritizationFormValues = {\n moscow: item?.moscow ?? '',\n kano: item?.kano ?? '',\n valueScore: item?.valueScore != null ? String(item.valueScore) : '',\n effortScore: item?.effortScore != null ? String(item.effortScore) : '',\n };\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(initial);\n const [prioritization, setPrioritization] =\n useState<PrioritizationFormValues>(initialPrioritization);\n const [error, setError] = useState<string | null>(null);\n const options = { refetchQueries: ROADMAP_QUERIES, awaitRefetchQueries: true };\n const [create, createState] = useMutation(CREATE_ITEM, options);\n const [update, updateState] = useMutation(UPDATE_ITEM, options);\n const saving = createState.loading || updateState.loading;\n const invalidDates = endBeforeStart(form.startDate, form.endDate);\n // Client-side bound matches the server's (0-100 whole number): a BAD_USER_INPUT round trip is avoidable, not\n // the only check — the server still enforces it (extensions.field: \"confidence\" on a refusal that slips past this).\n const confidence = parseIntField(form.confidence, { min: 0, max: 100 });\n const canSave =\n form.title.trim() !== '' &&\n !invalidDates &&\n !confidence.invalid &&\n prioritizationScoresValid(prioritization) &&\n !saving;\n const statuses = (ROADMAP_ITEM_STATUSES as readonly string[]).includes(form.status)\n ? ROADMAP_ITEM_STATUSES\n : [form.status, ...ROADMAP_ITEM_STATUSES];\n const horizons = (ROADMAP_HORIZONS as readonly string[]).includes(form.horizon)\n ? ROADMAP_HORIZONS\n : form.horizon\n ? [form.horizon, ...ROADMAP_HORIZONS]\n : ROADMAP_HORIZONS;\n const riskLevels = (ROADMAP_RISK_LEVELS as readonly string[]).includes(form.riskLevel)\n ? ROADMAP_RISK_LEVELS\n : form.riskLevel\n ? [form.riskLevel, ...ROADMAP_RISK_LEVELS]\n : ROADMAP_RISK_LEVELS;\n const idPrefix = item ? `roadmap-item-${item.id}` : 'roadmap-item-new';\n\n const save = async () => {\n setError(null);\n const common = {\n title: form.title.trim(),\n description: form.description.trim() || null,\n status: form.status,\n swimlane: form.swimlane.trim() || null,\n };\n const value = parseIntField(prioritization.valueScore, { min: 1, max: 10 });\n const effort = parseIntField(prioritization.effortScore, { min: 1, max: 10 });\n try {\n if (item) {\n const dates = {\n ...(form.startDate !== initial.startDate && { startDate: form.startDate || null }),\n ...(form.endDate !== initial.endDate && { endDate: form.endDate || null }),\n };\n // PRM-06/PRM-10 fields: sent only when changed, same reasoning as dates above — resending an unchanged\n // horizon/confidence/riskLevel/riskNote on a plain rename risks an unrelated refusal for no reason.\n const placement = {\n ...(form.horizon !== initial.horizon && { horizon: form.horizon || null }),\n ...(form.confidence !== initial.confidence && { confidence: confidence.value ?? null }),\n ...(form.riskLevel !== initial.riskLevel && { riskLevel: form.riskLevel || null }),\n ...(form.riskNote.trim() !== initial.riskNote.trim() && {\n riskNote: form.riskNote.trim() || null,\n }),\n };\n // PRM-27: same \"only when changed\" rule as placement above.\n const scoring = {\n ...(prioritization.moscow !== initialPrioritization.moscow && {\n moscow: prioritization.moscow || null,\n }),\n ...(prioritization.kano !== initialPrioritization.kano && {\n kano: prioritization.kano || null,\n }),\n ...(prioritization.valueScore !== initialPrioritization.valueScore && {\n valueScore: value.value ?? null,\n }),\n ...(prioritization.effortScore !== initialPrioritization.effortScore && {\n effortScore: effort.value ?? null,\n }),\n };\n await throwOnError(\n update({\n variables: { id: item.id, input: { ...common, ...dates, ...placement, ...scoring } },\n })\n );\n } else {\n await throwOnError(\n create({\n variables: {\n input: {\n roadmapId,\n ...common,\n startDate: form.startDate || undefined,\n endDate: form.endDate || undefined,\n horizon: form.horizon || undefined,\n confidence: confidence.value,\n riskLevel: form.riskLevel || undefined,\n riskNote: form.riskNote.trim() || undefined,\n moscow: prioritization.moscow || undefined,\n kano: prioritization.kano || undefined,\n valueScore: value.value,\n effortScore: effort.value,\n },\n },\n })\n );\n setForm(initial);\n setPrioritization(initialPrioritization);\n }\n setOpen(false);\n } catch (e) {\n setError(mutationErrorMessage(e, 'The item could not be saved.'));\n }\n };\n\n const field = 'mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm';\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) {\n setForm(initial);\n setPrioritization(initialPrioritization);\n } else {\n setError(null);\n }\n }}\n >\n <ResponsiveDialogTrigger asChild>\n {item ? (\n <Button size=\"sm\" variant=\"ghost\" aria-label={`Edit ${item.title}`}>\n <Pencil className=\"h-4 w-4\" />\n </Button>\n ) : (\n <Button size=\"sm\" data-testid=\"roadmap-add-item\">\n <Plus className=\"mr-1 h-4 w-4\" />\n Add item\n </Button>\n )}\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {item ? 'Edit roadmap item' : 'Add roadmap item'}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Items in the same swimlane are grouped together on the roadmap.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <div>\n <label htmlFor={`${idPrefix}-title`} className=\"text-sm font-medium\">\n Title\n </label>\n <Input\n id={`${idPrefix}-title`}\n className=\"mt-1\"\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor={`${idPrefix}-lane`} className=\"text-sm font-medium\">\n Swimlane\n </label>\n <Input\n id={`${idPrefix}-lane`}\n className=\"mt-1\"\n list={`${idPrefix}-lanes`}\n placeholder=\"e.g. Q4\"\n value={form.swimlane}\n onChange={(e) => setForm({ ...form, swimlane: e.target.value })}\n />\n <datalist id={`${idPrefix}-lanes`}>\n {lanes.map((lane) => (\n <option key={lane} value={lane} />\n ))}\n </datalist>\n </div>\n <div>\n <label htmlFor={`${idPrefix}-status`} className=\"text-sm font-medium\">\n Status\n </label>\n <select\n id={`${idPrefix}-status`}\n className={field}\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n >\n {statuses.map((s) => (\n <option key={s} value={s}>\n {enumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor={`${idPrefix}-start`} className=\"text-sm font-medium\">\n Start date\n </label>\n <Input\n id={`${idPrefix}-start`}\n type=\"date\"\n className=\"mt-1\"\n value={form.startDate}\n max={form.endDate || undefined}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor={`${idPrefix}-end`} className=\"text-sm font-medium\">\n End date\n </label>\n <Input\n id={`${idPrefix}-end`}\n type=\"date\"\n className=\"mt-1\"\n value={form.endDate}\n min={form.startDate || undefined}\n onChange={(e) => setForm({ ...form, endDate: e.target.value })}\n />\n </div>\n </div>\n {invalidDates && <ErrorLine message=\"The end date is before the start date.\" />}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor={`${idPrefix}-horizon`} className=\"text-sm font-medium\">\n Horizon\n </label>\n <select\n id={`${idPrefix}-horizon`}\n data-testid=\"roadmap-item-horizon\"\n className={field}\n value={form.horizon}\n onChange={(e) => setForm({ ...form, horizon: e.target.value })}\n >\n <option value=\"\">Not placed</option>\n {horizons.map((h) => (\n <option key={h} value={h}>\n {enumLabel(h)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor={`${idPrefix}-confidence`} className=\"text-sm font-medium\">\n Confidence (0-100)\n </label>\n <Input\n id={`${idPrefix}-confidence`}\n data-testid=\"roadmap-item-confidence\"\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n max={100}\n className=\"mt-1\"\n value={form.confidence}\n onChange={(e) => setForm({ ...form, confidence: e.target.value })}\n />\n {confidence.invalid && (\n <ErrorLine message=\"Confidence must be a whole number from 0 to 100.\" />\n )}\n </div>\n </div>\n <div>\n <label htmlFor={`${idPrefix}-risk-level`} className=\"text-sm font-medium\">\n Risk level\n </label>\n <select\n id={`${idPrefix}-risk-level`}\n data-testid=\"roadmap-item-risk-level\"\n className={field}\n value={form.riskLevel}\n onChange={(e) => setForm({ ...form, riskLevel: e.target.value })}\n >\n <option value=\"\">None</option>\n {riskLevels.map((r) => (\n <option key={r} value={r}>\n {enumLabel(r)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor={`${idPrefix}-risk-note`} className=\"text-sm font-medium\">\n Risk note\n </label>\n <textarea\n id={`${idPrefix}-risk-note`}\n data-testid=\"roadmap-item-risk-note\"\n className={`${field} min-h-[56px]`}\n maxLength={1000}\n placeholder=\"Why this item is risky, and what would reduce it\"\n value={form.riskNote}\n onChange={(e) => setForm({ ...form, riskNote: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor={`${idPrefix}-description`} className=\"text-sm font-medium\">\n Description\n </label>\n <textarea\n id={`${idPrefix}-description`}\n className={`${field} min-h-[72px]`}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n {/* PRM-27: MoSCoW, Kano and Value-vs-Effort scoring. The quadrant badge shown here is the value last\n read back from the server (this item's own `valueEffortQuadrant`, never computed client-side) —\n on save the roadmap detail page's item row (which stays mounted under this dialog) refreshes with\n the freshly-computed one, since GetRoadmapDetailPage is in ROADMAP_QUERIES and is awaited before\n this dialog closes. */}\n <div className=\"border-t pt-3\">\n <p className=\"text-sm font-medium\">Prioritization</p>\n {item && (\n <div className=\"mt-1 flex items-center gap-2\">\n <span className=\"text-xs text-muted-foreground\">Current quadrant:</span>\n <QuadrantBadge quadrant={item.valueEffortQuadrant ?? null} />\n </div>\n )}\n <div className=\"mt-2\">\n <PrioritizationScoreFields\n idPrefix={idPrefix}\n values={prioritization}\n onChange={setPrioritization}\n />\n </div>\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button onClick={() => void save()} disabled={!canSave}>\n {saving ? 'Saving…' : item ? 'Save item' : 'Add item'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nexport function RemoveRoadmapItemButton({ id, title }: { id: string; title: string }) {\n const [remove, state] = useMutation(DELETE_ITEM, {\n refetchQueries: ROADMAP_QUERIES,\n awaitRefetchQueries: true,\n });\n const [error, setError] = useState<string | null>(null);\n const onRemove = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id } }));\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The item could not be removed.');\n }\n };\n return (\n <div className=\"flex flex-col items-end\">\n <ConfirmButton\n label=\"\"\n ariaLabel={`Remove ${title}`}\n variant=\"outline\"\n icon={<Trash2 className=\"h-4 w-4\" />}\n title={`Remove \"${title}\" from the roadmap?`}\n description=\"The item is removed from this roadmap. This cannot be undone here.\"\n confirmLabel=\"Remove item\"\n destructive\n disabled={state.loading}\n onConfirm={() => void onRemove()}\n />\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n\n/** Edit (the roadmap's own fields live on the Edit page) and Delete for the roadmap header. */\nexport function RoadmapHeaderActions({ roadmapId, name }: { roadmapId: string; name: string }) {\n const navigate = usePlanMagnetNavigate();\n const [remove, state] = useMutation(DELETE_ROADMAP, {\n refetchQueries: ['ListRoadmapsPage', 'ListRoadmaps'],\n context: LEAVES_PAGE_CONTEXT,\n });\n const [error, setError] = useState<string | null>(null);\n const onDelete = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: roadmapId } }));\n navigate('/products/roadmaps-page');\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The roadmap could not be deleted.');\n }\n };\n return (\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n data-testid=\"roadmap-edit\"\n // BOFF-8045: edit-product-page now edits a real Product, not a Roadmap —\n // this button owns the roadmap's own fields, so it has its own page.\n onClick={() => navigate(detailPath('/products/edit-roadmap-page', roadmapId))}\n >\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n {/* BOFF-7983: the analytics page existed with no way in. */}\n <Button\n size=\"sm\"\n variant=\"outline\"\n data-testid=\"roadmap-analytics\"\n onClick={() => navigate(detailPath('/products/roadmap-analytics-page', roadmapId))}\n >\n <BarChart3 className=\"mr-1 h-4 w-4\" />\n Analytics\n </Button>\n <ConfirmButton\n label=\"Delete\"\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n title={`Delete ${name}?`}\n description=\"The roadmap and its items are deleted. This cannot be undone here.\"\n confirmLabel=\"Delete roadmap\"\n destructive\n disabled={state.loading}\n onConfirm={() => void onDelete()}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -4,21 +4,20 @@
4
4
  * re-assigned from whichever statuses happen to appear in a given project/graph — a categorical channel keeps the
5
5
  * same color for the same identity everywhere it shows up.
6
6
  *
7
- * Values are complete literal Tailwind class names (never built with a template string): Tailwind's build scans
8
- * source text for whole class-name tokens, so `` `fill-${status}` `` would never be picked up and would ship
9
- * invisible/unstyled in production while every check here still passes.
7
+ * Values are CSS custom-property references to fe-libs' chart token ramp (`var(--color-chart-N)`), applied through
8
+ * inline `style` (`fill`, `stroke`, `backgroundColor`) — NOT Tailwind utilities. The host apps' Tailwind themes do
9
+ * not register `--color-chart-*`, so chart utility classes are never generated: measured in prod 2026-09-30, no
10
+ * such rule existed in the served CSS and every chart mark rendered black. The token variables themselves are
11
+ * always defined (fe-libs tokens.css), so referencing them directly works in every host.
10
12
  *
11
13
  * Colors come from fe-libs' chart token ramp (`chart-1`..`chart-12`), not the `status-*` tokens — those are
12
14
  * reserved for good/warning/serious/critical severity (see `dev/skills` dataviz guidance) and workflow status is
13
15
  * an identity dimension, not a severity one.
14
16
  */
15
17
  export interface StatusVisual {
16
- /** Fill class for the node/bar body. */
17
- fill: string;
18
- /** Stroke class matching `fill`, for outlines and edge/line marks of the same status. */
19
- stroke: string;
20
- /** Solid swatch class for a small legend dot. */
21
- swatch: string;
18
+ /** The status's chart color as a CSS value (`var(--color-chart-N)`) — use as an SVG `fill`/`stroke` or a legend
19
+ * swatch's `backgroundColor` through inline style. */
20
+ color: string;
22
21
  }
23
22
  export declare function statusVisual(status: string): StatusVisual;
24
23
  /** Every status with an assigned visual, in the fixed legend order. */
@@ -1 +1 @@
1
- {"version":3,"file":"statusColor.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/utils/statusColor.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;CAChB;AAoBD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAEzD;AAED,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAAuB,CAAC"}
1
+ {"version":3,"file":"statusColor.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/utils/statusColor.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,YAAY;IAC3B;0DACsD;IACtD,KAAK,EAAE,MAAM,CAAC;CACf;AAgBD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAEzD;AAED,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAAuB,CAAC"}
@@ -1,46 +1,14 @@
1
1
  import { WORK_ITEM_STATUSES as e } from "../controls/WorkItemControls.js";
2
2
  //#region src/planmagnet/utils/statusColor.ts
3
3
  var t = {
4
- BACKLOG: {
5
- fill: "fill-chart-6",
6
- stroke: "stroke-chart-6",
7
- swatch: "bg-chart-6"
8
- },
9
- TODO: {
10
- fill: "fill-chart-1",
11
- stroke: "stroke-chart-1",
12
- swatch: "bg-chart-1"
13
- },
14
- IN_PROGRESS: {
15
- fill: "fill-chart-4",
16
- stroke: "stroke-chart-4",
17
- swatch: "bg-chart-4"
18
- },
19
- IN_REVIEW: {
20
- fill: "fill-chart-3",
21
- stroke: "stroke-chart-3",
22
- swatch: "bg-chart-3"
23
- },
24
- TESTING: {
25
- fill: "fill-chart-9",
26
- stroke: "stroke-chart-9",
27
- swatch: "bg-chart-9"
28
- },
29
- DONE: {
30
- fill: "fill-chart-2",
31
- stroke: "stroke-chart-2",
32
- swatch: "bg-chart-2"
33
- },
34
- CANCELLED: {
35
- fill: "fill-chart-10",
36
- stroke: "stroke-chart-10",
37
- swatch: "bg-chart-10"
38
- }
39
- }, n = {
40
- fill: "fill-chart-12",
41
- stroke: "stroke-chart-12",
42
- swatch: "bg-chart-12"
43
- };
4
+ BACKLOG: { color: "var(--color-chart-6)" },
5
+ TODO: { color: "var(--color-chart-1)" },
6
+ IN_PROGRESS: { color: "var(--color-chart-4)" },
7
+ IN_REVIEW: { color: "var(--color-chart-3)" },
8
+ TESTING: { color: "var(--color-chart-9)" },
9
+ DONE: { color: "var(--color-chart-2)" },
10
+ CANCELLED: { color: "var(--color-chart-10)" }
11
+ }, n = { color: "var(--color-chart-12)" };
44
12
  function r(e) {
45
13
  return t[e] ?? n;
46
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"statusColor.js","names":[],"sources":["../../../src/planmagnet/utils/statusColor.ts"],"sourcesContent":["import { WORK_ITEM_STATUSES } from '../controls/WorkItemControls';\n\n/**\n * One fixed chart color per WorkItemStatus (PDM-27 dependency graph, ANR-24 Gantt): both visualizations color by\n * status, so they share this single mapping rather than each re-deriving one. The order is FIXED and never\n * re-assigned from whichever statuses happen to appear in a given project/graph — a categorical channel keeps the\n * same color for the same identity everywhere it shows up.\n *\n * Values are complete literal Tailwind class names (never built with a template string): Tailwind's build scans\n * source text for whole class-name tokens, so `` `fill-${status}` `` would never be picked up and would ship\n * invisible/unstyled in production while every check here still passes.\n *\n * Colors come from fe-libs' chart token ramp (`chart-1`..`chart-12`), not the `status-*` tokens — those are\n * reserved for good/warning/serious/critical severity (see `dev/skills` dataviz guidance) and workflow status is\n * an identity dimension, not a severity one.\n */\nexport interface StatusVisual {\n /** Fill class for the node/bar body. */\n fill: string;\n /** Stroke class matching `fill`, for outlines and edge/line marks of the same status. */\n stroke: string;\n /** Solid swatch class for a small legend dot. */\n swatch: string;\n}\n\nconst STATUS_VISUALS: Record<(typeof WORK_ITEM_STATUSES)[number], StatusVisual> = {\n BACKLOG: { fill: 'fill-chart-6', stroke: 'stroke-chart-6', swatch: 'bg-chart-6' },\n TODO: { fill: 'fill-chart-1', stroke: 'stroke-chart-1', swatch: 'bg-chart-1' },\n IN_PROGRESS: { fill: 'fill-chart-4', stroke: 'stroke-chart-4', swatch: 'bg-chart-4' },\n IN_REVIEW: { fill: 'fill-chart-3', stroke: 'stroke-chart-3', swatch: 'bg-chart-3' },\n TESTING: { fill: 'fill-chart-9', stroke: 'stroke-chart-9', swatch: 'bg-chart-9' },\n DONE: { fill: 'fill-chart-2', stroke: 'stroke-chart-2', swatch: 'bg-chart-2' },\n CANCELLED: { fill: 'fill-chart-10', stroke: 'stroke-chart-10', swatch: 'bg-chart-10' },\n};\n\n/** A status the API returns that this map doesn't know yet (see WorkItemStatusCount's own doc comment on the\n * same risk) falls back to this rather than throwing or rendering unstyled. */\nconst FALLBACK_VISUAL: StatusVisual = {\n fill: 'fill-chart-12',\n stroke: 'stroke-chart-12',\n swatch: 'bg-chart-12',\n};\n\nexport function statusVisual(status: string): StatusVisual {\n return (STATUS_VISUALS as Record<string, StatusVisual>)[status] ?? FALLBACK_VISUAL;\n}\n\n/** Every status with an assigned visual, in the fixed legend order. */\nexport const STATUS_LEGEND_ORDER: readonly string[] = WORK_ITEM_STATUSES;\n"],"mappings":";;AAyBA,IAAM,IAA4E;CAChF,SAAS;EAAE,MAAM;EAAgB,QAAQ;EAAkB,QAAQ;EAAc;CACjF,MAAM;EAAE,MAAM;EAAgB,QAAQ;EAAkB,QAAQ;EAAc;CAC9E,aAAa;EAAE,MAAM;EAAgB,QAAQ;EAAkB,QAAQ;EAAc;CACrF,WAAW;EAAE,MAAM;EAAgB,QAAQ;EAAkB,QAAQ;EAAc;CACnF,SAAS;EAAE,MAAM;EAAgB,QAAQ;EAAkB,QAAQ;EAAc;CACjF,MAAM;EAAE,MAAM;EAAgB,QAAQ;EAAkB,QAAQ;EAAc;CAC9E,WAAW;EAAE,MAAM;EAAiB,QAAQ;EAAmB,QAAQ;EAAe;CACvF,EAIK,IAAgC;CACpC,MAAM;CACN,QAAQ;CACR,QAAQ;CACT;AAED,SAAgB,EAAa,GAA8B;AACzD,QAAQ,EAAgD,MAAW;;AAIrE,IAAa,IAAyC"}
1
+ {"version":3,"file":"statusColor.js","names":[],"sources":["../../../src/planmagnet/utils/statusColor.ts"],"sourcesContent":["import { WORK_ITEM_STATUSES } from '../controls/WorkItemControls';\n\n/**\n * One fixed chart color per WorkItemStatus (PDM-27 dependency graph, ANR-24 Gantt): both visualizations color by\n * status, so they share this single mapping rather than each re-deriving one. The order is FIXED and never\n * re-assigned from whichever statuses happen to appear in a given project/graph — a categorical channel keeps the\n * same color for the same identity everywhere it shows up.\n *\n * Values are CSS custom-property references to fe-libs' chart token ramp (`var(--color-chart-N)`), applied through\n * inline `style` (`fill`, `stroke`, `backgroundColor`) — NOT Tailwind utilities. The host apps' Tailwind themes do\n * not register `--color-chart-*`, so chart utility classes are never generated: measured in prod 2026-09-30, no\n * such rule existed in the served CSS and every chart mark rendered black. The token variables themselves are\n * always defined (fe-libs tokens.css), so referencing them directly works in every host.\n *\n * Colors come from fe-libs' chart token ramp (`chart-1`..`chart-12`), not the `status-*` tokens — those are\n * reserved for good/warning/serious/critical severity (see `dev/skills` dataviz guidance) and workflow status is\n * an identity dimension, not a severity one.\n */\nexport interface StatusVisual {\n /** The status's chart color as a CSS value (`var(--color-chart-N)`) — use as an SVG `fill`/`stroke` or a legend\n * swatch's `backgroundColor` through inline style. */\n color: string;\n}\n\nconst STATUS_VISUALS: Record<(typeof WORK_ITEM_STATUSES)[number], StatusVisual> = {\n BACKLOG: { color: 'var(--color-chart-6)' },\n TODO: { color: 'var(--color-chart-1)' },\n IN_PROGRESS: { color: 'var(--color-chart-4)' },\n IN_REVIEW: { color: 'var(--color-chart-3)' },\n TESTING: { color: 'var(--color-chart-9)' },\n DONE: { color: 'var(--color-chart-2)' },\n CANCELLED: { color: 'var(--color-chart-10)' },\n};\n\n/** A status the API returns that this map doesn't know yet (see WorkItemStatusCount's own doc comment on the\n * same risk) falls back to this rather than throwing or rendering unstyled. */\nconst FALLBACK_VISUAL: StatusVisual = { color: 'var(--color-chart-12)' };\n\nexport function statusVisual(status: string): StatusVisual {\n return (STATUS_VISUALS as Record<string, StatusVisual>)[status] ?? FALLBACK_VISUAL;\n}\n\n/** Every status with an assigned visual, in the fixed legend order. */\nexport const STATUS_LEGEND_ORDER: readonly string[] = WORK_ITEM_STATUSES;\n"],"mappings":";;AAwBA,IAAM,IAA4E;CAChF,SAAS,EAAE,OAAO,wBAAwB;CAC1C,MAAM,EAAE,OAAO,wBAAwB;CACvC,aAAa,EAAE,OAAO,wBAAwB;CAC9C,WAAW,EAAE,OAAO,wBAAwB;CAC5C,SAAS,EAAE,OAAO,wBAAwB;CAC1C,MAAM,EAAE,OAAO,wBAAwB;CACvC,WAAW,EAAE,OAAO,yBAAyB;CAC9C,EAIK,IAAgC,EAAE,OAAO,yBAAyB;AAExE,SAAgB,EAAa,GAA8B;AACzD,QAAQ,EAAgD,MAAW;;AAIrE,IAAa,IAAyC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-planmagnet",
3
- "version": "2026.930.22",
3
+ "version": "2026.930.24",
4
4
  "description": "Burdenoff PlanMagnet Microfrontend",
5
5
  "type": "module",
6
6
  "files": [