@burdenoff/microfe-planmagnet 2026.927.4 → 2026.927.6
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/board/ProjectBoard.d.ts +2 -0
- package/dist/planmagnet/board/ProjectBoard.d.ts.map +1 -1
- package/dist/planmagnet/board/ProjectBoard.js +492 -225
- package/dist/planmagnet/board/ProjectBoard.js.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.js +142 -139
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
- 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.map +1 -1
- package/dist/planmagnet/controls/MilestoneControls.js +76 -73
- package/dist/planmagnet/controls/MilestoneControls.js.map +1 -1
- 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.map +1 -1
- package/dist/planmagnet/controls/ReleaseControls.js +47 -44
- package/dist/planmagnet/controls/ReleaseControls.js.map +1 -1
- 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 +147 -144
- package/dist/planmagnet/pages/DocsDocDetailPagePage.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 +173 -173
- 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 +120 -117
- package/dist/planmagnet/pages/ProductsEditProductPagePage.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 +168 -103
- 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/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 +12 -2
- 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 +2 -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/ProjectsEditProjectPagePage.js +6 -6
- 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/ProjectsPlanningEditPagePage.js +4 -4
- 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/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/TasksTasksPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.js +110 -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 +151 -138
- 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';\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 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
|
+
{"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":";;;;;;;;;;;;AA4BA,IAAM,IAAgB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CnB,IAAoB,CAAG;;;;;;GAQvB,IAAoB,CAAG;;;;;;;;GAUvB,IAAoB,CAAG;;;;GAMvB,IAAmB,CAAG;;;;GAMtB,IAAwC;CAC5C,aAAa;CACb,aAAa;CACb,SAAS;CACT,WAAW;CACZ;AAED,SAAS,EAAY,EAAE,eAAkC;AAEvD,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;GAAgD,OAAO,EAAE,OAAO,GAHvE,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,EAAS,CAAC,CAAC,CAG8B,IAAI;GAAI,CAAA;EAC1F,CAAA;;AAoBV,SAAS,EAAa,EACpB,OACA,cACA,gBAKC;CACD,IAAM,CAAC,GAAY,KAAiB,EAAS,OAAO,EAAG,aAAa,CAAC,EAC/D,CAAC,GAAiB,EAAE,SAAS,OAAc,EAAY,EAAkB,EACzE,CAAC,GAAiB,EAAE,SAAS,OAAc,EAAY,EAAkB,EAGzE,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAE7D,IAAqB,YAAY;EACrC,IAAM,IAAS,EAAG,eAAe,YAAa,QAAe,OAAe,OAAO,EAAW;AAC1F,cAAO,MAAM,EAAO,EACxB;KAAe,KAAK;AACpB,OAAI;AAIF,IAHA,MAAM,EACJ,EAAgB,EAAE,WAAW;KAAE,IAAI,EAAG;KAAI,OAAO,EAAE,cAAc,GAAQ;KAAE,EAAE,CAAC,CAC/E,EACD,GAAW;YACJ,GAAG;AACV,MAAe,aAAa,QAAQ,EAAE,UAAU,mCAAmC;;;IAIjF,IAAe,YAAY;AAC/B,IAAe,KAAK;AACpB,MAAI;AAEF,GADA,MAAM,EAAa,EAAgB,EAAE,WAAW,EAAE,IAAI,EAAG,IAAI,EAAE,CAAC,CAAC,EACjE,GAAW;WACJ,GAAG;AACV,KAAe,aAAa,QAAQ,EAAE,UAAU,uCAAuC;;;AAI3F,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACG,KACC,kBAAC,KAAD;IAAG,MAAK;IAAQ,WAAU;cACvB;IACC,CAAA;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAuB,EAAG;MAAU,CAAA,EAChD,EAAG,eACF,kBAAC,KAAD;MAAG,WAAU;gBAAwC,EAAG;MAAgB,CAAA,CAEtE;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MACE,WAAW,+CAA+C,EAAc,EAAG,WAAW;gBAErF,EAAG,OAAO,aAAa,CAAC,QAAQ,KAAK,IAAI;MACrC,CAAA,EAEP,kBAAC,GAAD;MACE,OAAM;MACN,WAAW,UAAU,EAAG;MACxB,SAAQ;MACR,MAAM,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;MACpC,OAAO,WAAW,EAAG,MAAM;MAC3B,aAAY;MACZ,cAAa;MACb,aAAA;MACA,UAAU;MACV,iBAAiB,KAAK,GAAc;MACpC,CAAA,CACE;OACF;;GACN,kBAAC,GAAD,EAAa,UAAU,EAAG,UAAY,CAAA;GACtC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD,EAAA,UACG,EAAG,eAAe,YACf,EAAG,eAAe,IAChB,SACA,aACF,GAAG,EAAG,eAAe,EAAG,OAAO,IAAI,EAAG,SAAS,GAAG,MAAM,EAAG,cAAc,EAAG,OAAO,IAAI,EAAG,SAAS,MAClG,CAAA,EACP,kBAAC,QAAD,EAAA,UAAA,CAAO,KAAK,MAAM,EAAG,SAAS,EAAC,IAAQ,EAAA,CAAA,CACnC;;GACL,EAAG,qBAAqB,cAAc,KACrC,kBAAC,KAAD;IAAG,WAAU;cAAb;KACG,EAAG,qBAAqB;KAAU;KAAE,EAAG,qBAAqB;KAAY;KAC5D,KAAK,MAAM,EAAG,qBAAqB,kBAAkB,IAAI;KAAC;KAErE;;GAGN,kBAAC,GAAD;IACE,aAAa,EAAG;IAChB,gBAAgB,EAAG;IACnB,QAAQ,EAAG,mBAAmB,EAAE;IAChC,CAAA;GACF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAG,eAAe,YACjB,kBAAC,UAAD;KACE,WAAU;KACV,OAAO;KACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;eAHhD,CAKE,kBAAC,UAAD;MAAQ,OAAM;gBAAI;MAAiB,CAAA,EACnC,kBAAC,UAAD;MAAQ,OAAM;gBAAI;MAAa,CAAA,CACxB;SAET,kBAAC,GAAD;KACE,MAAK;KACL,OAAO;KACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;KAC9C,WAAU;KACV,CAAA,EAEJ,kBAAC,GAAD;KAAQ,MAAK;KAAK,SAAQ;KAAU,UAAU;KAAU,SAAS;eAC9D,IAAW,cAAc;KACnB,CAAA,CACL;;GACF;;;AAIV,SAAgB,IAA+B;CAC7C,IAAM,EAAE,mBAAgB,GAAe,EACjC,IAAW,GAAuB,EAClC,IAAc,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAAC,IAAI,cAAc,IAAI,IAEhF,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAQ,KAAa,EAAS;EACnC,OAAO;EACP,YAAY;EACZ,aAAa;EACb,MAAM;EACP,CAAC,EAEI,EAAE,SAAM,YAAS,UAAO,eAAY,EAcvC,GAAe;EAChB,WAAW,EAAE,IAAI,GAAa;EAC9B,MAAM,CAAC,KAAe,CAAC;EACxB,CAAC,EAEI,CAAC,GAAiB,EAAE,SAAS,OAAgB,EAAY,EAAkB,EAC3E,CAAC,GAAiB,EAAE,SAAS,OAAuB,EAAY,GAAkB,EACtF,SAAS,GACV,CAAC,EACI,CAAC,GAAa,KAAkB,EAAwB,KAAK;AAMnE,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD,EAAK,WAAU,kFAAmF,CAAA;EAC9F,CAAA;AAGV,KAAI,EAAO,QAAO,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAkC,WAAQ,EAAM,QAAc;;CAEhF,IAAM,IAAY,GAAM;AACxB,KAAI,CAAC,EAAW,QAAO,kBAAC,OAAD;EAAK,WAAU;YAA4B;EAA0B,CAAA;CAE5F,IAAM,IAAqB,YAAY;AACrC,MAAI,CAAC,EAAO,MAAM,MAAM,IAAI,CAAC,EAAa;EAC1C,IAAM,IAAS,OAAO,EAAO,eAAe,YAAY,IAAI,EAAO,YAAY;AAC3E,cAAO,MAAM,EAAO,EACxB;KAAe,KAAK;AACpB,OAAI;AACF,UAAM,EACJ,EAAgB,EACd,WAAW,EACT,OAAO;KACL;KACA,OAAO,EAAO,MAAM,MAAM;KAC1B,YAAY,EAAO;KACnB,aAAa;KACb,MAAM,EAAO,KAAK,MAAM,IAAI,KAAA;KAC7B,EACF,EACF,CAAC,CACH;YACM,GAAG;AACV,MAAe,aAAa,QAAQ,EAAE,UAAU,qCAAqC;AACrF;;AAIG,GAFL,EAAU;IAAE,OAAO;IAAI,YAAY;IAAW,aAAa;IAAO,MAAM;IAAI,CAAC,EAC7E,EAAc,GAAM,EACf,GAAS;;IAGV,IAAwB,YAAY;AACxC,IAAe,KAAK;AACpB,MAAI;AACF,SAAM,EAAa,EAAgB,EAAE,WAAW,EAAE,IAAI,GAAa,EAAE,CAAC,CAAC;WAChE,GAAG;AACV,KAAe,aAAa,QAAQ,EAAE,UAAU,sCAAsC;AACtF;;AAEF,IAAS,oBAAoB;;AAG/B,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACG,KACC,kBAAC,KAAD;IAAG,MAAK;IAAQ,WAAU;cACvB;IACC,CAAA;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAQ,SAAQ;OAAQ,MAAK;OAAO,eAAe,EAAS,oBAAoB;iBAC9E,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;OAC1B,CAAA;MACT,kBAAC,GAAD,EAAM,WAAU,wBAAyB,CAAA;MACzC,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA0B,EAAU;OAAW,CAAA,EAC7D,kBAAC,KAAD;OAAG,WAAU;iBAAiC,EAAU;OAAgB,CAAA,CACpE,EAAA,CAAA;MACF;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,kBAAC,GAAD,EAAoC,cAAa,CAAA,EACjD,kBAAC,GAAD;MACE,OAAM;MACN,SAAQ;MACR,MAAM,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA;MACzC,OAAO,WAAW,EAAU,MAAM;MAClC,aAAY;MACZ,cAAa;MACb,aAAA;MACA,UAAU;MACV,iBAAiB,KAAK,GAAuB;MAC7C,CAAA,CACE;OACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;MAAY,WAAU;gBAAtB,CACE,kBAAC,GAAD,EAAA,UAAW,YAAoB,CAAA,EAC/B,kBAAC,GAAD;OAAO,SAAQ;iBAAf,CAA0B,KAAK,MAAM,EAAU,SAAS,EAAC,aAAkB;SAChE;SACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAW,EAAU,eAAe;OAA+B,CAAA,EAChF,kBAAC,GAAD,EAAa,UAAU,EAAU,UAAY,CAAA,CACjC;QACT,EAAA,CAAA,EAEP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;MAAY,WAAU;gBAAtB,CACE,kBAAC,GAAD,EAAA,UAAW,eAAuB,CAAA,EACjC,CAAC,KACA,kBAAC,GAAD;OAAQ,MAAK;OAAK,SAAQ;OAAU,eAAe,EAAc,GAAK;iBAAtE,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,iBAE1B;SAEA;SACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACG,KACC,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD;UACE,aAAY;UACZ,OAAO,EAAO;UACd,WAAW,MAAM,EAAU;WAAE,GAAG;WAAQ,OAAO,EAAE,OAAO;WAAO,CAAC;UAChE,CAAA;SACF,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,UAAD;YACE,WAAU;YACV,OAAO,EAAO;YACd,WAAW,MAAM,EAAU;aAAE,GAAG;aAAQ,YAAY,EAAE,OAAO;aAAO,CAAC;sBAHvE;aAKE,kBAAC,UAAD;cAAQ,OAAM;wBAAU;cAAgB,CAAA;aACxC,kBAAC,UAAD;cAAQ,OAAM;wBAAa;cAAmB,CAAA;aAC9C,kBAAC,UAAD;cAAQ,OAAM;wBAAU;cAAiB,CAAA;aAClC;;WACR,EAAO,eAAe,aACrB,kBAAC,GAAD;YACE,MAAK;YACL,aAAY;YACZ,OAAO,EAAO;YACd,WAAW,MAAM,EAAU;aAAE,GAAG;aAAQ,aAAa,EAAE,OAAO;aAAO,CAAC;YACtE,CAAA;WAEH,EAAO,eAAe,aACrB,kBAAC,GAAD;YACE,aAAY;YACZ,OAAO,EAAO;YACd,WAAW,MAAM,EAAU;aAAE,GAAG;aAAQ,MAAM,EAAE,OAAO;aAAO,CAAC;YAC/D,CAAA;WAEA;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD;WACE,MAAK;WACL,SAAQ;WACR,eAAe;AAEb,YADA,EAAc,GAAM,EACpB,EAAU;aACR,OAAO;aACP,YAAY;aACZ,aAAa;aACb,MAAM;aACP,CAAC;;qBAEL;WAEQ,CAAA,EACT,kBAAC,GAAD;WACE,MAAK;WACL,UAAU,KAAc,CAAC,EAAO,MAAM,MAAM;WAC5C,SAAS;qBAER,IAAa,cAAc;WACrB,CAAA,CACL;;SACF;;OAEP,EAAU,WAAW,WAAW,KAAK,CAAC,KACrC,kBAAC,KAAD;QAAG,WAAU;kBAAgC;QAEzC,CAAA;OAEL,EAAU,WAAW,KAAK,MACzB,kBAAC,GAAD;QAEM;QACJ,iBAAiB,KAAK,GAAS;QAC/B,iBAAiB,KAAK,GAAS;QAC/B,EAJK,EAAG,GAIR,CACF;OACU;QACT,EAAA,CAAA,CACH;QAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,WAAmB,CAAA,EACnB,CAAA,EACb,kBAAC,GAAD;KAAa,WAAU;eAAvB;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAM,WAAU,iCAAkC,CAAA;QAClD,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAa,CAAA;QACrD,kBAAC,QAAD;SAAM,WAAU;mBACd,kBAAC,GAAD,EAAY,IAAI,EAAU,SAAW,CAAA;SAChC,CAAA;QACH;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAU,WAAU,iCAAkC,CAAA;QACtD,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAc,CAAA;QACtD,kBAAC,QAAD;SAAM,WAAU;mBAAe,EAAU;SAAmB,CAAA;QACxD;;MACL,EAAU,WACT,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAU,WAAU,iCAAkC,CAAA;QACtD,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAkB,CAAA;QAC1D,kBAAC,QAAD;SAAM,WAAU;mBACb,IAAI,KAAK,EAAU,QAAQ,CAAC,oBAAoB;SAC5C,CAAA;QACH;;MAER,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAU,WAAU,iCAAkC,CAAA;QACtD,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAe,CAAA;QACvD,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAU,YAAY,IAAI,KAAK,EAAU,UAAU,CAAC,oBAAoB,GAAG;SACvE,CAAA;QACH;;MACM;OACT,EAAA,CAAA,CACH;;GACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsEditProductPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsEditProductPagePage.tsx"],"names":[],"mappings":"AAoEA,wBAAgB,2BAA2B,
|
|
1
|
+
{"version":3,"file":"ProductsEditProductPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsEditProductPagePage.tsx"],"names":[],"mappings":"AAoEA,wBAAgB,2BAA2B,4CA4P1C"}
|
|
@@ -1,17 +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 { ConfirmButton as
|
|
5
|
-
import { PlanningFocusBadge as
|
|
6
|
-
import { useMemo as
|
|
7
|
-
import { ArrowLeft as
|
|
8
|
-
import { useParams as
|
|
9
|
-
import { gql as
|
|
10
|
-
import { useMutation as
|
|
11
|
-
import { jsx as
|
|
12
|
-
import { Button as
|
|
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";
|
|
13
13
|
//#region src/planmagnet/pages/ProductsEditProductPagePage.tsx
|
|
14
|
-
var
|
|
14
|
+
var D = g`
|
|
15
15
|
query GetRoadmapForEdit($id: ID!) {
|
|
16
16
|
planmagnetRoadmap(id: $id) {
|
|
17
17
|
id
|
|
@@ -30,7 +30,7 @@ var E = h`
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
`,
|
|
33
|
+
`, O = g`
|
|
34
34
|
mutation UpdateRoadmap($id: ID!, $input: PlanmagnetUpdateRoadmapInput!) {
|
|
35
35
|
planmagnetUpdateRoadmap(id: $id, input: $input) {
|
|
36
36
|
id
|
|
@@ -43,16 +43,16 @@ var E = h`
|
|
|
43
43
|
updatedAt
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
`,
|
|
46
|
+
`, k = g`
|
|
47
47
|
mutation DeleteRoadmap($id: ID!) {
|
|
48
48
|
planmagnetDeleteRoadmap(id: $id)
|
|
49
49
|
}
|
|
50
50
|
`;
|
|
51
|
-
function
|
|
52
|
-
let { workspaceId:
|
|
53
|
-
variables: { id:
|
|
54
|
-
skip: !
|
|
55
|
-
}), [
|
|
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
56
|
refetchQueries: [
|
|
57
57
|
"ListRoadmaps",
|
|
58
58
|
"ListRoadmapsPage",
|
|
@@ -61,215 +61,218 @@ function k() {
|
|
|
61
61
|
"GetRoadmapDetailPage"
|
|
62
62
|
],
|
|
63
63
|
awaitRefetchQueries: !0
|
|
64
|
-
}), [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
},
|
|
73
|
-
|
|
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);
|
|
74
77
|
try {
|
|
75
|
-
await
|
|
76
|
-
id:
|
|
78
|
+
await i(F({ variables: {
|
|
79
|
+
id: j,
|
|
77
80
|
input: {
|
|
78
|
-
name:
|
|
79
|
-
description:
|
|
80
|
-
planningFocus:
|
|
81
|
-
isPublic:
|
|
81
|
+
name: q.name,
|
|
82
|
+
description: q.description.trim() || null,
|
|
83
|
+
planningFocus: q.planningFocus,
|
|
84
|
+
isPublic: q.isPublic
|
|
82
85
|
}
|
|
83
|
-
} })),
|
|
86
|
+
} })), H(!0);
|
|
84
87
|
} catch (e) {
|
|
85
|
-
|
|
88
|
+
B(e instanceof Error ? e.message : "The product could not be saved.");
|
|
86
89
|
}
|
|
87
|
-
},
|
|
88
|
-
|
|
90
|
+
}, X = async () => {
|
|
91
|
+
B(null);
|
|
89
92
|
try {
|
|
90
|
-
await
|
|
93
|
+
await i(L({ variables: { id: j } })), A("/products/products-page");
|
|
91
94
|
} catch (e) {
|
|
92
|
-
|
|
95
|
+
B(e instanceof Error ? e.message : "The product could not be deleted.");
|
|
93
96
|
}
|
|
94
97
|
};
|
|
95
|
-
return
|
|
98
|
+
return N && !M ? /* @__PURE__ */ y("div", {
|
|
96
99
|
className: "flex items-center justify-center p-12",
|
|
97
|
-
children: /* @__PURE__ */
|
|
98
|
-
}) :
|
|
100
|
+
children: /* @__PURE__ */ y(d, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
101
|
+
}) : P ? /* @__PURE__ */ b("div", {
|
|
99
102
|
className: "p-6 text-destructive",
|
|
100
|
-
children: ["Failed to load product: ",
|
|
101
|
-
}) :
|
|
103
|
+
children: ["Failed to load product: ", P.message]
|
|
104
|
+
}) : U ? /* @__PURE__ */ b("div", {
|
|
102
105
|
className: "space-y-6 p-6",
|
|
103
106
|
children: [
|
|
104
|
-
/* @__PURE__ */
|
|
107
|
+
/* @__PURE__ */ b("div", {
|
|
105
108
|
className: "flex items-center justify-between",
|
|
106
|
-
children: [/* @__PURE__ */
|
|
109
|
+
children: [/* @__PURE__ */ b("div", {
|
|
107
110
|
className: "flex items-center gap-3",
|
|
108
|
-
children: [/* @__PURE__ */
|
|
111
|
+
children: [/* @__PURE__ */ y(x, {
|
|
109
112
|
variant: "ghost",
|
|
110
113
|
size: "sm",
|
|
111
|
-
onClick: () =>
|
|
112
|
-
children: /* @__PURE__ */
|
|
113
|
-
}), /* @__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", {
|
|
114
117
|
className: "text-2xl font-semibold",
|
|
115
118
|
children: "Edit Product"
|
|
116
|
-
}), /* @__PURE__ */
|
|
119
|
+
}), /* @__PURE__ */ b("p", {
|
|
117
120
|
className: "text-sm text-muted-foreground",
|
|
118
|
-
children: ["Editing ",
|
|
121
|
+
children: ["Editing ", U.name]
|
|
119
122
|
})] })]
|
|
120
|
-
}), /* @__PURE__ */
|
|
123
|
+
}), /* @__PURE__ */ b(x, {
|
|
121
124
|
size: "sm",
|
|
122
|
-
onClick:
|
|
123
|
-
disabled:
|
|
124
|
-
children: [
|
|
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"]
|
|
125
128
|
})]
|
|
126
129
|
}),
|
|
127
|
-
|
|
130
|
+
V && /* @__PURE__ */ y("div", {
|
|
128
131
|
role: "status",
|
|
129
132
|
className: "p-3 bg-muted text-status-success-text rounded-md text-sm",
|
|
130
133
|
children: "Changes saved."
|
|
131
134
|
}),
|
|
132
|
-
|
|
135
|
+
z && /* @__PURE__ */ y("div", {
|
|
133
136
|
role: "alert",
|
|
134
137
|
className: "p-3 rounded-md bg-destructive/10 text-destructive text-sm",
|
|
135
|
-
children:
|
|
138
|
+
children: z
|
|
136
139
|
}),
|
|
137
|
-
/* @__PURE__ */
|
|
140
|
+
/* @__PURE__ */ b("div", {
|
|
138
141
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
139
|
-
children: [/* @__PURE__ */
|
|
142
|
+
children: [/* @__PURE__ */ y("div", {
|
|
140
143
|
className: "lg:col-span-2 space-y-6",
|
|
141
|
-
children: /* @__PURE__ */
|
|
144
|
+
children: /* @__PURE__ */ b(S, { children: [/* @__PURE__ */ y(w, { children: /* @__PURE__ */ y(T, { children: "Basic Information" }) }), /* @__PURE__ */ b(C, {
|
|
142
145
|
className: "space-y-4",
|
|
143
146
|
children: [
|
|
144
|
-
/* @__PURE__ */
|
|
147
|
+
/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
145
148
|
className: "text-sm font-medium",
|
|
146
149
|
children: "Product Name"
|
|
147
|
-
}), /* @__PURE__ */
|
|
148
|
-
value:
|
|
149
|
-
onChange: (e) =>
|
|
150
|
-
...
|
|
150
|
+
}), /* @__PURE__ */ y(E, {
|
|
151
|
+
value: q.name,
|
|
152
|
+
onChange: (e) => J({
|
|
153
|
+
...q,
|
|
151
154
|
name: e.target.value
|
|
152
155
|
}),
|
|
153
156
|
className: "mt-1"
|
|
154
157
|
})] }),
|
|
155
|
-
/* @__PURE__ */
|
|
158
|
+
/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
156
159
|
className: "text-sm font-medium",
|
|
157
160
|
children: "Description"
|
|
158
|
-
}), /* @__PURE__ */
|
|
161
|
+
}), /* @__PURE__ */ y("textarea", {
|
|
159
162
|
className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]",
|
|
160
|
-
value:
|
|
161
|
-
onChange: (e) =>
|
|
162
|
-
...
|
|
163
|
+
value: q.description,
|
|
164
|
+
onChange: (e) => J({
|
|
165
|
+
...q,
|
|
163
166
|
description: e.target.value
|
|
164
167
|
})
|
|
165
168
|
})] }),
|
|
166
|
-
/* @__PURE__ */
|
|
167
|
-
value:
|
|
168
|
-
onChange: (e) =>
|
|
169
|
-
...
|
|
169
|
+
/* @__PURE__ */ y(s, {
|
|
170
|
+
value: q.planningFocus,
|
|
171
|
+
onChange: (e) => J({
|
|
172
|
+
...q,
|
|
170
173
|
planningFocus: e
|
|
171
174
|
})
|
|
172
175
|
}),
|
|
173
|
-
/* @__PURE__ */
|
|
176
|
+
/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
174
177
|
className: "text-sm font-medium",
|
|
175
178
|
children: "Visibility"
|
|
176
|
-
}), /* @__PURE__ */
|
|
179
|
+
}), /* @__PURE__ */ y("div", {
|
|
177
180
|
className: "mt-1 flex gap-2",
|
|
178
|
-
children: [!1, !0].map((e) => /* @__PURE__ */
|
|
179
|
-
onClick: () =>
|
|
180
|
-
...
|
|
181
|
+
children: [!1, !0].map((e) => /* @__PURE__ */ y("button", {
|
|
182
|
+
onClick: () => J({
|
|
183
|
+
...q,
|
|
181
184
|
isPublic: e
|
|
182
185
|
}),
|
|
183
|
-
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"}`,
|
|
184
187
|
children: e ? "Public" : "Private"
|
|
185
188
|
}, String(e)))
|
|
186
189
|
})] })
|
|
187
190
|
]
|
|
188
191
|
})] })
|
|
189
|
-
}), /* @__PURE__ */
|
|
192
|
+
}), /* @__PURE__ */ b("div", {
|
|
190
193
|
className: "space-y-6",
|
|
191
|
-
children: [/* @__PURE__ */
|
|
194
|
+
children: [/* @__PURE__ */ b(S, { children: [/* @__PURE__ */ y(w, { children: /* @__PURE__ */ y(T, { children: "Product Info" }) }), /* @__PURE__ */ b(C, {
|
|
192
195
|
className: "space-y-3 text-sm",
|
|
193
196
|
children: [
|
|
194
|
-
/* @__PURE__ */
|
|
197
|
+
/* @__PURE__ */ b("div", {
|
|
195
198
|
className: "flex items-center gap-2",
|
|
196
|
-
children: [/* @__PURE__ */
|
|
199
|
+
children: [/* @__PURE__ */ y(f, { className: "h-4 w-4 text-primary" }), /* @__PURE__ */ y("span", {
|
|
197
200
|
className: "font-medium",
|
|
198
|
-
children:
|
|
201
|
+
children: U.name
|
|
199
202
|
})]
|
|
200
203
|
}),
|
|
201
|
-
/* @__PURE__ */
|
|
204
|
+
/* @__PURE__ */ b("div", {
|
|
202
205
|
className: "flex justify-between",
|
|
203
|
-
children: [/* @__PURE__ */
|
|
206
|
+
children: [/* @__PURE__ */ y("span", {
|
|
204
207
|
className: "text-muted-foreground",
|
|
205
208
|
children: "Items"
|
|
206
|
-
}), /* @__PURE__ */
|
|
209
|
+
}), /* @__PURE__ */ y("span", {
|
|
207
210
|
className: "font-medium",
|
|
208
|
-
children:
|
|
211
|
+
children: U.items?.length ?? 0
|
|
209
212
|
})]
|
|
210
213
|
}),
|
|
211
|
-
/* @__PURE__ */
|
|
214
|
+
/* @__PURE__ */ b("div", {
|
|
212
215
|
className: "flex justify-between",
|
|
213
|
-
children: [/* @__PURE__ */
|
|
216
|
+
children: [/* @__PURE__ */ y("span", {
|
|
214
217
|
className: "text-muted-foreground",
|
|
215
218
|
children: "Planning focus"
|
|
216
|
-
}), /* @__PURE__ */
|
|
219
|
+
}), /* @__PURE__ */ y(o, { focus: U.planningFocus })]
|
|
217
220
|
}),
|
|
218
|
-
/* @__PURE__ */
|
|
221
|
+
/* @__PURE__ */ b("div", {
|
|
219
222
|
className: "flex justify-between",
|
|
220
|
-
children: [/* @__PURE__ */
|
|
223
|
+
children: [/* @__PURE__ */ y("span", {
|
|
221
224
|
className: "text-muted-foreground",
|
|
222
225
|
children: "Visibility"
|
|
223
|
-
}), /* @__PURE__ */
|
|
226
|
+
}), /* @__PURE__ */ y("span", {
|
|
224
227
|
className: "font-medium",
|
|
225
|
-
children:
|
|
228
|
+
children: U.isPublic ? "Public" : "Private"
|
|
226
229
|
})]
|
|
227
230
|
}),
|
|
228
|
-
/* @__PURE__ */
|
|
231
|
+
/* @__PURE__ */ b("div", {
|
|
229
232
|
className: "flex justify-between",
|
|
230
|
-
children: [/* @__PURE__ */
|
|
233
|
+
children: [/* @__PURE__ */ y("span", {
|
|
231
234
|
className: "text-muted-foreground",
|
|
232
235
|
children: "Owner"
|
|
233
|
-
}), /* @__PURE__ */
|
|
236
|
+
}), /* @__PURE__ */ y("span", {
|
|
234
237
|
className: "font-medium",
|
|
235
|
-
children: /* @__PURE__ */
|
|
238
|
+
children: /* @__PURE__ */ y(n, { id: U.ownerId })
|
|
236
239
|
})]
|
|
237
240
|
}),
|
|
238
|
-
/* @__PURE__ */
|
|
241
|
+
/* @__PURE__ */ b("div", {
|
|
239
242
|
className: "flex justify-between",
|
|
240
|
-
children: [/* @__PURE__ */
|
|
243
|
+
children: [/* @__PURE__ */ y("span", {
|
|
241
244
|
className: "text-muted-foreground",
|
|
242
245
|
children: "Created"
|
|
243
|
-
}), /* @__PURE__ */
|
|
246
|
+
}), /* @__PURE__ */ y("span", { children: new Date(U.createdAt).toLocaleDateString() })]
|
|
244
247
|
}),
|
|
245
|
-
/* @__PURE__ */
|
|
248
|
+
/* @__PURE__ */ b("div", {
|
|
246
249
|
className: "flex justify-between",
|
|
247
|
-
children: [/* @__PURE__ */
|
|
250
|
+
children: [/* @__PURE__ */ y("span", {
|
|
248
251
|
className: "text-muted-foreground",
|
|
249
252
|
children: "Updated"
|
|
250
|
-
}), /* @__PURE__ */
|
|
253
|
+
}), /* @__PURE__ */ y("span", { children: new Date(U.updatedAt).toLocaleDateString() })]
|
|
251
254
|
})
|
|
252
255
|
]
|
|
253
|
-
})] }), /* @__PURE__ */
|
|
254
|
-
label:
|
|
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",
|
|
255
258
|
variant: "outline",
|
|
256
|
-
icon: /* @__PURE__ */
|
|
257
|
-
title: `Delete ${
|
|
259
|
+
icon: /* @__PURE__ */ y(m, { className: "mr-1 h-4 w-4" }),
|
|
260
|
+
title: `Delete ${U.name}?`,
|
|
258
261
|
description: "The product's roadmap and its items are deleted. This cannot be undone here.",
|
|
259
262
|
confirmLabel: "Delete product",
|
|
260
263
|
destructive: !0,
|
|
261
|
-
disabled:
|
|
262
|
-
onConfirm: () => void
|
|
264
|
+
disabled: R,
|
|
265
|
+
onConfirm: () => void X()
|
|
263
266
|
}) })] })]
|
|
264
267
|
})]
|
|
265
268
|
})
|
|
266
269
|
]
|
|
267
|
-
}) : /* @__PURE__ */
|
|
270
|
+
}) : /* @__PURE__ */ y("div", {
|
|
268
271
|
className: "p-6 text-muted-foreground",
|
|
269
272
|
children: "No product found."
|
|
270
273
|
});
|
|
271
274
|
}
|
|
272
275
|
//#endregion
|
|
273
|
-
export {
|
|
276
|
+
export { A as ProductsEditProductPagePage };
|
|
274
277
|
|
|
275
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, Trash2 } from 'lucide-react';\nimport { throwOnError } 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 });\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
|
+
{"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":"ProductsFeatureAnalyticsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeatureAnalyticsPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProductsFeatureAnalyticsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeatureAnalyticsPagePage.tsx"],"names":[],"mappings":"AAgDA,wBAAgB,gCAAgC,4CAiP/C"}
|