@burdenoff/microfe-planmagnet 2026.927.3 → 2026.927.5

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 (166) hide show
  1. package/dist/generated/global-types.d.ts +9 -1
  2. package/dist/generated/global-types.d.ts.map +1 -1
  3. package/dist/generated/wspace-operations.d.ts +0 -12
  4. package/dist/generated/wspace-operations.d.ts.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  6. package/dist/planmagnet/PlanMagnetRoutes.js +14 -10
  7. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  8. package/dist/planmagnet/admin/PlanMagnetAdminRoot.d.ts.map +1 -1
  9. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js +149 -127
  10. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js.map +1 -1
  11. package/dist/planmagnet/controls/FeatureControls.d.ts +26 -0
  12. package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -0
  13. package/dist/planmagnet/controls/FeatureControls.js +321 -0
  14. package/dist/planmagnet/controls/FeatureControls.js.map +1 -0
  15. package/dist/planmagnet/controls/FeatureTriage.d.ts +21 -0
  16. package/dist/planmagnet/controls/FeatureTriage.d.ts.map +1 -0
  17. package/dist/planmagnet/controls/FeatureTriage.js +398 -0
  18. package/dist/planmagnet/controls/FeatureTriage.js.map +1 -0
  19. package/dist/planmagnet/controls/MilestoneControls.d.ts +17 -0
  20. package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -0
  21. package/dist/planmagnet/controls/MilestoneControls.js +203 -0
  22. package/dist/planmagnet/controls/MilestoneControls.js.map +1 -0
  23. package/dist/planmagnet/controls/ObjectiveControls.d.ts +31 -0
  24. package/dist/planmagnet/controls/ObjectiveControls.d.ts.map +1 -0
  25. package/dist/planmagnet/controls/ObjectiveControls.js +296 -0
  26. package/dist/planmagnet/controls/ObjectiveControls.js.map +1 -0
  27. package/dist/planmagnet/controls/Pickers.d.ts +26 -0
  28. package/dist/planmagnet/controls/Pickers.d.ts.map +1 -0
  29. package/dist/planmagnet/controls/Pickers.js +171 -0
  30. package/dist/planmagnet/controls/Pickers.js.map +1 -0
  31. package/dist/planmagnet/controls/ProjectControls.d.ts.map +1 -1
  32. package/dist/planmagnet/controls/ProjectControls.js +47 -44
  33. package/dist/planmagnet/controls/ProjectControls.js.map +1 -1
  34. package/dist/planmagnet/controls/ReleaseControls.d.ts +14 -0
  35. package/dist/planmagnet/controls/ReleaseControls.d.ts.map +1 -0
  36. package/dist/planmagnet/controls/ReleaseControls.js +111 -0
  37. package/dist/planmagnet/controls/ReleaseControls.js.map +1 -0
  38. package/dist/planmagnet/controls/WorkItemControls.d.ts.map +1 -1
  39. package/dist/planmagnet/controls/WorkItemControls.js +68 -65
  40. package/dist/planmagnet/controls/WorkItemControls.js.map +1 -1
  41. package/dist/planmagnet/hooks/useCounts.d.ts +39 -0
  42. package/dist/planmagnet/hooks/useCounts.d.ts.map +1 -0
  43. package/dist/planmagnet/hooks/useCounts.js +209 -0
  44. package/dist/planmagnet/hooks/useCounts.js.map +1 -0
  45. package/dist/planmagnet/import/exportColumns.d.ts +52 -0
  46. package/dist/planmagnet/import/exportColumns.d.ts.map +1 -0
  47. package/dist/planmagnet/import/exportColumns.js +81 -0
  48. package/dist/planmagnet/import/exportColumns.js.map +1 -0
  49. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
  50. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +255 -242
  51. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  52. package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -1
  53. package/dist/planmagnet/pages/DashboardDashboardPagePage.js +199 -160
  54. package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -1
  55. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  56. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +190 -153
  57. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  58. package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.d.ts.map +1 -1
  59. package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js +63 -56
  60. package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js.map +1 -1
  61. package/dist/planmagnet/pages/GoalsGoalsPagePage.d.ts.map +1 -1
  62. package/dist/planmagnet/pages/GoalsGoalsPagePage.js +2 -7
  63. package/dist/planmagnet/pages/GoalsGoalsPagePage.js.map +1 -1
  64. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
  65. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +229 -214
  66. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
  67. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  68. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +145 -114
  69. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  70. package/dist/planmagnet/pages/ProductsEditReleasePagePage.d.ts.map +1 -1
  71. package/dist/planmagnet/pages/ProductsEditReleasePagePage.js +134 -105
  72. package/dist/planmagnet/pages/ProductsEditReleasePagePage.js.map +1 -1
  73. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.d.ts.map +1 -1
  74. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js +97 -117
  75. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js.map +1 -1
  76. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  77. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +169 -110
  78. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  79. package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
  80. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +117 -103
  81. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
  82. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
  83. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +119 -94
  84. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  85. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  86. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +4 -8
  87. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  88. package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
  89. package/dist/planmagnet/pages/ProductsProductsPagePage.js +85 -90
  90. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  91. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
  92. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +118 -107
  93. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
  94. package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
  95. package/dist/planmagnet/pages/ProductsReleasesPagePage.js +4 -2
  96. package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
  97. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
  98. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +48 -71
  99. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
  100. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
  101. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +17 -13
  102. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
  103. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts.map +1 -1
  104. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js +1 -1
  105. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js.map +1 -1
  106. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts.map +1 -1
  107. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +55 -63
  108. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -1
  109. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  110. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +11 -8
  111. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  112. package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -1
  113. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +1 -1
  114. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -1
  115. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  116. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +152 -139
  117. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  118. package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
  119. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +75 -74
  120. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
  121. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts +7 -0
  122. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
  123. package/dist/planmagnet/pages/ReportsReportsPagePage.js +39 -201
  124. package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -1
  125. package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
  126. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +118 -99
  127. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
  128. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +123 -128
  129. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
  130. package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
  131. package/dist/planmagnet/pages/TasksEditTaskPagePage.js +139 -107
  132. package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
  133. package/dist/planmagnet/pages/TasksImportPagePage.d.ts +2 -0
  134. package/dist/planmagnet/pages/TasksImportPagePage.d.ts.map +1 -0
  135. package/dist/planmagnet/pages/TasksImportPagePage.js +309 -0
  136. package/dist/planmagnet/pages/TasksImportPagePage.js.map +1 -0
  137. package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
  138. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +173 -126
  139. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
  140. package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
  141. package/dist/planmagnet/pages/TasksTasksPagePage.js +111 -104
  142. package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
  143. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  144. package/dist/planmagnet/pages/TeamTeamPagePage.js +99 -177
  145. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  146. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts.map +1 -1
  147. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -4
  148. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js.map +1 -1
  149. package/dist/planmagnet/releases/ReleaseWorkItemsCard.d.ts.map +1 -1
  150. package/dist/planmagnet/releases/ReleaseWorkItemsCard.js +10 -4
  151. package/dist/planmagnet/releases/ReleaseWorkItemsCard.js.map +1 -1
  152. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
  153. package/dist/planmagnet/roadmaps/RoadmapControls.js +150 -137
  154. package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
  155. package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
  156. package/dist/planmagnet/sprints/SprintControls.js +32 -30
  157. package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
  158. package/dist/planmagnet/utils/mutationResult.d.ts +10 -0
  159. package/dist/planmagnet/utils/mutationResult.d.ts.map +1 -1
  160. package/dist/planmagnet/utils/mutationResult.js +2 -1
  161. package/dist/planmagnet/utils/mutationResult.js.map +1 -1
  162. package/dist/planmagnet/utils/rowNavigation.d.ts +2 -0
  163. package/dist/planmagnet/utils/rowNavigation.d.ts.map +1 -1
  164. package/dist/planmagnet/utils/rowNavigation.js +9 -8
  165. package/dist/planmagnet/utils/rowNavigation.js.map +1 -1
  166. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"GoalsObjectiveDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/GoalsObjectiveDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { throwOnError } from '../utils/mutationResult';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Button,\n Input,\n Badge,\n} from '@burdenoff/fe-libs/ui';\nimport { Flag, Calendar, User, Plus, Trash2, ArrowLeft } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { PersonName } from '../people/People';\n\n// BOFF-4441: OKR detail — real Objective + KeyResult data via GraphQL.\n// `progress` (on both the objective and each key result) is computed live\n// server-side from current/target values; this page only ever reads it,\n// never derives it client-side. `linkedWorkItemRollup` surfaces a real,\n// live-computed completion ratio over WorkItems linked to a key result --\n// informational only, it does not drive `currentValue` (see the backend\n// module header for the explicit deferral of that automation).\nconst GET_OBJECTIVE = gql`\n query GetObjectiveDetail($id: ID!) {\n planmagnetObjective(id: $id) {\n id\n workspaceId\n title\n description\n ownerId\n periodLabel\n startDate\n endDate\n status\n progress\n createdAt\n keyResults {\n id\n title\n description\n metricType\n startValue\n targetValue\n currentValue\n unit\n status\n ownerId\n progress\n linkedWorkItemRollup {\n totalLinked\n doneCount\n completionRatio\n }\n }\n }\n }\n`;\n\nconst CREATE_KEY_RESULT = gql`\n mutation CreateKeyResult($input: PlanmagnetCreateKeyResultInput!) {\n planmagnetCreateKeyResult(input: $input) {\n id\n }\n }\n`;\n\nconst UPDATE_KEY_RESULT = gql`\n mutation UpdateKeyResult($id: ID!, $input: PlanmagnetUpdateKeyResultInput!) {\n planmagnetUpdateKeyResult(id: $id, input: $input) {\n id\n currentValue\n status\n }\n }\n`;\n\nconst DELETE_KEY_RESULT = gql`\n mutation DeleteKeyResult($id: ID!) {\n planmagnetDeleteKeyResult(id: $id)\n }\n`;\n\nconst DELETE_OBJECTIVE = gql`\n mutation DeleteObjective($id: ID!) {\n planmagnetDeleteObjective(id: $id)\n }\n`;\n\nconst objectiveStatusColor: Record<string, string> = {\n DRAFT: 'bg-gray-100 text-gray-800',\n ACTIVE: 'bg-blue-100 text-blue-800',\n COMPLETED: 'bg-green-100 text-green-800',\n CANCELLED: 'bg-red-100 text-red-800',\n};\n\nconst krStatusColor: Record<string, string> = {\n NOT_STARTED: 'bg-gray-100 text-gray-800',\n IN_PROGRESS: 'bg-blue-100 text-blue-800',\n AT_RISK: 'bg-orange-100 text-orange-800',\n COMPLETED: 'bg-green-100 text-green-800',\n};\n\nfunction ProgressBar({ progress }: { progress: number }) {\n const pct = Math.max(0, Math.min(100, Math.round(progress)));\n return (\n <div className=\"h-2 w-full rounded-full bg-muted overflow-hidden\">\n <div className=\"h-full rounded-full bg-primary transition-all\" style={{ width: `${pct}%` }} />\n </div>\n );\n}\n\ninterface KeyResultNode {\n id: string;\n title: string;\n description: string | null;\n metricType: 'NUMERIC' | 'PERCENTAGE' | 'BOOLEAN';\n startValue: number;\n targetValue: number;\n currentValue: number;\n unit: string | null;\n status: string;\n ownerId: string | null;\n progress: number;\n linkedWorkItemRollup: { totalLinked: number; doneCount: number; completionRatio: number };\n}\n\nfunction KeyResultRow({\n kr,\n onUpdated,\n onDeleted,\n}: {\n kr: KeyResultNode;\n onUpdated: () => void;\n onDeleted: () => void;\n}) {\n const [draftValue, setDraftValue] = useState(String(kr.currentValue));\n const [updateKeyResult, { loading: updating }] = useMutation(UPDATE_KEY_RESULT);\n const [deleteKeyResult, { loading: deleting }] = useMutation(DELETE_KEY_RESULT);\n // fe-libs sets mutate errorPolicy 'all': a refused mutation RESOLVES with\n // { error }. These handlers used to carry on as if it had worked.\n const [actionError, setActionError] = useState<string | null>(null);\n\n const handleSaveProgress = async () => {\n const parsed = kr.metricType === 'BOOLEAN' ? (draftValue === '1' ? 1 : 0) : Number(draftValue);\n if (Number.isNaN(parsed)) return;\n setActionError(null);\n try {\n await throwOnError(\n updateKeyResult({ variables: { id: kr.id, input: { currentValue: parsed } } })\n );\n onUpdated();\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'The progress could not be saved.');\n }\n };\n\n const handleDelete = async () => {\n setActionError(null);\n try {\n await throwOnError(deleteKeyResult({ variables: { id: kr.id } }));\n onDeleted();\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'The key result could not be deleted.');\n }\n };\n\n return (\n <div className=\"rounded-md border border-input p-4 space-y-3\">\n {actionError && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {actionError}\n </p>\n )}\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium\">{kr.title}</p>\n {kr.description && (\n <p className=\"text-xs text-muted-foreground mt-0.5\">{kr.description}</p>\n )}\n </div>\n <div className=\"flex items-center gap-2 shrink-0\">\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${krStatusColor[kr.status] ?? ''}`}\n >\n {kr.status.toLowerCase().replace('_', ' ')}\n </span>\n <Button\n size=\"icon\"\n variant=\"ghost\"\n disabled={deleting}\n onClick={handleDelete}\n aria-label={`Delete ${kr.title}`}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n <ProgressBar progress={kr.progress} />\n <div className=\"flex items-center justify-between text-xs text-muted-foreground\">\n <span>\n {kr.metricType === 'BOOLEAN'\n ? kr.currentValue > 0\n ? 'Done'\n : 'Not done'\n : `${kr.currentValue}${kr.unit ? ` ${kr.unit}` : ''} of ${kr.targetValue}${kr.unit ? ` ${kr.unit}` : ''}`}\n </span>\n <span>{Math.round(kr.progress)}%</span>\n </div>\n {kr.linkedWorkItemRollup.totalLinked > 0 && (\n <p className=\"text-xs text-muted-foreground\">\n {kr.linkedWorkItemRollup.doneCount}/{kr.linkedWorkItemRollup.totalLinked} linked work\n items done ({Math.round(kr.linkedWorkItemRollup.completionRatio * 100)}%) &mdash;\n informational, update progress above manually\n </p>\n )}\n <div className=\"flex items-center gap-2\">\n {kr.metricType === 'BOOLEAN' ? (\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={draftValue}\n onChange={(e) => setDraftValue(e.target.value)}\n >\n <option value=\"0\">Not done</option>\n <option value=\"1\">Done</option>\n </select>\n ) : (\n <Input\n type=\"number\"\n value={draftValue}\n onChange={(e) => setDraftValue(e.target.value)}\n className=\"w-32\"\n />\n )}\n <Button size=\"sm\" variant=\"outline\" disabled={updating} onClick={handleSaveProgress}>\n {updating ? 'Saving...' : 'Update progress'}\n </Button>\n </div>\n </div>\n );\n}\n\nexport function GoalsObjectiveDetailPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const objectiveId = new URLSearchParams(window.location.search).get('objectiveId') ?? '';\n\n const [isAddingKr, setIsAddingKr] = useState(false);\n const [krForm, setKrForm] = useState({\n title: '',\n metricType: 'NUMERIC',\n targetValue: '100',\n unit: '',\n });\n\n const { data, loading, error, refetch } = useQuery<{\n planmagnetObjective: {\n id: string;\n title: string;\n description: string | null;\n ownerId: string;\n periodLabel: string;\n startDate: string | null;\n endDate: string | null;\n status: string;\n progress: number;\n createdAt: string;\n keyResults: KeyResultNode[];\n } | null;\n }>(GET_OBJECTIVE, {\n variables: { id: objectiveId },\n skip: !workspaceId || !objectiveId,\n });\n\n const [createKeyResult, { loading: creatingKr }] = useMutation(CREATE_KEY_RESULT);\n const [deleteObjective, { loading: deletingObjective }] = useMutation(DELETE_OBJECTIVE);\n const [actionError, setActionError] = useState<string | null>(null);\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n }\n if (error) return <div className=\"p-6 text-red-500\">Error: {error.message}</div>;\n\n const objective = data?.planmagnetObjective;\n if (!objective) return <div className=\"p-6 text-muted-foreground\">Objective not found.</div>;\n\n const handleAddKeyResult = async () => {\n if (!krForm.title.trim() || !objectiveId) return;\n const target = Number(krForm.metricType === 'BOOLEAN' ? 1 : krForm.targetValue);\n if (Number.isNaN(target)) return;\n setActionError(null);\n try {\n await throwOnError(\n createKeyResult({\n variables: {\n input: {\n objectiveId,\n title: krForm.title.trim(),\n metricType: krForm.metricType,\n targetValue: target,\n unit: krForm.unit.trim() || undefined,\n },\n },\n })\n );\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'The key result could not be added.');\n return;\n }\n setKrForm({ title: '', metricType: 'NUMERIC', targetValue: '100', unit: '' });\n setIsAddingKr(false);\n void refetch();\n };\n\n const handleDeleteObjective = async () => {\n setActionError(null);\n try {\n await throwOnError(deleteObjective({ variables: { id: objectiveId } }));\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'The objective could not be deleted.');\n return;\n }\n navigate('/goals/goals-page');\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n {actionError && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {actionError}\n </p>\n )}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigate('/goals/goals-page')}>\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <Flag className=\"h-6 w-6 text-primary\" />\n <div>\n <h1 className=\"text-2xl font-semibold\">{objective.title}</h1>\n <p className=\"text-sm text-muted-foreground\">{objective.periodLabel}</p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <span\n className={`text-xs px-3 py-1 rounded-full capitalize ${objectiveStatusColor[objective.status] ?? 'bg-gray-100'}`}\n >\n {objective.status.toLowerCase()}\n </span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={deletingObjective}\n onClick={handleDeleteObjective}\n >\n <Trash2 className=\"mr-1 h-4 w-4\" />\n Delete\n </Button>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle>Overview</CardTitle>\n <Badge variant=\"outline\">{Math.round(objective.progress)}% complete</Badge>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm\">{objective.description ?? 'No description provided.'}</p>\n <ProgressBar progress={objective.progress} />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle>Key Results</CardTitle>\n {!isAddingKr && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => setIsAddingKr(true)}>\n <Plus className=\"mr-1 h-4 w-4\" />\n Add Key Result\n </Button>\n )}\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {isAddingKr && (\n <div className=\"space-y-3 rounded-md border border-input p-3\">\n <Input\n placeholder=\"Key result title, e.g. Sign 10 new enterprise customers\"\n value={krForm.title}\n onChange={(e) => setKrForm({ ...krForm, title: e.target.value })}\n />\n <div className=\"grid grid-cols-3 gap-3\">\n <select\n className=\"rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={krForm.metricType}\n onChange={(e) => setKrForm({ ...krForm, metricType: e.target.value })}\n >\n <option value=\"NUMERIC\">Numeric</option>\n <option value=\"PERCENTAGE\">Percentage</option>\n <option value=\"BOOLEAN\">Yes / No</option>\n </select>\n {krForm.metricType !== 'BOOLEAN' && (\n <Input\n type=\"number\"\n placeholder=\"Target value\"\n value={krForm.targetValue}\n onChange={(e) => setKrForm({ ...krForm, targetValue: e.target.value })}\n />\n )}\n {krForm.metricType === 'NUMERIC' && (\n <Input\n placeholder=\"Unit (optional)\"\n value={krForm.unit}\n onChange={(e) => setKrForm({ ...krForm, unit: e.target.value })}\n />\n )}\n </div>\n <div className=\"flex gap-2 justify-end\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setIsAddingKr(false);\n setKrForm({\n title: '',\n metricType: 'NUMERIC',\n targetValue: '100',\n unit: '',\n });\n }}\n >\n Cancel\n </Button>\n <Button\n size=\"sm\"\n disabled={creatingKr || !krForm.title.trim()}\n onClick={handleAddKeyResult}\n >\n {creatingKr ? 'Adding...' : 'Add Key Result'}\n </Button>\n </div>\n </div>\n )}\n {objective.keyResults.length === 0 && !isAddingKr && (\n <p className=\"text-sm text-muted-foreground\">\n No key results yet. Add one to make this objective measurable.\n </p>\n )}\n {objective.keyResults.map((kr) => (\n <KeyResultRow\n key={kr.id}\n kr={kr}\n onUpdated={() => void refetch()}\n onDeleted={() => void refetch()}\n />\n ))}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex items-center gap-2\">\n <User className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Owner:</span>\n <span className=\"font-medium\">\n <PersonName id={objective.ownerId} />\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Period:</span>\n <span className=\"font-medium\">{objective.periodLabel}</span>\n </div>\n {objective.endDate && (\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Target end:</span>\n <span className=\"font-medium\">\n {new Date(objective.endDate).toLocaleDateString()}\n </span>\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Created:</span>\n <span className=\"font-medium\">\n {objective.createdAt ? new Date(objective.createdAt).toLocaleDateString() : '—'}\n </span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"GoalsObjectiveDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/GoalsObjectiveDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { throwOnError, LEAVES_PAGE_CONTEXT } from '../utils/mutationResult';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Button,\n Input,\n Badge,\n} from '@burdenoff/fe-libs/ui';\nimport { Flag, Calendar, User, Plus, Trash2, ArrowLeft } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { PersonName } from '../people/People';\nimport { ConfirmButton } from '../controls/ConfirmButton';\nimport { KeyResultWorkItems, ObjectiveHeaderControls } from '../controls/ObjectiveControls';\n\n// BOFF-4441: OKR detail — real Objective + KeyResult data via GraphQL.\n// `progress` (on both the objective and each key result) is computed live\n// server-side from current/target values; this page only ever reads it,\n// never derives it client-side. `linkedWorkItemRollup` surfaces a real,\n// live-computed completion ratio over WorkItems linked to a key result --\n// informational only, it does not drive `currentValue` (see the backend\n// module header for the explicit deferral of that automation).\nconst GET_OBJECTIVE = gql`\n query GetObjectiveDetail($id: ID!) {\n planmagnetObjective(id: $id) {\n id\n workspaceId\n title\n description\n ownerId\n periodLabel\n startDate\n endDate\n status\n progress\n createdAt\n keyResults {\n id\n title\n description\n metricType\n startValue\n targetValue\n currentValue\n unit\n status\n ownerId\n progress\n linkedWorkItemRollup {\n totalLinked\n doneCount\n completionRatio\n }\n linkedWorkItems {\n id\n key\n title\n status\n }\n }\n }\n }\n`;\n\nconst CREATE_KEY_RESULT = gql`\n mutation CreateKeyResult($input: PlanmagnetCreateKeyResultInput!) {\n planmagnetCreateKeyResult(input: $input) {\n id\n }\n }\n`;\n\nconst UPDATE_KEY_RESULT = gql`\n mutation UpdateKeyResult($id: ID!, $input: PlanmagnetUpdateKeyResultInput!) {\n planmagnetUpdateKeyResult(id: $id, input: $input) {\n id\n currentValue\n status\n }\n }\n`;\n\nconst DELETE_KEY_RESULT = gql`\n mutation DeleteKeyResult($id: ID!) {\n planmagnetDeleteKeyResult(id: $id)\n }\n`;\n\nconst DELETE_OBJECTIVE = gql`\n mutation DeleteObjective($id: ID!) {\n planmagnetDeleteObjective(id: $id)\n }\n`;\n\nconst krStatusColor: Record<string, string> = {\n NOT_STARTED: 'bg-gray-100 text-gray-800',\n IN_PROGRESS: 'bg-blue-100 text-blue-800',\n AT_RISK: 'bg-orange-100 text-orange-800',\n COMPLETED: 'bg-green-100 text-green-800',\n};\n\nfunction ProgressBar({ progress }: { progress: number }) {\n const pct = Math.max(0, Math.min(100, Math.round(progress)));\n return (\n <div className=\"h-2 w-full rounded-full bg-muted overflow-hidden\">\n <div className=\"h-full rounded-full bg-primary transition-all\" style={{ width: `${pct}%` }} />\n </div>\n );\n}\n\ninterface KeyResultNode {\n id: string;\n title: string;\n description: string | null;\n metricType: 'NUMERIC' | 'PERCENTAGE' | 'BOOLEAN';\n startValue: number;\n targetValue: number;\n currentValue: number;\n unit: string | null;\n status: string;\n ownerId: string | null;\n progress: number;\n linkedWorkItemRollup: { totalLinked: number; doneCount: number; completionRatio: number };\n linkedWorkItems: { id: string; key: string; title: string; status: string }[];\n}\n\nfunction KeyResultRow({\n kr,\n onUpdated,\n onDeleted,\n}: {\n kr: KeyResultNode;\n onUpdated: () => void;\n onDeleted: () => void;\n}) {\n const [draftValue, setDraftValue] = useState(String(kr.currentValue));\n const [updateKeyResult, { loading: updating }] = useMutation(UPDATE_KEY_RESULT);\n const [deleteKeyResult, { loading: deleting }] = useMutation(DELETE_KEY_RESULT);\n // fe-libs sets mutate errorPolicy 'all': a refused mutation RESOLVES with\n // { error }. These handlers used to carry on as if it had worked.\n const [actionError, setActionError] = useState<string | null>(null);\n\n const handleSaveProgress = async () => {\n const parsed = kr.metricType === 'BOOLEAN' ? (draftValue === '1' ? 1 : 0) : Number(draftValue);\n if (Number.isNaN(parsed)) return;\n setActionError(null);\n try {\n await throwOnError(\n updateKeyResult({ variables: { id: kr.id, input: { currentValue: parsed } } })\n );\n onUpdated();\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'The progress could not be saved.');\n }\n };\n\n const handleDelete = async () => {\n setActionError(null);\n try {\n await throwOnError(deleteKeyResult({ variables: { id: kr.id } }));\n onDeleted();\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'The key result could not be deleted.');\n }\n };\n\n return (\n <div className=\"rounded-md border border-input p-4 space-y-3\">\n {actionError && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {actionError}\n </p>\n )}\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium\">{kr.title}</p>\n {kr.description && (\n <p className=\"text-xs text-muted-foreground mt-0.5\">{kr.description}</p>\n )}\n </div>\n <div className=\"flex items-center gap-2 shrink-0\">\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${krStatusColor[kr.status] ?? ''}`}\n >\n {kr.status.toLowerCase().replace('_', ' ')}\n </span>\n {/* Deleting used to happen on the first click, with no confirmation. */}\n <ConfirmButton\n label=\"\"\n ariaLabel={`Delete ${kr.title}`}\n variant=\"outline\"\n icon={<Trash2 className=\"h-4 w-4\" />}\n title={`Delete \"${kr.title}\"?`}\n description=\"The key result and its progress are deleted. This cannot be undone here.\"\n confirmLabel=\"Delete key result\"\n destructive\n disabled={deleting}\n onConfirm={() => void handleDelete()}\n />\n </div>\n </div>\n <ProgressBar progress={kr.progress} />\n <div className=\"flex items-center justify-between text-xs text-muted-foreground\">\n <span>\n {kr.metricType === 'BOOLEAN'\n ? kr.currentValue > 0\n ? 'Done'\n : 'Not done'\n : `${kr.currentValue}${kr.unit ? ` ${kr.unit}` : ''} of ${kr.targetValue}${kr.unit ? ` ${kr.unit}` : ''}`}\n </span>\n <span>{Math.round(kr.progress)}%</span>\n </div>\n {kr.linkedWorkItemRollup.totalLinked > 0 && (\n <p className=\"text-xs text-muted-foreground\">\n {kr.linkedWorkItemRollup.doneCount}/{kr.linkedWorkItemRollup.totalLinked} linked work\n items done ({Math.round(kr.linkedWorkItemRollup.completionRatio * 100)}%) &mdash;\n informational, update progress above manually\n </p>\n )}\n {/* BOFF-7956: a key result could not be linked to work items, so the rollup above never appeared. */}\n <KeyResultWorkItems\n keyResultId={kr.id}\n keyResultTitle={kr.title}\n linked={kr.linkedWorkItems ?? []}\n />\n <div className=\"flex items-center gap-2\">\n {kr.metricType === 'BOOLEAN' ? (\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={draftValue}\n onChange={(e) => setDraftValue(e.target.value)}\n >\n <option value=\"0\">Not done</option>\n <option value=\"1\">Done</option>\n </select>\n ) : (\n <Input\n type=\"number\"\n value={draftValue}\n onChange={(e) => setDraftValue(e.target.value)}\n className=\"w-32\"\n />\n )}\n <Button size=\"sm\" variant=\"outline\" disabled={updating} onClick={handleSaveProgress}>\n {updating ? 'Saving...' : 'Update progress'}\n </Button>\n </div>\n </div>\n );\n}\n\nexport function GoalsObjectiveDetailPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const objectiveId = new URLSearchParams(window.location.search).get('objectiveId') ?? '';\n\n const [isAddingKr, setIsAddingKr] = useState(false);\n const [krForm, setKrForm] = useState({\n title: '',\n metricType: 'NUMERIC',\n targetValue: '100',\n unit: '',\n });\n\n const { data, loading, error, refetch } = useQuery<{\n planmagnetObjective: {\n id: string;\n title: string;\n description: string | null;\n ownerId: string;\n periodLabel: string;\n startDate: string | null;\n endDate: string | null;\n status: string;\n progress: number;\n createdAt: string;\n keyResults: KeyResultNode[];\n } | null;\n }>(GET_OBJECTIVE, {\n variables: { id: objectiveId },\n skip: !workspaceId || !objectiveId,\n });\n\n const [createKeyResult, { loading: creatingKr }] = useMutation(CREATE_KEY_RESULT);\n const [deleteObjective, { loading: deletingObjective }] = useMutation(DELETE_OBJECTIVE, {\n context: LEAVES_PAGE_CONTEXT,\n });\n const [actionError, setActionError] = useState<string | null>(null);\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n }\n if (error) return <div className=\"p-6 text-red-500\">Error: {error.message}</div>;\n\n const objective = data?.planmagnetObjective;\n if (!objective) return <div className=\"p-6 text-muted-foreground\">Objective not found.</div>;\n\n const handleAddKeyResult = async () => {\n if (!krForm.title.trim() || !objectiveId) return;\n const target = Number(krForm.metricType === 'BOOLEAN' ? 1 : krForm.targetValue);\n if (Number.isNaN(target)) return;\n setActionError(null);\n try {\n await throwOnError(\n createKeyResult({\n variables: {\n input: {\n objectiveId,\n title: krForm.title.trim(),\n metricType: krForm.metricType,\n targetValue: target,\n unit: krForm.unit.trim() || undefined,\n },\n },\n })\n );\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'The key result could not be added.');\n return;\n }\n setKrForm({ title: '', metricType: 'NUMERIC', targetValue: '100', unit: '' });\n setIsAddingKr(false);\n void refetch();\n };\n\n const handleDeleteObjective = async () => {\n setActionError(null);\n try {\n await throwOnError(deleteObjective({ variables: { id: objectiveId } }));\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'The objective could not be deleted.');\n return;\n }\n navigate('/goals/goals-page');\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n {actionError && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {actionError}\n </p>\n )}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigate('/goals/goals-page')}>\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <Flag className=\"h-6 w-6 text-primary\" />\n <div>\n <h1 className=\"text-2xl font-semibold\">{objective.title}</h1>\n <p className=\"text-sm text-muted-foreground\">{objective.periodLabel}</p>\n </div>\n </div>\n <div className=\"flex items-start gap-2\">\n {/* Status and Edit (BOFF-7956): an objective stayed ACTIVE forever. Delete now asks first. */}\n <ObjectiveHeaderControls objective={objective} />\n <ConfirmButton\n label=\"Delete\"\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n title={`Delete \"${objective.title}\"?`}\n description=\"The objective and its key results are deleted. This cannot be undone here.\"\n confirmLabel=\"Delete objective\"\n destructive\n disabled={deletingObjective}\n onConfirm={() => void handleDeleteObjective()}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle>Overview</CardTitle>\n <Badge variant=\"outline\">{Math.round(objective.progress)}% complete</Badge>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm\">{objective.description ?? 'No description provided.'}</p>\n <ProgressBar progress={objective.progress} />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle>Key Results</CardTitle>\n {!isAddingKr && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => setIsAddingKr(true)}>\n <Plus className=\"mr-1 h-4 w-4\" />\n Add Key Result\n </Button>\n )}\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {isAddingKr && (\n <div className=\"space-y-3 rounded-md border border-input p-3\">\n <Input\n placeholder=\"Key result title, e.g. Sign 10 new enterprise customers\"\n value={krForm.title}\n onChange={(e) => setKrForm({ ...krForm, title: e.target.value })}\n />\n <div className=\"grid grid-cols-3 gap-3\">\n <select\n className=\"rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={krForm.metricType}\n onChange={(e) => setKrForm({ ...krForm, metricType: e.target.value })}\n >\n <option value=\"NUMERIC\">Numeric</option>\n <option value=\"PERCENTAGE\">Percentage</option>\n <option value=\"BOOLEAN\">Yes / No</option>\n </select>\n {krForm.metricType !== 'BOOLEAN' && (\n <Input\n type=\"number\"\n placeholder=\"Target value\"\n value={krForm.targetValue}\n onChange={(e) => setKrForm({ ...krForm, targetValue: e.target.value })}\n />\n )}\n {krForm.metricType === 'NUMERIC' && (\n <Input\n placeholder=\"Unit (optional)\"\n value={krForm.unit}\n onChange={(e) => setKrForm({ ...krForm, unit: e.target.value })}\n />\n )}\n </div>\n <div className=\"flex gap-2 justify-end\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setIsAddingKr(false);\n setKrForm({\n title: '',\n metricType: 'NUMERIC',\n targetValue: '100',\n unit: '',\n });\n }}\n >\n Cancel\n </Button>\n <Button\n size=\"sm\"\n disabled={creatingKr || !krForm.title.trim()}\n onClick={handleAddKeyResult}\n >\n {creatingKr ? 'Adding...' : 'Add Key Result'}\n </Button>\n </div>\n </div>\n )}\n {objective.keyResults.length === 0 && !isAddingKr && (\n <p className=\"text-sm text-muted-foreground\">\n No key results yet. Add one to make this objective measurable.\n </p>\n )}\n {objective.keyResults.map((kr) => (\n <KeyResultRow\n key={kr.id}\n kr={kr}\n onUpdated={() => void refetch()}\n onDeleted={() => void refetch()}\n />\n ))}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex items-center gap-2\">\n <User className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Owner:</span>\n <span className=\"font-medium\">\n <PersonName id={objective.ownerId} />\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Period:</span>\n <span className=\"font-medium\">{objective.periodLabel}</span>\n </div>\n {objective.endDate && (\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Target end:</span>\n <span className=\"font-medium\">\n {new Date(objective.endDate).toLocaleDateString()}\n </span>\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Created:</span>\n <span className=\"font-medium\">\n {objective.createdAt ? new Date(objective.createdAt).toLocaleDateString() : '—'}\n </span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsEditProductPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsEditProductPagePage.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,2BAA2B,4CAwN1C"}
1
+ {"version":3,"file":"ProductsEditProductPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsEditProductPagePage.tsx"],"names":[],"mappings":"AAoEA,wBAAgB,2BAA2B,4CA4P1C"}
@@ -1,15 +1,17 @@
1
1
  import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
2
  import { PersonName as n } from "../people/People.js";
3
- import { PlanningFocusBadge as r, PlanningFocusSelect as i } from "../roadmaps/planningFocus.js";
4
- import { useMemo as a, useState as o } from "react";
5
- import { ArrowLeft as s, Loader2 as c, Package as l, Save as u } from "lucide-react";
6
- import { useParams as d } from "react-router";
7
- import { gql as f } from "@apollo/client";
8
- import { useMutation as p, useQuery as m } from "@apollo/client/react";
9
- import { jsx as h, jsxs as g } from "react/jsx-runtime";
10
- import { Button as _, Card as v, CardContent as y, CardHeader as b, CardTitle as x, Input as S } from "@burdenoff/fe-libs/ui";
3
+ import { LEAVES_PAGE_CONTEXT as r, throwOnError as i } from "../utils/mutationResult.js";
4
+ import { ConfirmButton as a } from "../controls/ConfirmButton.js";
5
+ import { PlanningFocusBadge as o, PlanningFocusSelect as s } from "../roadmaps/planningFocus.js";
6
+ import { useMemo as c, useState as l } from "react";
7
+ import { ArrowLeft as u, Loader2 as d, Package as f, Save as p, Trash2 as m } from "lucide-react";
8
+ import { useParams as h } from "react-router";
9
+ import { gql as g } from "@apollo/client";
10
+ import { useMutation as _, useQuery as v } from "@apollo/client/react";
11
+ import { jsx as y, jsxs as b } from "react/jsx-runtime";
12
+ import { Button as x, Card as S, CardContent as C, CardHeader as w, CardTitle as T, Input as E } from "@burdenoff/fe-libs/ui";
11
13
  //#region src/planmagnet/pages/ProductsEditProductPagePage.tsx
12
- var C = f`
14
+ var D = g`
13
15
  query GetRoadmapForEdit($id: ID!) {
14
16
  planmagnetRoadmap(id: $id) {
15
17
  id
@@ -28,7 +30,7 @@ var C = f`
28
30
  }
29
31
  }
30
32
  }
31
- `, w = f`
33
+ `, O = g`
32
34
  mutation UpdateRoadmap($id: ID!, $input: PlanmagnetUpdateRoadmapInput!) {
33
35
  planmagnetUpdateRoadmap(id: $id, input: $input) {
34
36
  id
@@ -41,207 +43,236 @@ var C = f`
41
43
  updatedAt
42
44
  }
43
45
  }
44
- `, T = f`
46
+ `, k = g`
45
47
  mutation DeleteRoadmap($id: ID!) {
46
48
  planmagnetDeleteRoadmap(id: $id)
47
49
  }
48
50
  `;
49
- function E() {
50
- let { workspaceId: f } = e(), E = t(), D = d().id ?? "", { data: O, loading: k, error: A } = m(C, {
51
- variables: { id: D },
52
- skip: !D
53
- }), [j, { loading: M, error: N }] = p(w, { refetchQueries: ["ListRoadmaps", "GetRoadmapForEdit"] }), [P, { loading: F }] = p(T, {
54
- refetchQueries: ["ListRoadmaps"],
55
- onCompleted: () => E("/products/products-page")
56
- }), I = O?.planmagnetRoadmap, L = a(() => ({
57
- name: I?.name ?? "",
58
- description: I?.description ?? "",
59
- planningFocus: I?.planningFocus ?? null,
60
- isPublic: I?.isPublic ?? !1
61
- }), [I]), [R, z] = o({}), B = {
62
- ...L,
63
- ...R
64
- }, V = (e) => z(e);
65
- return k && !O ? /* @__PURE__ */ h("div", {
51
+ function A() {
52
+ let { workspaceId: g } = e(), A = t(), j = h().id ?? "", { data: M, loading: N, error: P } = v(D, {
53
+ variables: { id: j },
54
+ skip: !j
55
+ }), [F, { loading: I }] = _(O, {
56
+ refetchQueries: [
57
+ "ListRoadmaps",
58
+ "ListRoadmapsPage",
59
+ "GetRoadmapForEdit",
60
+ "GetRoadmapDetail",
61
+ "GetRoadmapDetailPage"
62
+ ],
63
+ awaitRefetchQueries: !0
64
+ }), [L, { loading: R }] = _(k, {
65
+ refetchQueries: ["ListRoadmaps", "ListRoadmapsPage"],
66
+ context: r
67
+ }), [z, B] = l(null), [V, H] = l(!1), U = M?.planmagnetRoadmap, W = c(() => ({
68
+ name: U?.name ?? "",
69
+ description: U?.description ?? "",
70
+ planningFocus: U?.planningFocus ?? null,
71
+ isPublic: U?.isPublic ?? !1
72
+ }), [U]), [G, K] = l({}), q = {
73
+ ...W,
74
+ ...G
75
+ }, J = (e) => K(e), Y = async () => {
76
+ B(null), H(!1);
77
+ try {
78
+ await i(F({ variables: {
79
+ id: j,
80
+ input: {
81
+ name: q.name,
82
+ description: q.description.trim() || null,
83
+ planningFocus: q.planningFocus,
84
+ isPublic: q.isPublic
85
+ }
86
+ } })), H(!0);
87
+ } catch (e) {
88
+ B(e instanceof Error ? e.message : "The product could not be saved.");
89
+ }
90
+ }, X = async () => {
91
+ B(null);
92
+ try {
93
+ await i(L({ variables: { id: j } })), A("/products/products-page");
94
+ } catch (e) {
95
+ B(e instanceof Error ? e.message : "The product could not be deleted.");
96
+ }
97
+ };
98
+ return N && !M ? /* @__PURE__ */ y("div", {
66
99
  className: "flex items-center justify-center p-12",
67
- children: /* @__PURE__ */ h(c, { className: "h-8 w-8 animate-spin text-muted-foreground" })
68
- }) : A ? /* @__PURE__ */ g("div", {
100
+ children: /* @__PURE__ */ y(d, { className: "h-8 w-8 animate-spin text-muted-foreground" })
101
+ }) : P ? /* @__PURE__ */ b("div", {
69
102
  className: "p-6 text-destructive",
70
- children: ["Failed to load product: ", A.message]
71
- }) : I ? /* @__PURE__ */ g("div", {
103
+ children: ["Failed to load product: ", P.message]
104
+ }) : U ? /* @__PURE__ */ b("div", {
72
105
  className: "space-y-6 p-6",
73
106
  children: [
74
- /* @__PURE__ */ g("div", {
107
+ /* @__PURE__ */ b("div", {
75
108
  className: "flex items-center justify-between",
76
- children: [/* @__PURE__ */ g("div", {
109
+ children: [/* @__PURE__ */ b("div", {
77
110
  className: "flex items-center gap-3",
78
- children: [/* @__PURE__ */ h(_, {
111
+ children: [/* @__PURE__ */ y(x, {
79
112
  variant: "ghost",
80
113
  size: "sm",
81
- onClick: () => E("/products/products-page"),
82
- children: /* @__PURE__ */ h(s, { className: "h-4 w-4" })
83
- }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("h1", {
114
+ onClick: () => A("/products/products-page"),
115
+ children: /* @__PURE__ */ y(u, { className: "h-4 w-4" })
116
+ }), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("h1", {
84
117
  className: "text-2xl font-semibold",
85
118
  children: "Edit Product"
86
- }), /* @__PURE__ */ g("p", {
119
+ }), /* @__PURE__ */ b("p", {
87
120
  className: "text-sm text-muted-foreground",
88
- children: ["Editing ", I.name]
121
+ children: ["Editing ", U.name]
89
122
  })] })]
90
- }), /* @__PURE__ */ g(_, {
123
+ }), /* @__PURE__ */ b(x, {
91
124
  size: "sm",
92
- onClick: () => {
93
- j({ variables: {
94
- id: D,
95
- input: {
96
- name: B.name,
97
- description: B.description || void 0,
98
- planningFocus: B.planningFocus,
99
- isPublic: B.isPublic
100
- }
101
- } });
102
- },
103
- disabled: M,
104
- children: [M ? /* @__PURE__ */ h(c, { className: "mr-1 h-4 w-4 animate-spin" }) : /* @__PURE__ */ h(u, { className: "mr-1 h-4 w-4" }), "Save Changes"]
125
+ onClick: Y,
126
+ disabled: I,
127
+ children: [I ? /* @__PURE__ */ y(d, { className: "mr-1 h-4 w-4 animate-spin" }) : /* @__PURE__ */ y(p, { className: "mr-1 h-4 w-4" }), "Save Changes"]
105
128
  })]
106
129
  }),
107
- N && /* @__PURE__ */ g("div", {
130
+ V && /* @__PURE__ */ y("div", {
131
+ role: "status",
132
+ className: "p-3 bg-muted text-status-success-text rounded-md text-sm",
133
+ children: "Changes saved."
134
+ }),
135
+ z && /* @__PURE__ */ y("div", {
108
136
  role: "alert",
109
137
  className: "p-3 rounded-md bg-destructive/10 text-destructive text-sm",
110
- children: ["Failed to save: ", N.message]
138
+ children: z
111
139
  }),
112
- /* @__PURE__ */ g("div", {
140
+ /* @__PURE__ */ b("div", {
113
141
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
114
- children: [/* @__PURE__ */ h("div", {
142
+ children: [/* @__PURE__ */ y("div", {
115
143
  className: "lg:col-span-2 space-y-6",
116
- children: /* @__PURE__ */ g(v, { children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ h(x, { children: "Basic Information" }) }), /* @__PURE__ */ g(y, {
144
+ children: /* @__PURE__ */ b(S, { children: [/* @__PURE__ */ y(w, { children: /* @__PURE__ */ y(T, { children: "Basic Information" }) }), /* @__PURE__ */ b(C, {
117
145
  className: "space-y-4",
118
146
  children: [
119
- /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
147
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
120
148
  className: "text-sm font-medium",
121
149
  children: "Product Name"
122
- }), /* @__PURE__ */ h(S, {
123
- value: B.name,
124
- onChange: (e) => V({
125
- ...B,
150
+ }), /* @__PURE__ */ y(E, {
151
+ value: q.name,
152
+ onChange: (e) => J({
153
+ ...q,
126
154
  name: e.target.value
127
155
  }),
128
156
  className: "mt-1"
129
157
  })] }),
130
- /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
158
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
131
159
  className: "text-sm font-medium",
132
160
  children: "Description"
133
- }), /* @__PURE__ */ h("textarea", {
161
+ }), /* @__PURE__ */ y("textarea", {
134
162
  className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]",
135
- value: B.description,
136
- onChange: (e) => V({
137
- ...B,
163
+ value: q.description,
164
+ onChange: (e) => J({
165
+ ...q,
138
166
  description: e.target.value
139
167
  })
140
168
  })] }),
141
- /* @__PURE__ */ h(i, {
142
- value: B.planningFocus,
143
- onChange: (e) => V({
144
- ...B,
169
+ /* @__PURE__ */ y(s, {
170
+ value: q.planningFocus,
171
+ onChange: (e) => J({
172
+ ...q,
145
173
  planningFocus: e
146
174
  })
147
175
  }),
148
- /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
176
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
149
177
  className: "text-sm font-medium",
150
178
  children: "Visibility"
151
- }), /* @__PURE__ */ h("div", {
179
+ }), /* @__PURE__ */ y("div", {
152
180
  className: "mt-1 flex gap-2",
153
- children: [!1, !0].map((e) => /* @__PURE__ */ h("button", {
154
- onClick: () => V({
155
- ...B,
181
+ children: [!1, !0].map((e) => /* @__PURE__ */ y("button", {
182
+ onClick: () => J({
183
+ ...q,
156
184
  isPublic: e
157
185
  }),
158
- className: `rounded-md px-3 py-1.5 text-sm ${B.isPublic === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
186
+ className: `rounded-md px-3 py-1.5 text-sm ${q.isPublic === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
159
187
  children: e ? "Public" : "Private"
160
188
  }, String(e)))
161
189
  })] })
162
190
  ]
163
191
  })] })
164
- }), /* @__PURE__ */ g("div", {
192
+ }), /* @__PURE__ */ b("div", {
165
193
  className: "space-y-6",
166
- children: [/* @__PURE__ */ g(v, { children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ h(x, { children: "Product Info" }) }), /* @__PURE__ */ g(y, {
194
+ children: [/* @__PURE__ */ b(S, { children: [/* @__PURE__ */ y(w, { children: /* @__PURE__ */ y(T, { children: "Product Info" }) }), /* @__PURE__ */ b(C, {
167
195
  className: "space-y-3 text-sm",
168
196
  children: [
169
- /* @__PURE__ */ g("div", {
197
+ /* @__PURE__ */ b("div", {
170
198
  className: "flex items-center gap-2",
171
- children: [/* @__PURE__ */ h(l, { className: "h-4 w-4 text-primary" }), /* @__PURE__ */ h("span", {
199
+ children: [/* @__PURE__ */ y(f, { className: "h-4 w-4 text-primary" }), /* @__PURE__ */ y("span", {
172
200
  className: "font-medium",
173
- children: I.name
201
+ children: U.name
174
202
  })]
175
203
  }),
176
- /* @__PURE__ */ g("div", {
204
+ /* @__PURE__ */ b("div", {
177
205
  className: "flex justify-between",
178
- children: [/* @__PURE__ */ h("span", {
206
+ children: [/* @__PURE__ */ y("span", {
179
207
  className: "text-muted-foreground",
180
208
  children: "Items"
181
- }), /* @__PURE__ */ h("span", {
209
+ }), /* @__PURE__ */ y("span", {
182
210
  className: "font-medium",
183
- children: I.items?.length ?? 0
211
+ children: U.items?.length ?? 0
184
212
  })]
185
213
  }),
186
- /* @__PURE__ */ g("div", {
214
+ /* @__PURE__ */ b("div", {
187
215
  className: "flex justify-between",
188
- children: [/* @__PURE__ */ h("span", {
216
+ children: [/* @__PURE__ */ y("span", {
189
217
  className: "text-muted-foreground",
190
218
  children: "Planning focus"
191
- }), /* @__PURE__ */ h(r, { focus: I.planningFocus })]
219
+ }), /* @__PURE__ */ y(o, { focus: U.planningFocus })]
192
220
  }),
193
- /* @__PURE__ */ g("div", {
221
+ /* @__PURE__ */ b("div", {
194
222
  className: "flex justify-between",
195
- children: [/* @__PURE__ */ h("span", {
223
+ children: [/* @__PURE__ */ y("span", {
196
224
  className: "text-muted-foreground",
197
225
  children: "Visibility"
198
- }), /* @__PURE__ */ h("span", {
226
+ }), /* @__PURE__ */ y("span", {
199
227
  className: "font-medium",
200
- children: I.isPublic ? "Public" : "Private"
228
+ children: U.isPublic ? "Public" : "Private"
201
229
  })]
202
230
  }),
203
- /* @__PURE__ */ g("div", {
231
+ /* @__PURE__ */ b("div", {
204
232
  className: "flex justify-between",
205
- children: [/* @__PURE__ */ h("span", {
233
+ children: [/* @__PURE__ */ y("span", {
206
234
  className: "text-muted-foreground",
207
235
  children: "Owner"
208
- }), /* @__PURE__ */ h("span", {
236
+ }), /* @__PURE__ */ y("span", {
209
237
  className: "font-medium",
210
- children: /* @__PURE__ */ h(n, { id: I.ownerId })
238
+ children: /* @__PURE__ */ y(n, { id: U.ownerId })
211
239
  })]
212
240
  }),
213
- /* @__PURE__ */ g("div", {
241
+ /* @__PURE__ */ b("div", {
214
242
  className: "flex justify-between",
215
- children: [/* @__PURE__ */ h("span", {
243
+ children: [/* @__PURE__ */ y("span", {
216
244
  className: "text-muted-foreground",
217
245
  children: "Created"
218
- }), /* @__PURE__ */ h("span", { children: new Date(I.createdAt).toLocaleDateString() })]
246
+ }), /* @__PURE__ */ y("span", { children: new Date(U.createdAt).toLocaleDateString() })]
219
247
  }),
220
- /* @__PURE__ */ g("div", {
248
+ /* @__PURE__ */ b("div", {
221
249
  className: "flex justify-between",
222
- children: [/* @__PURE__ */ h("span", {
250
+ children: [/* @__PURE__ */ y("span", {
223
251
  className: "text-muted-foreground",
224
252
  children: "Updated"
225
- }), /* @__PURE__ */ h("span", { children: new Date(I.updatedAt).toLocaleDateString() })]
253
+ }), /* @__PURE__ */ y("span", { children: new Date(U.updatedAt).toLocaleDateString() })]
226
254
  })
227
255
  ]
228
- })] }), /* @__PURE__ */ g(v, { children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ h(x, { children: "Danger Zone" }) }), /* @__PURE__ */ h(y, { children: /* @__PURE__ */ h(_, {
229
- variant: "destructive",
230
- size: "sm",
231
- className: "w-full",
232
- disabled: F,
233
- onClick: () => P({ variables: { id: D } }),
234
- children: F ? "Archiving..." : "Archive Product"
256
+ })] }), /* @__PURE__ */ b(S, { children: [/* @__PURE__ */ y(w, { children: /* @__PURE__ */ y(T, { children: "Danger Zone" }) }), /* @__PURE__ */ y(C, { children: /* @__PURE__ */ y(a, {
257
+ label: R ? "Deleting…" : "Delete product",
258
+ variant: "outline",
259
+ icon: /* @__PURE__ */ y(m, { className: "mr-1 h-4 w-4" }),
260
+ title: `Delete ${U.name}?`,
261
+ description: "The product's roadmap and its items are deleted. This cannot be undone here.",
262
+ confirmLabel: "Delete product",
263
+ destructive: !0,
264
+ disabled: R,
265
+ onConfirm: () => void X()
235
266
  }) })] })]
236
267
  })]
237
268
  })
238
269
  ]
239
- }) : /* @__PURE__ */ h("div", {
270
+ }) : /* @__PURE__ */ y("div", {
240
271
  className: "p-6 text-muted-foreground",
241
272
  children: "No product found."
242
273
  });
243
274
  }
244
275
  //#endregion
245
- export { E as ProductsEditProductPagePage };
276
+ export { A as ProductsEditProductPagePage };
246
277
 
247
278
  //# sourceMappingURL=ProductsEditProductPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsEditProductPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsEditProductPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Button,\n Input,\n Badge,\n} from '@burdenoff/fe-libs/ui';\nimport { Save, ArrowLeft, Package, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport {\n PlanningFocusBadge,\n PlanningFocusSelect,\n type PlanningFocus,\n} from '../roadmaps/planningFocus';\n\nconst GET_ROADMAP = gql`\n query GetRoadmapForEdit($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n workspaceId\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n status\n }\n }\n }\n`;\n\nconst UPDATE_ROADMAP = gql`\n mutation UpdateRoadmap($id: ID!, $input: PlanmagnetUpdateRoadmapInput!) {\n planmagnetUpdateRoadmap(id: $id, input: $input) {\n id\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n updatedAt\n }\n }\n`;\n\nconst DELETE_ROADMAP = gql`\n mutation DeleteRoadmap($id: ID!) {\n planmagnetDeleteRoadmap(id: $id)\n }\n`;\n\nexport function ProductsEditProductPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const params = useParams<{ id: string }>();\n const roadmapId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_ROADMAP, {\n variables: { id: roadmapId },\n skip: !roadmapId,\n });\n\n const [updateRoadmap, { loading: saving, error: saveError }] = useMutation(UPDATE_ROADMAP, {\n refetchQueries: ['ListRoadmaps', 'GetRoadmapForEdit'],\n });\n\n const [deleteRoadmap, { loading: deleting }] = useMutation(DELETE_ROADMAP, {\n refetchQueries: ['ListRoadmaps'],\n onCompleted: () => navigate('/products/products-page'),\n });\n\n const roadmap = data?.planmagnetRoadmap;\n\n const defaultForm = useMemo(\n () => ({\n name: roadmap?.name ?? '',\n description: roadmap?.description ?? '',\n planningFocus: (roadmap?.planningFocus ?? null) as PlanningFocus | null,\n isPublic: roadmap?.isPublic ?? false,\n }),\n [roadmap]\n );\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const [formOverrides, setFormOverrides] = useState<Record<string, any>>({});\n const form = { ...defaultForm, ...formOverrides } as {\n name: string;\n description: string;\n planningFocus: PlanningFocus | null;\n isPublic: boolean;\n };\n const setForm = (val: typeof form) => setFormOverrides(val);\n\n const handleSave = () => {\n updateRoadmap({\n variables: {\n id: roadmapId,\n input: {\n name: form.name,\n description: form.description || undefined,\n // The display is not editable: the page has one layout (BOFF-7934).\n // null clears a focus that was set by mistake.\n planningFocus: form.planningFocus,\n isPublic: form.isPublic,\n },\n },\n });\n };\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load product: {error.message}</div>;\n }\n\n if (!roadmap) {\n return <div className=\"p-6 text-muted-foreground\">No product found.</div>;\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigate('/products/products-page')}>\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Product</h1>\n <p className=\"text-sm text-muted-foreground\">Editing {roadmap.name}</p>\n </div>\n </div>\n <Button size=\"sm\" onClick={handleSave} disabled={saving}>\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Save Changes\n </Button>\n </div>\n\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to save: {saveError.message}\n </div>\n )}\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Basic Information</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Product Name</label>\n <Input\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n <PlanningFocusSelect\n value={form.planningFocus}\n onChange={(planningFocus) => setForm({ ...form, planningFocus })}\n />\n <div>\n <label className=\"text-sm font-medium\">Visibility</label>\n <div className=\"mt-1 flex gap-2\">\n {[false, true].map((pub) => (\n <button\n key={String(pub)}\n onClick={() => setForm({ ...form, isPublic: pub })}\n className={`rounded-md px-3 py-1.5 text-sm ${form.isPublic === pub ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {pub ? 'Public' : 'Private'}\n </button>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Product Info</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex items-center gap-2\">\n <Package className=\"h-4 w-4 text-primary\" />\n <span className=\"font-medium\">{roadmap.name}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Items</span>\n <span className=\"font-medium\">{roadmap.items?.length ?? 0}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Planning focus</span>\n <PlanningFocusBadge focus={roadmap.planningFocus} />\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Visibility</span>\n <span className=\"font-medium\">{roadmap.isPublic ? 'Public' : 'Private'}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Owner</span>\n <span className=\"font-medium\">\n <PersonName id={roadmap.ownerId} />\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(roadmap.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(roadmap.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Danger Zone</CardTitle>\n </CardHeader>\n <CardContent>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n className=\"w-full\"\n disabled={deleting}\n onClick={() => deleteRoadmap({ variables: { id: roadmapId } })}\n >\n {deleting ? 'Archiving...' : 'Archive Product'}\n </Button>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsEditProductPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsEditProductPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Button,\n Input,\n Badge,\n} from '@burdenoff/fe-libs/ui';\nimport { Save, ArrowLeft, Package, Loader2, Trash2 } from 'lucide-react';\nimport { throwOnError, LEAVES_PAGE_CONTEXT } from '../utils/mutationResult';\nimport { ConfirmButton } from '../controls/ConfirmButton';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport {\n PlanningFocusBadge,\n PlanningFocusSelect,\n type PlanningFocus,\n} from '../roadmaps/planningFocus';\n\nconst GET_ROADMAP = gql`\n query GetRoadmapForEdit($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n workspaceId\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n status\n }\n }\n }\n`;\n\nconst UPDATE_ROADMAP = gql`\n mutation UpdateRoadmap($id: ID!, $input: PlanmagnetUpdateRoadmapInput!) {\n planmagnetUpdateRoadmap(id: $id, input: $input) {\n id\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n updatedAt\n }\n }\n`;\n\nconst DELETE_ROADMAP = gql`\n mutation DeleteRoadmap($id: ID!) {\n planmagnetDeleteRoadmap(id: $id)\n }\n`;\n\nexport function ProductsEditProductPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const params = useParams<{ id: string }>();\n const roadmapId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_ROADMAP, {\n variables: { id: roadmapId },\n skip: !roadmapId,\n });\n\n const [updateRoadmap, { loading: saving }] = useMutation(UPDATE_ROADMAP, {\n refetchQueries: [\n 'ListRoadmaps',\n 'ListRoadmapsPage',\n 'GetRoadmapForEdit',\n 'GetRoadmapDetail',\n 'GetRoadmapDetailPage',\n ],\n awaitRefetchQueries: true,\n });\n // BOFF-7980: the button said \"Archive\" but deleted, on the first click, without awaiting — a refusal showed nothing.\n const [deleteRoadmap, { loading: deleting }] = useMutation(DELETE_ROADMAP, {\n refetchQueries: ['ListRoadmaps', 'ListRoadmapsPage'],\n context: LEAVES_PAGE_CONTEXT,\n });\n const [saveError, setSaveError] = useState<string | null>(null);\n const [saved, setSaved] = useState(false);\n\n const roadmap = data?.planmagnetRoadmap;\n\n const defaultForm = useMemo(\n () => ({\n name: roadmap?.name ?? '',\n description: roadmap?.description ?? '',\n planningFocus: (roadmap?.planningFocus ?? null) as PlanningFocus | null,\n isPublic: roadmap?.isPublic ?? false,\n }),\n [roadmap]\n );\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const [formOverrides, setFormOverrides] = useState<Record<string, any>>({});\n const form = { ...defaultForm, ...formOverrides } as {\n name: string;\n description: string;\n planningFocus: PlanningFocus | null;\n isPublic: boolean;\n };\n const setForm = (val: typeof form) => setFormOverrides(val);\n\n // BOFF-7979: an emptied description is cleared (null), not silently kept; success is confirmed.\n const handleSave = async () => {\n setSaveError(null);\n setSaved(false);\n try {\n await throwOnError(\n updateRoadmap({\n variables: {\n id: roadmapId,\n input: {\n name: form.name,\n description: form.description.trim() || null,\n // The display is not editable: the page has one layout (BOFF-7934).\n // null clears a focus that was set by mistake.\n planningFocus: form.planningFocus,\n isPublic: form.isPublic,\n },\n },\n })\n );\n setSaved(true);\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The product could not be saved.');\n }\n };\n\n const handleDelete = async () => {\n setSaveError(null);\n try {\n await throwOnError(deleteRoadmap({ variables: { id: roadmapId } }));\n navigate('/products/products-page');\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The product could not be deleted.');\n }\n };\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load product: {error.message}</div>;\n }\n\n if (!roadmap) {\n return <div className=\"p-6 text-muted-foreground\">No product found.</div>;\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigate('/products/products-page')}>\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Product</h1>\n <p className=\"text-sm text-muted-foreground\">Editing {roadmap.name}</p>\n </div>\n </div>\n <Button size=\"sm\" onClick={handleSave} disabled={saving}>\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Save Changes\n </Button>\n </div>\n\n {saved && (\n <div role=\"status\" className=\"p-3 bg-muted text-status-success-text rounded-md text-sm\">\n Changes saved.\n </div>\n )}\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n {saveError}\n </div>\n )}\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Basic Information</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Product Name</label>\n <Input\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n <PlanningFocusSelect\n value={form.planningFocus}\n onChange={(planningFocus) => setForm({ ...form, planningFocus })}\n />\n <div>\n <label className=\"text-sm font-medium\">Visibility</label>\n <div className=\"mt-1 flex gap-2\">\n {[false, true].map((pub) => (\n <button\n key={String(pub)}\n onClick={() => setForm({ ...form, isPublic: pub })}\n className={`rounded-md px-3 py-1.5 text-sm ${form.isPublic === pub ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {pub ? 'Public' : 'Private'}\n </button>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Product Info</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex items-center gap-2\">\n <Package className=\"h-4 w-4 text-primary\" />\n <span className=\"font-medium\">{roadmap.name}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Items</span>\n <span className=\"font-medium\">{roadmap.items?.length ?? 0}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Planning focus</span>\n <PlanningFocusBadge focus={roadmap.planningFocus} />\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Visibility</span>\n <span className=\"font-medium\">{roadmap.isPublic ? 'Public' : 'Private'}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Owner</span>\n <span className=\"font-medium\">\n <PersonName id={roadmap.ownerId} />\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(roadmap.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(roadmap.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Danger Zone</CardTitle>\n </CardHeader>\n <CardContent>\n <ConfirmButton\n label={deleting ? 'Deleting…' : 'Delete product'}\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n title={`Delete ${roadmap.name}?`}\n description=\"The product's roadmap and its items are deleted. This cannot be undone here.\"\n confirmLabel=\"Delete product\"\n destructive\n disabled={deleting}\n onConfirm={() => void handleDelete()}\n />\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsEditReleasePagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsEditReleasePagePage.tsx"],"names":[],"mappings":"AA4CA,wBAAgB,2BAA2B,4CAqM1C"}
1
+ {"version":3,"file":"ProductsEditReleasePagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsEditReleasePagePage.tsx"],"names":[],"mappings":"AA8CA,wBAAgB,2BAA2B,4CAiO1C"}