@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.
- package/dist/generated/global-types.d.ts +9 -1
- package/dist/generated/global-types.d.ts.map +1 -1
- package/dist/generated/wspace-operations.d.ts +0 -12
- package/dist/generated/wspace-operations.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +14 -10
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/admin/PlanMagnetAdminRoot.d.ts.map +1 -1
- package/dist/planmagnet/admin/PlanMagnetAdminRoot.js +149 -127
- package/dist/planmagnet/admin/PlanMagnetAdminRoot.js.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts +26 -0
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/FeatureControls.js +321 -0
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -0
- package/dist/planmagnet/controls/FeatureTriage.d.ts +21 -0
- package/dist/planmagnet/controls/FeatureTriage.d.ts.map +1 -0
- package/dist/planmagnet/controls/FeatureTriage.js +398 -0
- package/dist/planmagnet/controls/FeatureTriage.js.map +1 -0
- package/dist/planmagnet/controls/MilestoneControls.d.ts +17 -0
- package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/MilestoneControls.js +203 -0
- package/dist/planmagnet/controls/MilestoneControls.js.map +1 -0
- package/dist/planmagnet/controls/ObjectiveControls.d.ts +31 -0
- package/dist/planmagnet/controls/ObjectiveControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/ObjectiveControls.js +296 -0
- package/dist/planmagnet/controls/ObjectiveControls.js.map +1 -0
- package/dist/planmagnet/controls/Pickers.d.ts +26 -0
- package/dist/planmagnet/controls/Pickers.d.ts.map +1 -0
- package/dist/planmagnet/controls/Pickers.js +171 -0
- package/dist/planmagnet/controls/Pickers.js.map +1 -0
- package/dist/planmagnet/controls/ProjectControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/ProjectControls.js +47 -44
- package/dist/planmagnet/controls/ProjectControls.js.map +1 -1
- package/dist/planmagnet/controls/ReleaseControls.d.ts +14 -0
- package/dist/planmagnet/controls/ReleaseControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/ReleaseControls.js +111 -0
- package/dist/planmagnet/controls/ReleaseControls.js.map +1 -0
- package/dist/planmagnet/controls/WorkItemControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/WorkItemControls.js +68 -65
- package/dist/planmagnet/controls/WorkItemControls.js.map +1 -1
- package/dist/planmagnet/hooks/useCounts.d.ts +39 -0
- package/dist/planmagnet/hooks/useCounts.d.ts.map +1 -0
- package/dist/planmagnet/hooks/useCounts.js +209 -0
- package/dist/planmagnet/hooks/useCounts.js.map +1 -0
- package/dist/planmagnet/import/exportColumns.d.ts +52 -0
- package/dist/planmagnet/import/exportColumns.d.ts.map +1 -0
- package/dist/planmagnet/import/exportColumns.js +81 -0
- package/dist/planmagnet/import/exportColumns.js.map +1 -0
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +255 -242
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js +199 -160
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js +190 -153
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js +63 -56
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsGoalsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsGoalsPagePage.js +2 -7
- package/dist/planmagnet/pages/GoalsGoalsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +229 -214
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js +145 -114
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.js +134 -105
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js +97 -117
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +169 -110
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +117 -103
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +119 -94
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +4 -8
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +85 -90
- package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +118 -107
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js +4 -2
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +48 -71
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +17 -13
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js +1 -1
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +55 -63
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +11 -8
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +152 -139
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +75 -74
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts +7 -0
- package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.js +39 -201
- package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +118 -99
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +123 -128
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js +139 -107
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksImportPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TasksImportPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TasksImportPagePage.js +309 -0
- package/dist/planmagnet/pages/TasksImportPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +173 -126
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.js +111 -104
- package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TeamTeamPagePage.js +99 -177
- package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -4
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js.map +1 -1
- package/dist/planmagnet/releases/ReleaseWorkItemsCard.d.ts.map +1 -1
- package/dist/planmagnet/releases/ReleaseWorkItemsCard.js +10 -4
- package/dist/planmagnet/releases/ReleaseWorkItemsCard.js.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapControls.js +150 -137
- package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.js +32 -30
- package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
- package/dist/planmagnet/utils/mutationResult.d.ts +10 -0
- package/dist/planmagnet/utils/mutationResult.d.ts.map +1 -1
- package/dist/planmagnet/utils/mutationResult.js +2 -1
- package/dist/planmagnet/utils/mutationResult.js.map +1 -1
- package/dist/planmagnet/utils/rowNavigation.d.ts +2 -0
- package/dist/planmagnet/utils/rowNavigation.d.ts.map +1 -1
- package/dist/planmagnet/utils/rowNavigation.js +9 -8
- package/dist/planmagnet/utils/rowNavigation.js.map +1 -1
- 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)}%) —\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)}%) —\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":"
|
|
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 {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
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
|
|
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
|
-
`,
|
|
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
|
-
`,
|
|
46
|
+
`, k = g`
|
|
45
47
|
mutation DeleteRoadmap($id: ID!) {
|
|
46
48
|
planmagnetDeleteRoadmap(id: $id)
|
|
47
49
|
}
|
|
48
50
|
`;
|
|
49
|
-
function
|
|
50
|
-
let { workspaceId:
|
|
51
|
-
variables: { id:
|
|
52
|
-
skip: !
|
|
53
|
-
}), [
|
|
54
|
-
refetchQueries: [
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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__ */
|
|
68
|
-
}) :
|
|
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: ",
|
|
71
|
-
}) :
|
|
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__ */
|
|
107
|
+
/* @__PURE__ */ b("div", {
|
|
75
108
|
className: "flex items-center justify-between",
|
|
76
|
-
children: [/* @__PURE__ */
|
|
109
|
+
children: [/* @__PURE__ */ b("div", {
|
|
77
110
|
className: "flex items-center gap-3",
|
|
78
|
-
children: [/* @__PURE__ */
|
|
111
|
+
children: [/* @__PURE__ */ y(x, {
|
|
79
112
|
variant: "ghost",
|
|
80
113
|
size: "sm",
|
|
81
|
-
onClick: () =>
|
|
82
|
-
children: /* @__PURE__ */
|
|
83
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
119
|
+
}), /* @__PURE__ */ b("p", {
|
|
87
120
|
className: "text-sm text-muted-foreground",
|
|
88
|
-
children: ["Editing ",
|
|
121
|
+
children: ["Editing ", U.name]
|
|
89
122
|
})] })]
|
|
90
|
-
}), /* @__PURE__ */
|
|
123
|
+
}), /* @__PURE__ */ b(x, {
|
|
91
124
|
size: "sm",
|
|
92
|
-
onClick:
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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:
|
|
138
|
+
children: z
|
|
111
139
|
}),
|
|
112
|
-
/* @__PURE__ */
|
|
140
|
+
/* @__PURE__ */ b("div", {
|
|
113
141
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
114
|
-
children: [/* @__PURE__ */
|
|
142
|
+
children: [/* @__PURE__ */ y("div", {
|
|
115
143
|
className: "lg:col-span-2 space-y-6",
|
|
116
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
147
|
+
/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
120
148
|
className: "text-sm font-medium",
|
|
121
149
|
children: "Product Name"
|
|
122
|
-
}), /* @__PURE__ */
|
|
123
|
-
value:
|
|
124
|
-
onChange: (e) =>
|
|
125
|
-
...
|
|
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__ */
|
|
158
|
+
/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
131
159
|
className: "text-sm font-medium",
|
|
132
160
|
children: "Description"
|
|
133
|
-
}), /* @__PURE__ */
|
|
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:
|
|
136
|
-
onChange: (e) =>
|
|
137
|
-
...
|
|
163
|
+
value: q.description,
|
|
164
|
+
onChange: (e) => J({
|
|
165
|
+
...q,
|
|
138
166
|
description: e.target.value
|
|
139
167
|
})
|
|
140
168
|
})] }),
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
-
value:
|
|
143
|
-
onChange: (e) =>
|
|
144
|
-
...
|
|
169
|
+
/* @__PURE__ */ y(s, {
|
|
170
|
+
value: q.planningFocus,
|
|
171
|
+
onChange: (e) => J({
|
|
172
|
+
...q,
|
|
145
173
|
planningFocus: e
|
|
146
174
|
})
|
|
147
175
|
}),
|
|
148
|
-
/* @__PURE__ */
|
|
176
|
+
/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
149
177
|
className: "text-sm font-medium",
|
|
150
178
|
children: "Visibility"
|
|
151
|
-
}), /* @__PURE__ */
|
|
179
|
+
}), /* @__PURE__ */ y("div", {
|
|
152
180
|
className: "mt-1 flex gap-2",
|
|
153
|
-
children: [!1, !0].map((e) => /* @__PURE__ */
|
|
154
|
-
onClick: () =>
|
|
155
|
-
...
|
|
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 ${
|
|
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__ */
|
|
192
|
+
}), /* @__PURE__ */ b("div", {
|
|
165
193
|
className: "space-y-6",
|
|
166
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
197
|
+
/* @__PURE__ */ b("div", {
|
|
170
198
|
className: "flex items-center gap-2",
|
|
171
|
-
children: [/* @__PURE__ */
|
|
199
|
+
children: [/* @__PURE__ */ y(f, { className: "h-4 w-4 text-primary" }), /* @__PURE__ */ y("span", {
|
|
172
200
|
className: "font-medium",
|
|
173
|
-
children:
|
|
201
|
+
children: U.name
|
|
174
202
|
})]
|
|
175
203
|
}),
|
|
176
|
-
/* @__PURE__ */
|
|
204
|
+
/* @__PURE__ */ b("div", {
|
|
177
205
|
className: "flex justify-between",
|
|
178
|
-
children: [/* @__PURE__ */
|
|
206
|
+
children: [/* @__PURE__ */ y("span", {
|
|
179
207
|
className: "text-muted-foreground",
|
|
180
208
|
children: "Items"
|
|
181
|
-
}), /* @__PURE__ */
|
|
209
|
+
}), /* @__PURE__ */ y("span", {
|
|
182
210
|
className: "font-medium",
|
|
183
|
-
children:
|
|
211
|
+
children: U.items?.length ?? 0
|
|
184
212
|
})]
|
|
185
213
|
}),
|
|
186
|
-
/* @__PURE__ */
|
|
214
|
+
/* @__PURE__ */ b("div", {
|
|
187
215
|
className: "flex justify-between",
|
|
188
|
-
children: [/* @__PURE__ */
|
|
216
|
+
children: [/* @__PURE__ */ y("span", {
|
|
189
217
|
className: "text-muted-foreground",
|
|
190
218
|
children: "Planning focus"
|
|
191
|
-
}), /* @__PURE__ */
|
|
219
|
+
}), /* @__PURE__ */ y(o, { focus: U.planningFocus })]
|
|
192
220
|
}),
|
|
193
|
-
/* @__PURE__ */
|
|
221
|
+
/* @__PURE__ */ b("div", {
|
|
194
222
|
className: "flex justify-between",
|
|
195
|
-
children: [/* @__PURE__ */
|
|
223
|
+
children: [/* @__PURE__ */ y("span", {
|
|
196
224
|
className: "text-muted-foreground",
|
|
197
225
|
children: "Visibility"
|
|
198
|
-
}), /* @__PURE__ */
|
|
226
|
+
}), /* @__PURE__ */ y("span", {
|
|
199
227
|
className: "font-medium",
|
|
200
|
-
children:
|
|
228
|
+
children: U.isPublic ? "Public" : "Private"
|
|
201
229
|
})]
|
|
202
230
|
}),
|
|
203
|
-
/* @__PURE__ */
|
|
231
|
+
/* @__PURE__ */ b("div", {
|
|
204
232
|
className: "flex justify-between",
|
|
205
|
-
children: [/* @__PURE__ */
|
|
233
|
+
children: [/* @__PURE__ */ y("span", {
|
|
206
234
|
className: "text-muted-foreground",
|
|
207
235
|
children: "Owner"
|
|
208
|
-
}), /* @__PURE__ */
|
|
236
|
+
}), /* @__PURE__ */ y("span", {
|
|
209
237
|
className: "font-medium",
|
|
210
|
-
children: /* @__PURE__ */
|
|
238
|
+
children: /* @__PURE__ */ y(n, { id: U.ownerId })
|
|
211
239
|
})]
|
|
212
240
|
}),
|
|
213
|
-
/* @__PURE__ */
|
|
241
|
+
/* @__PURE__ */ b("div", {
|
|
214
242
|
className: "flex justify-between",
|
|
215
|
-
children: [/* @__PURE__ */
|
|
243
|
+
children: [/* @__PURE__ */ y("span", {
|
|
216
244
|
className: "text-muted-foreground",
|
|
217
245
|
children: "Created"
|
|
218
|
-
}), /* @__PURE__ */
|
|
246
|
+
}), /* @__PURE__ */ y("span", { children: new Date(U.createdAt).toLocaleDateString() })]
|
|
219
247
|
}),
|
|
220
|
-
/* @__PURE__ */
|
|
248
|
+
/* @__PURE__ */ b("div", {
|
|
221
249
|
className: "flex justify-between",
|
|
222
|
-
children: [/* @__PURE__ */
|
|
250
|
+
children: [/* @__PURE__ */ y("span", {
|
|
223
251
|
className: "text-muted-foreground",
|
|
224
252
|
children: "Updated"
|
|
225
|
-
}), /* @__PURE__ */
|
|
253
|
+
}), /* @__PURE__ */ y("span", { children: new Date(U.updatedAt).toLocaleDateString() })]
|
|
226
254
|
})
|
|
227
255
|
]
|
|
228
|
-
})] }), /* @__PURE__ */
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
className: "w-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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__ */
|
|
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 {
|
|
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":"
|
|
1
|
+
{"version":3,"file":"ProductsEditReleasePagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsEditReleasePagePage.tsx"],"names":[],"mappings":"AA8CA,wBAAgB,2BAA2B,4CAiO1C"}
|