@burdenoff/microfe-planmagnet 2026.925.6 → 2026.925.8
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/wspace-types.d.ts +17 -0
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/planmagnet/board/ProjectBoard.d.ts +5 -0
- package/dist/planmagnet/board/ProjectBoard.d.ts.map +1 -0
- package/dist/planmagnet/board/ProjectBoard.js +492 -0
- package/dist/planmagnet/board/ProjectBoard.js.map +1 -0
- package/dist/planmagnet/create/CreateDialogs.d.ts.map +1 -1
- package/dist/planmagnet/create/CreateDialogs.js +118 -117
- package/dist/planmagnet/create/CreateDialogs.js.map +1 -1
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +322 -280
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -1
- 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.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +147 -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 +129 -93
- package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.js +202 -201
- package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
- package/dist/planmagnet/utils/mutationResult.d.ts +16 -0
- package/dist/planmagnet/utils/mutationResult.d.ts.map +1 -0
- package/dist/planmagnet/utils/mutationResult.js +10 -0
- package/dist/planmagnet/utils/mutationResult.js.map +1 -0
- package/package.json +3 -2
|
@@ -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 { 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\n const handleSaveProgress = async () => {\n const parsed = kr.metricType === 'BOOLEAN' ? (draftValue === '1' ? 1 : 0) : Number(draftValue);\n if (Number.isNaN(parsed)) return;\n await updateKeyResult({ variables: { id: kr.id, input: { currentValue: parsed } } });\n onUpdated();\n };\n\n const handleDelete = async () => {\n await deleteKeyResult({ variables: { id: kr.id } });\n onDeleted();\n };\n\n return (\n <div className=\"rounded-md border border-input p-4 space-y-3\">\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\n if (loading) {\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 await 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 setKrForm({ title: '', metricType: 'NUMERIC', targetValue: '100', unit: '' });\n setIsAddingKr(false);\n void refetch();\n };\n\n const handleDeleteObjective = async () => {\n await deleteObjective({ variables: { id: objectiveId } });\n navigate('/goals/goals-page');\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=\"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 } 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 if (loading) {\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TasksCreateTaskPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/TasksCreateTaskPagePage.tsx"],"names":[],"mappings":"AAgDA,wBAAgB,uBAAuB,
|
|
1
|
+
{"version":3,"file":"TasksCreateTaskPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/TasksCreateTaskPagePage.tsx"],"names":[],"mappings":"AAgDA,wBAAgB,uBAAuB,4CAkMtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TasksCreateTaskPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TasksCreateTaskPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, X, CheckSquare } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\ninterface PlanmagnetProjectOption {\n id: string;\n name: string;\n key: string;\n}\n\nconst CREATE_WORK_ITEM = gql`\n mutation CreateWorkItem($input: PlanmagnetCreateWorkItemInput!) {\n planmagnetCreateWorkItem(input: $input) {\n id\n title\n type\n status\n priority\n key\n storyPoints\n dueDate\n labels\n sprintId\n createdAt\n }\n }\n`;\n\n// BOFF-4442/4443 live-verify fix: a WorkItem must belong to a real project\n// (PlanmagnetCreateWorkItemInput.projectId is non-null); the form needs the\n// caller's actual projects to choose from rather than a free-text name.\nconst GET_PROJECTS = gql`\n query GetProjectsForTaskCreate($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n key\n }\n }\n }\n`;\n\nexport function TasksCreateTaskPagePage() {\n const { workspaceId } = usePlanMagnet();\n const [form, setForm] = useState({\n title: '',\n description: '',\n projectId: '',\n priority: 'MEDIUM',\n type: 'STORY',\n storyPoints: '5',\n labels: '',\n sprintId: '',\n dueDate: '',\n });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const { data: projectsData } = useQuery<Record<string, any>>(GET_PROJECTS, {\n variables: { workspaceId: workspaceId ?? 'default' },\n skip: !workspaceId,\n });\n const projects: PlanmagnetProjectOption[] = projectsData?.planmagnetProjects?.items ?? [];\n // Fall back to the first loaded project when the user hasn't picked one\n // yet, without stashing derived state in an effect (avoids the extra\n // render cycle and keeps a single source of truth: `projects`).\n const selectedProjectId = form.projectId || projects[0]?.id || '';\n\n const [createWorkItem, { loading: saving, error }] = useMutation(CREATE_WORK_ITEM, {\n refetchQueries: ['GetWorkItems'],\n });\n\n const handleCreate = async () => {\n if (!form.title.trim() || !selectedProjectId || !workspaceId) return;\n await createWorkItem({\n variables: {\n input: {\n projectId: selectedProjectId,\n title: form.title,\n description: form.description || undefined,\n type: form.type,\n priority: form.priority,\n storyPoints: form.storyPoints ? parseInt(form.storyPoints, 10) : undefined,\n labels: form.labels\n ? form.labels\n .split(',')\n .map((l) => l.trim())\n .filter(Boolean)\n : undefined,\n sprintId: form.sprintId || undefined,\n dueDate: form.dueDate || undefined,\n },\n },\n });\n };\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Create Task</h1>\n <p className=\"text-sm text-muted-foreground\">Add a new task to the project backlog.</p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <X className=\"mr-1 h-4 w-4\" />\n Cancel\n </Button>\n <Button\n size=\"sm\"\n onClick={handleCreate}\n disabled={saving || !form.title.trim() || !selectedProjectId}\n >\n <Save className=\"mr-1 h-4 w-4\" />\n {saving ? 'Creating...' : 'Create Task'}\n </Button>\n </div>\n </div>\n {error && <div className=\"p-6 text-red-500\">Error: {error.message}</div>}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <CheckSquare className=\"h-4 w-4\" />\n Task Details\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Title</label>\n <Input\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. Implement search query parser\"\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-[100px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"Describe the task requirements...\"\n />\n </div>\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Project</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={selectedProjectId}\n onChange={(e) => setForm({ ...form, projectId: e.target.value })}\n >\n {projects.length === 0 && (\n <option value=\"\" disabled>\n No projects available\n </option>\n )}\n {projects.map((p) => (\n <option key={p.id} value={p.id}>\n {p.key} — {p.name}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Priority</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.priority}\n onChange={(e) => setForm({ ...form, priority: e.target.value })}\n >\n {['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'].map((p) => (\n <option key={p} value={p}>\n {p.toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Type</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.type}\n onChange={(e) => setForm({ ...form, type: e.target.value })}\n >\n {['STORY', 'BUG', 'TASK', 'EPIC', 'SUB_TASK'].map((t) => (\n <option key={t} value={t}>\n {t.toLowerCase().replace('_', ' ')}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Story Points</label>\n <Input\n type=\"number\"\n value={form.storyPoints}\n onChange={(e) => setForm({ ...form, storyPoints: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Sprint ID</label>\n <Input\n value={form.sprintId}\n onChange={(e) => setForm({ ...form, sprintId: e.target.value })}\n className=\"mt-1\"\n placeholder=\"Sprint ID\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Due Date</label>\n <Input\n type=\"date\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Labels (comma-separated)</label>\n <Input\n value={form.labels}\n onChange={(e) => setForm({ ...form, labels: e.target.value })}\n className=\"mt-1\"\n placeholder=\"backend, search, performance\"\n />\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TasksCreateTaskPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TasksCreateTaskPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, X, CheckSquare } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\ninterface PlanmagnetProjectOption {\n id: string;\n name: string;\n key: string;\n}\n\nconst CREATE_WORK_ITEM = gql`\n mutation CreateWorkItem($input: PlanmagnetCreateWorkItemInput!) {\n planmagnetCreateWorkItem(input: $input) {\n id\n title\n type\n status\n priority\n key\n storyPoints\n dueDate\n labels\n sprintId\n createdAt\n }\n }\n`;\n\n// BOFF-4442/4443 live-verify fix: a WorkItem must belong to a real project\n// (PlanmagnetCreateWorkItemInput.projectId is non-null); the form needs the\n// caller's actual projects to choose from rather than a free-text name.\nconst GET_PROJECTS = gql`\n query GetProjectsForTaskCreate($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n key\n }\n }\n }\n`;\n\nexport function TasksCreateTaskPagePage() {\n const { workspaceId } = usePlanMagnet();\n const [form, setForm] = useState({\n title: '',\n description: '',\n projectId: '',\n priority: 'MEDIUM',\n type: 'STORY',\n storyPoints: '5',\n labels: '',\n sprintId: '',\n dueDate: '',\n });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const { data: projectsData } = useQuery<Record<string, any>>(GET_PROJECTS, {\n variables: { workspaceId: workspaceId ?? 'default' },\n skip: !workspaceId,\n });\n const projects: PlanmagnetProjectOption[] = projectsData?.planmagnetProjects?.items ?? [];\n // Fall back to the first loaded project when the user hasn't picked one\n // yet, without stashing derived state in an effect (avoids the extra\n // render cycle and keeps a single source of truth: `projects`).\n const selectedProjectId = form.projectId || projects[0]?.id || '';\n\n const [createWorkItem, { loading: saving, error }] = useMutation(CREATE_WORK_ITEM, {\n refetchQueries: ['GetWorkItems'],\n });\n\n const handleCreate = async () => {\n if (!form.title.trim() || !selectedProjectId || !workspaceId) return;\n // mutation-error: shown from the hook's `error`; nothing runs on failure.\n await createWorkItem({\n variables: {\n input: {\n projectId: selectedProjectId,\n title: form.title,\n description: form.description || undefined,\n type: form.type,\n priority: form.priority,\n storyPoints: form.storyPoints ? parseInt(form.storyPoints, 10) : undefined,\n labels: form.labels\n ? form.labels\n .split(',')\n .map((l) => l.trim())\n .filter(Boolean)\n : undefined,\n sprintId: form.sprintId || undefined,\n dueDate: form.dueDate || undefined,\n },\n },\n });\n };\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Create Task</h1>\n <p className=\"text-sm text-muted-foreground\">Add a new task to the project backlog.</p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <X className=\"mr-1 h-4 w-4\" />\n Cancel\n </Button>\n <Button\n size=\"sm\"\n onClick={handleCreate}\n disabled={saving || !form.title.trim() || !selectedProjectId}\n >\n <Save className=\"mr-1 h-4 w-4\" />\n {saving ? 'Creating...' : 'Create Task'}\n </Button>\n </div>\n </div>\n {error && <div className=\"p-6 text-red-500\">Error: {error.message}</div>}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <CheckSquare className=\"h-4 w-4\" />\n Task Details\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Title</label>\n <Input\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. Implement search query parser\"\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-[100px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"Describe the task requirements...\"\n />\n </div>\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Project</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={selectedProjectId}\n onChange={(e) => setForm({ ...form, projectId: e.target.value })}\n >\n {projects.length === 0 && (\n <option value=\"\" disabled>\n No projects available\n </option>\n )}\n {projects.map((p) => (\n <option key={p.id} value={p.id}>\n {p.key} — {p.name}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Priority</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.priority}\n onChange={(e) => setForm({ ...form, priority: e.target.value })}\n >\n {['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'].map((p) => (\n <option key={p} value={p}>\n {p.toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Type</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.type}\n onChange={(e) => setForm({ ...form, type: e.target.value })}\n >\n {['STORY', 'BUG', 'TASK', 'EPIC', 'SUB_TASK'].map((t) => (\n <option key={t} value={t}>\n {t.toLowerCase().replace('_', ' ')}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Story Points</label>\n <Input\n type=\"number\"\n value={form.storyPoints}\n onChange={(e) => setForm({ ...form, storyPoints: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Sprint ID</label>\n <Input\n value={form.sprintId}\n onChange={(e) => setForm({ ...form, sprintId: e.target.value })}\n className=\"mt-1\"\n placeholder=\"Sprint ID\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Due Date</label>\n <Input\n type=\"date\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Labels (comma-separated)</label>\n <Input\n value={form.labels}\n onChange={(e) => setForm({ ...form, labels: e.target.value })}\n className=\"mt-1\"\n placeholder=\"backend, search, performance\"\n />\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TasksEditTaskPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/TasksEditTaskPagePage.tsx"],"names":[],"mappings":"AA8DA,wBAAgB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"TasksEditTaskPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/TasksEditTaskPagePage.tsx"],"names":[],"mappings":"AA8DA,wBAAgB,qBAAqB,4CAoLpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TasksEditTaskPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TasksEditTaskPagePage.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 { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, ArrowLeft } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst GET_WORK_ITEM = gql`\n query GetWorkItem($workItemId: ID!) {\n planmagnetWorkItem(id: $workItemId) {\n id\n title\n description\n type\n status\n priority\n key\n storyPoints\n dueDate\n labels\n tags\n createdAt\n updatedAt\n }\n }\n`;\n\nconst UPDATE_WORK_ITEM = gql`\n mutation UpdateWorkItem($workItemId: ID!, $input: PlanmagnetUpdateWorkItemInput!) {\n planmagnetUpdateWorkItem(id: $workItemId, input: $input) {\n id\n title\n description\n type\n status\n priority\n storyPoints\n dueDate\n labels\n updatedAt\n }\n }\n`;\n\ninterface WorkItemNode {\n id: string;\n title: string;\n description: string | null;\n type: string;\n status: string;\n priority: string;\n key: string;\n storyPoints: number | null;\n dueDate: string | null;\n labels: string[];\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport function TasksEditTaskPagePage() {\n const { workspaceId } = usePlanMagnet();\n\n // In a real app, workItemId comes from the route params.\n // For now we use a placeholder that the host shell should provide.\n const workItemId = new URLSearchParams(window.location.search).get('taskId') ?? '';\n\n const { data, loading, error } = useQuery<{\n planmagnetWorkItem: WorkItemNode;\n }>(GET_WORK_ITEM, {\n variables: { workItemId },\n skip: !workspaceId || !workItemId,\n });\n\n const [updateWorkItem, { loading: saving, error: saveError }] = useMutation(UPDATE_WORK_ITEM, {\n refetchQueries: ['GetWorkItems', 'GetWorkItem'],\n });\n\n const workItem = data?.planmagnetWorkItem;\n\n const defaultForm = useMemo(\n () => ({\n title: workItem?.title ?? '',\n description: workItem?.description ?? '',\n priority: workItem?.priority ?? 'MEDIUM',\n type: workItem?.type ?? 'STORY',\n storyPoints: workItem?.storyPoints?.toString() ?? '0',\n labels: (workItem?.labels ?? []).join(', '),\n dueDate: workItem?.dueDate ?? '',\n }),\n [workItem]\n );\n\n const [formOverrides, setFormOverrides] = useState<Record<string, string>>({});\n const form = { ...defaultForm, ...formOverrides };\n const setForm = (updates: Record<string, string>) => setFormOverrides(updates);\n\n const handleSave = async () => {\n if (!workspaceId || !workItemId) return;\n await updateWorkItem({\n variables: {\n workItemId,\n input: {\n title: form.title,\n description: form.description || undefined,\n priority: form.priority,\n type: form.type,\n storyPoints: form.storyPoints ? parseInt(form.storyPoints, 10) : undefined,\n labels: form.labels\n ? form.labels\n .split(',')\n .map((l) => l.trim())\n .filter(Boolean)\n : undefined,\n dueDate: form.dueDate || undefined,\n },\n },\n });\n };\n\n if (loading) {\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\n if (error) {\n return <div className=\"p-6 text-red-500\">Error: {error.message}</div>;\n }\n\n if (!data?.planmagnetWorkItem) {\n return <div className=\"p-6 text-muted-foreground\">Task not found.</div>;\n }\n\n const task = data.planmagnetWorkItem;\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"sm\">\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Task</h1>\n <p className=\"text-sm text-muted-foreground\">{task.title}</p>\n </div>\n </div>\n <Button size=\"sm\" onClick={handleSave} disabled={saving}>\n <Save className=\"mr-1 h-4 w-4\" />\n {saving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n {saveError && <div className=\"p-6 text-red-500\">Error: {saveError.message}</div>}\n <Card>\n <CardHeader>\n <CardTitle>Task Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Title</label>\n <Input\n value={form.title}\n onChange={(e) => setForm({ ...form, title: 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-[100px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Priority</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.priority}\n onChange={(e) => setForm({ ...form, priority: e.target.value })}\n >\n {['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'].map((p) => (\n <option key={p} value={p}>\n {p.toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Type</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.type}\n onChange={(e) => setForm({ ...form, type: e.target.value })}\n >\n {['STORY', 'BUG', 'TASK', 'EPIC', 'SUB_TASK'].map((t) => (\n <option key={t} value={t}>\n {t.toLowerCase().replace('_', ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Story Points</label>\n <Input\n type=\"number\"\n value={form.storyPoints}\n onChange={(e) => setForm({ ...form, storyPoints: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Due Date</label>\n <Input\n type=\"date\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Labels</label>\n <Input\n value={form.labels}\n onChange={(e) => setForm({ ...form, labels: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TasksEditTaskPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TasksEditTaskPagePage.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 { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, ArrowLeft } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst GET_WORK_ITEM = gql`\n query GetWorkItem($workItemId: ID!) {\n planmagnetWorkItem(id: $workItemId) {\n id\n title\n description\n type\n status\n priority\n key\n storyPoints\n dueDate\n labels\n tags\n createdAt\n updatedAt\n }\n }\n`;\n\nconst UPDATE_WORK_ITEM = gql`\n mutation UpdateWorkItem($workItemId: ID!, $input: PlanmagnetUpdateWorkItemInput!) {\n planmagnetUpdateWorkItem(id: $workItemId, input: $input) {\n id\n title\n description\n type\n status\n priority\n storyPoints\n dueDate\n labels\n updatedAt\n }\n }\n`;\n\ninterface WorkItemNode {\n id: string;\n title: string;\n description: string | null;\n type: string;\n status: string;\n priority: string;\n key: string;\n storyPoints: number | null;\n dueDate: string | null;\n labels: string[];\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport function TasksEditTaskPagePage() {\n const { workspaceId } = usePlanMagnet();\n\n // In a real app, workItemId comes from the route params.\n // For now we use a placeholder that the host shell should provide.\n const workItemId = new URLSearchParams(window.location.search).get('taskId') ?? '';\n\n const { data, loading, error } = useQuery<{\n planmagnetWorkItem: WorkItemNode;\n }>(GET_WORK_ITEM, {\n variables: { workItemId },\n skip: !workspaceId || !workItemId,\n });\n\n const [updateWorkItem, { loading: saving, error: saveError }] = useMutation(UPDATE_WORK_ITEM, {\n refetchQueries: ['GetWorkItems', 'GetWorkItem'],\n });\n\n const workItem = data?.planmagnetWorkItem;\n\n const defaultForm = useMemo(\n () => ({\n title: workItem?.title ?? '',\n description: workItem?.description ?? '',\n priority: workItem?.priority ?? 'MEDIUM',\n type: workItem?.type ?? 'STORY',\n storyPoints: workItem?.storyPoints?.toString() ?? '0',\n labels: (workItem?.labels ?? []).join(', '),\n dueDate: workItem?.dueDate ?? '',\n }),\n [workItem]\n );\n\n const [formOverrides, setFormOverrides] = useState<Record<string, string>>({});\n const form = { ...defaultForm, ...formOverrides };\n const setForm = (updates: Record<string, string>) => setFormOverrides(updates);\n\n const handleSave = async () => {\n if (!workspaceId || !workItemId) return;\n // mutation-error: shown from the hook's `error`; nothing runs on failure.\n await updateWorkItem({\n variables: {\n workItemId,\n input: {\n title: form.title,\n description: form.description || undefined,\n priority: form.priority,\n type: form.type,\n storyPoints: form.storyPoints ? parseInt(form.storyPoints, 10) : undefined,\n labels: form.labels\n ? form.labels\n .split(',')\n .map((l) => l.trim())\n .filter(Boolean)\n : undefined,\n dueDate: form.dueDate || undefined,\n },\n },\n });\n };\n\n if (loading) {\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\n if (error) {\n return <div className=\"p-6 text-red-500\">Error: {error.message}</div>;\n }\n\n if (!data?.planmagnetWorkItem) {\n return <div className=\"p-6 text-muted-foreground\">Task not found.</div>;\n }\n\n const task = data.planmagnetWorkItem;\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"sm\">\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Task</h1>\n <p className=\"text-sm text-muted-foreground\">{task.title}</p>\n </div>\n </div>\n <Button size=\"sm\" onClick={handleSave} disabled={saving}>\n <Save className=\"mr-1 h-4 w-4\" />\n {saving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n {saveError && <div className=\"p-6 text-red-500\">Error: {saveError.message}</div>}\n <Card>\n <CardHeader>\n <CardTitle>Task Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Title</label>\n <Input\n value={form.title}\n onChange={(e) => setForm({ ...form, title: 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-[100px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Priority</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.priority}\n onChange={(e) => setForm({ ...form, priority: e.target.value })}\n >\n {['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'].map((p) => (\n <option key={p} value={p}>\n {p.toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Type</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.type}\n onChange={(e) => setForm({ ...form, type: e.target.value })}\n >\n {['STORY', 'BUG', 'TASK', 'EPIC', 'SUB_TASK'].map((t) => (\n <option key={t} value={t}>\n {t.toLowerCase().replace('_', ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Story Points</label>\n <Input\n type=\"number\"\n value={form.storyPoints}\n onChange={(e) => setForm({ ...form, storyPoints: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Due Date</label>\n <Input\n type=\"date\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Labels</label>\n <Input\n value={form.labels}\n onChange={(e) => setForm({ ...form, labels: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TasksTaskDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/TasksTaskDetailPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TasksTaskDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/TasksTaskDetailPagePage.tsx"],"names":[],"mappings":"AA2RA,wBAAgB,uBAAuB,4CA+JtC"}
|