@burdenoff/microfe-planmagnet 2026.924.5 → 2026.925.1
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/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +191 -172
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +59 -51
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -1
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +115 -105
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.d.ts +39 -0
- package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -0
- package/dist/planmagnet/sprints/SprintControls.js +522 -0
- package/dist/planmagnet/sprints/SprintControls.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SprintsStartSprintPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/SprintsStartSprintPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { Zap, Calendar, Users, Play } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst GET_SPRINTS = gql`\n query GetSprints(\n $workspaceId: ID!\n $filter: PlanmagnetSprintFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetSprints(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n name\n goal\n status\n startDate\n endDate\n velocity\n workItems {\n id\n title\n status\n storyPoints\n assigneeId\n }\n }\n totalCount\n }\n }\n`;\n\nconst START_SPRINT = gql`\n mutation StartSprint($sprintId: ID!) {\n planmagnetStartSprint(id: $sprintId) {\n id\n status\n startDate\n }\n }\n`;\n\ninterface SprintWorkItem {\n id: string;\n title: string;\n status: string;\n storyPoints: number | null;\n assigneeId: string | null;\n}\n\ninterface SprintNode {\n id: string;\n name: string;\n goal: string | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n velocity: number | null;\n workItems: SprintWorkItem[];\n}\n\ninterface SprintMetrics {\n totalPoints: number;\n completedPoints: number;\n tasksTotal: number;\n tasksCompleted: number;\n teamMemberIds: string[];\n velocity: number;\n}\n\nfunction deriveSprintMetrics(sprint: SprintNode): SprintMetrics {\n const items = sprint.workItems ?? [];\n const totalPoints = items.reduce((sum, w) => sum + (w.storyPoints ?? 0), 0);\n const completedPoints = items\n .filter((w) => w.status === 'DONE')\n .reduce((sum, w) => sum + (w.storyPoints ?? 0), 0);\n const tasksTotal = items.length;\n const tasksCompleted = items.filter((w) => w.status === 'DONE').length;\n const teamMemberIds = Array.from(\n new Set(items.map((w) => w.assigneeId).filter((id): id is string => Boolean(id)))\n );\n const velocity = sprint.velocity ?? completedPoints;\n return { totalPoints, completedPoints, tasksTotal, tasksCompleted, teamMemberIds, velocity };\n}\n\nexport function SprintsStartSprintPagePage() {\n const { workspaceId } = usePlanMagnet();\n\n const {\n data: sprintData,\n loading: sprintsLoading,\n error: sprintsError,\n } = useQuery<{\n planmagnetSprints: { items: SprintNode[]; totalCount: number };\n }>(GET_SPRINTS, {\n variables: { workspaceId, pagination: { first: 50 } },\n skip: !workspaceId,\n });\n\n const sprints = sprintData?.planmagnetSprints?.items ?? [];\n const activeSprints = sprints.filter((s) => s.status === 'ACTIVE');\n const planningSprints = sprints.filter((s) => s.status === 'PLANNING');\n\n const [startSprint, { loading: startingSprintLoading }] = useMutation(START_SPRINT, {\n refetchQueries: ['GetSprints'],\n });\n\n const handleStartSprint = async (sprintId: string) => {\n if (!workspaceId) return;\n await startSprint({ variables: { sprintId } });\n };\n\n if (sprintsLoading) {\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 (sprintsError) {\n return <div className=\"p-6 text-red-500\">Error: {sprintsError.message}</div>;\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Sprint Management</h1>\n <p className=\"text-sm text-muted-foreground\">\n {sprints.length} sprints total · {activeSprints.length} active\n </p>\n </div>\n\n {activeSprints.length > 0 && (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-medium\">Active Sprints</h2>\n {activeSprints.map((sprint) => {\n const metrics = deriveSprintMetrics(sprint);\n const tasksForSprint = sprint.workItems ?? [];\n const pctDone =\n metrics.totalPoints > 0\n ? Math.round((metrics.completedPoints / metrics.totalPoints) * 100)\n : 0;\n return (\n <Card key={sprint.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <Zap className=\"h-4 w-4 text-primary\" />\n {sprint.name}\n </CardTitle>\n <Badge>Active</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm text-muted-foreground\">{sprint.goal ?? ''}</p>\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n {sprint.startDate ?? '?'} - {sprint.endDate ?? '?'}\n </span>\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n {metrics.teamMemberIds.length} members\n </span>\n </div>\n <div className=\"grid grid-cols-3 gap-4 text-center\">\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">\n {metrics.completedPoints}/{metrics.totalPoints}\n </p>\n <p className=\"text-xs text-muted-foreground\">Points</p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">\n {metrics.tasksCompleted}/{metrics.tasksTotal}\n </p>\n <p className=\"text-xs text-muted-foreground\">Tasks</p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">{metrics.velocity}</p>\n <p className=\"text-xs text-muted-foreground\">Velocity</p>\n </div>\n </div>\n <div>\n <div className=\"flex justify-between text-xs mb-1\">\n <span className=\"text-muted-foreground\">Sprint Progress</span>\n <span className=\"font-medium\">{pctDone}%</span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary rounded-full\"\n style={{ width: `${pctDone}%` }}\n />\n </div>\n </div>\n {tasksForSprint.length > 0 && (\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-2\">\n Sprint Backlog\n </p>\n <div className=\"space-y-1\">\n {tasksForSprint.map((t) => (\n <div\n key={t.id}\n className=\"flex items-center justify-between border rounded p-2 text-sm\"\n >\n <span>{t.title}</span>\n <div className=\"flex gap-1\">\n <Badge variant=\"secondary\" className=\"text-xs capitalize\">\n {t.status.toLowerCase().replace('_', ' ')}\n </Badge>\n <span className=\"text-xs text-muted-foreground\">\n {t.storyPoints ?? 0}pts\n </span>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">Team</p>\n <div className=\"flex gap-2\">\n {metrics.teamMemberIds.map((memberId) => (\n <div\n key={memberId}\n className=\"h-7 w-7 rounded-full bg-primary/10 flex items-center justify-center text-[10px] font-medium text-primary\"\n title={memberId}\n >\n {memberId.slice(0, 2).toUpperCase()}\n </div>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n )}\n\n {planningSprints.length > 0 && (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-medium\">Planning</h2>\n {planningSprints.map((sprint) => {\n const metrics = deriveSprintMetrics(sprint);\n return (\n <Card key={sprint.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{sprint.name}</CardTitle>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\">Planning</Badge>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => handleStartSprint(sprint.id)}\n disabled={startingSprintLoading}\n >\n <Play className=\"mr-1 h-3 w-3\" />\n Start Sprint\n </Button>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">{sprint.goal ?? ''}</p>\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n <span>\n {sprint.startDate ?? '?'} - {sprint.endDate ?? '?'}\n </span>\n <span>{metrics.totalPoints} points planned</span>\n <span>{metrics.teamMemberIds.length} members</span>\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SprintsStartSprintPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/SprintsStartSprintPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Zap, Calendar, Users } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { NewSprintDialog, SprintLifecycleActions } from '../sprints/SprintControls';\n\nconst GET_SPRINTS = gql`\n query GetSprints(\n $workspaceId: ID!\n $filter: PlanmagnetSprintFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetSprints(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n name\n goal\n status\n startDate\n endDate\n velocity\n workItems {\n id\n title\n status\n storyPoints\n assigneeId\n }\n }\n totalCount\n }\n }\n`;\n\ninterface SprintWorkItem {\n id: string;\n title: string;\n status: string;\n storyPoints: number | null;\n assigneeId: string | null;\n}\n\ninterface SprintNode {\n id: string;\n name: string;\n goal: string | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n velocity: number | null;\n workItems: SprintWorkItem[];\n}\n\ninterface SprintMetrics {\n totalPoints: number;\n completedPoints: number;\n tasksTotal: number;\n tasksCompleted: number;\n teamMemberIds: string[];\n velocity: number;\n}\n\nfunction deriveSprintMetrics(sprint: SprintNode): SprintMetrics {\n const items = sprint.workItems ?? [];\n const totalPoints = items.reduce((sum, w) => sum + (w.storyPoints ?? 0), 0);\n const completedPoints = items\n .filter((w) => w.status === 'DONE')\n .reduce((sum, w) => sum + (w.storyPoints ?? 0), 0);\n const tasksTotal = items.length;\n const tasksCompleted = items.filter((w) => w.status === 'DONE').length;\n const teamMemberIds = Array.from(\n new Set(items.map((w) => w.assigneeId).filter((id): id is string => Boolean(id)))\n );\n const velocity = sprint.velocity ?? completedPoints;\n return { totalPoints, completedPoints, tasksTotal, tasksCompleted, teamMemberIds, velocity };\n}\n\nexport function SprintsStartSprintPagePage() {\n const { workspaceId } = usePlanMagnet();\n\n const {\n data: sprintData,\n loading: sprintsLoading,\n error: sprintsError,\n } = useQuery<{\n planmagnetSprints: { items: SprintNode[]; totalCount: number };\n }>(GET_SPRINTS, {\n variables: { workspaceId, pagination: { first: 50 } },\n skip: !workspaceId,\n });\n\n const sprints = sprintData?.planmagnetSprints?.items ?? [];\n // REVIEW is still in flight (it can be completed or cancelled), so it\n // belongs with the active sprints rather than vanishing from this page.\n const activeSprints = sprints.filter((s) => s.status === 'ACTIVE' || s.status === 'REVIEW');\n const planningSprints = sprints.filter((s) => s.status === 'PLANNING');\n\n const navigate = usePlanMagnetNavigate();\n const openSprint = (id: string) => navigate(detailPath('/projects/planning-detail-page', id));\n\n if (sprintsLoading) {\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 (sprintsError) {\n return <div className=\"p-6 text-red-500\">Error: {sprintsError.message}</div>;\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Sprint Management</h1>\n <p className=\"text-sm text-muted-foreground\">\n {sprints.length} sprints total · {activeSprints.length} active\n </p>\n </div>\n <NewSprintDialog onCreated={openSprint} />\n </div>\n\n {activeSprints.length > 0 && (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-medium\">Active Sprints</h2>\n {activeSprints.map((sprint) => {\n const metrics = deriveSprintMetrics(sprint);\n const tasksForSprint = sprint.workItems ?? [];\n const pctDone =\n metrics.totalPoints > 0\n ? Math.round((metrics.completedPoints / metrics.totalPoints) * 100)\n : 0;\n return (\n <Card key={sprint.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <Zap className=\"h-4 w-4 text-primary\" />\n <button\n type=\"button\"\n className=\"hover:underline text-left\"\n onClick={() => openSprint(sprint.id)}\n >\n {sprint.name}\n </button>\n </CardTitle>\n <div className=\"flex items-start gap-2\">\n <Badge>{sprint.status === 'REVIEW' ? 'Review' : 'Active'}</Badge>\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm text-muted-foreground\">{sprint.goal ?? ''}</p>\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n {sprint.startDate ?? '?'} - {sprint.endDate ?? '?'}\n </span>\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n {metrics.teamMemberIds.length} members\n </span>\n </div>\n <div className=\"grid grid-cols-3 gap-4 text-center\">\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">\n {metrics.completedPoints}/{metrics.totalPoints}\n </p>\n <p className=\"text-xs text-muted-foreground\">Points</p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">\n {metrics.tasksCompleted}/{metrics.tasksTotal}\n </p>\n <p className=\"text-xs text-muted-foreground\">Tasks</p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">{metrics.velocity}</p>\n <p className=\"text-xs text-muted-foreground\">Velocity</p>\n </div>\n </div>\n <div>\n <div className=\"flex justify-between text-xs mb-1\">\n <span className=\"text-muted-foreground\">Sprint Progress</span>\n <span className=\"font-medium\">{pctDone}%</span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary rounded-full\"\n style={{ width: `${pctDone}%` }}\n />\n </div>\n </div>\n {tasksForSprint.length > 0 && (\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-2\">\n Sprint Backlog\n </p>\n <div className=\"space-y-1\">\n {tasksForSprint.map((t) => (\n <div\n key={t.id}\n className=\"flex items-center justify-between border rounded p-2 text-sm\"\n >\n <span>{t.title}</span>\n <div className=\"flex gap-1\">\n <Badge variant=\"secondary\" className=\"text-xs capitalize\">\n {t.status.toLowerCase().replace('_', ' ')}\n </Badge>\n <span className=\"text-xs text-muted-foreground\">\n {t.storyPoints ?? 0}pts\n </span>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">Team</p>\n <div className=\"flex gap-2\">\n {metrics.teamMemberIds.map((memberId) => (\n <div\n key={memberId}\n className=\"h-7 w-7 rounded-full bg-primary/10 flex items-center justify-center text-[10px] font-medium text-primary\"\n title={memberId}\n >\n {memberId.slice(0, 2).toUpperCase()}\n </div>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n )}\n\n {planningSprints.length > 0 && (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-medium\">Planning</h2>\n {planningSprints.map((sprint) => {\n const metrics = deriveSprintMetrics(sprint);\n return (\n <Card key={sprint.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">\n <button\n type=\"button\"\n className=\"hover:underline text-left\"\n onClick={() => openSprint(sprint.id)}\n >\n {sprint.name}\n </button>\n </CardTitle>\n <div className=\"flex items-start gap-2\">\n <Badge variant=\"secondary\">Planning</Badge>\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">{sprint.goal ?? ''}</p>\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n <span>\n {sprint.startDate ?? '?'} - {sprint.endDate ?? '?'}\n </span>\n <span>{metrics.totalPoints} points planned</span>\n <span>{metrics.teamMemberIds.length} members</span>\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprint lifecycle + membership controls.
|
|
3
|
+
*
|
|
4
|
+
* The buttons mirror the service's transition table exactly
|
|
5
|
+
* (wspace-planmagnet-svc sprint.service.ts `TRANSITIONS`):
|
|
6
|
+
* start PLANNING -> ACTIVE
|
|
7
|
+
* complete ACTIVE | REVIEW -> COMPLETED
|
|
8
|
+
* cancel PLANNING|ACTIVE|REVIEW -> CANCELLED
|
|
9
|
+
* A COMPLETED or CANCELLED sprint is finished: no transitions, and its work
|
|
10
|
+
* items can no longer be added or removed. The server enforces all of this;
|
|
11
|
+
* the UI only hides what would be refused, and still surfaces a refusal (a
|
|
12
|
+
* concurrent change can make a shown button stale).
|
|
13
|
+
*/
|
|
14
|
+
export type SprintStatus = 'PLANNING' | 'ACTIVE' | 'REVIEW' | 'COMPLETED' | 'CANCELLED';
|
|
15
|
+
export type SprintAction = 'start' | 'complete' | 'cancel';
|
|
16
|
+
export declare function sprintActionsFor(status: string): SprintAction[];
|
|
17
|
+
export declare function isSprintFinished(status: string): boolean;
|
|
18
|
+
export declare function SprintLifecycleActions({ sprintId, sprintName, status, }: {
|
|
19
|
+
sprintId: string;
|
|
20
|
+
sprintName: string;
|
|
21
|
+
status: string;
|
|
22
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
23
|
+
export interface SprintWorkItem {
|
|
24
|
+
id: string;
|
|
25
|
+
key: string;
|
|
26
|
+
title?: string | null;
|
|
27
|
+
status: string;
|
|
28
|
+
storyPoints: number | null;
|
|
29
|
+
}
|
|
30
|
+
export declare function SprintWorkItemsPanel({ sprintId, projectId, status, items, }: {
|
|
31
|
+
sprintId: string;
|
|
32
|
+
projectId: string;
|
|
33
|
+
status: string;
|
|
34
|
+
items: SprintWorkItem[];
|
|
35
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
export declare function NewSprintDialog({ onCreated }: {
|
|
37
|
+
onCreated: (sprintId: string) => void;
|
|
38
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
//# sourceMappingURL=SprintControls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SprintControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/sprints/SprintControls.tsx"],"names":[],"mappings":"AA4BA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;AACxF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAQ3D,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAI/D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAExD;AAsCD,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,UAAU,EACV,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB,kDAyDA;AAsFD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAQD,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,SAAS,EACT,MAAM,EACN,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB,2CAyEA;AAmKD,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,2CAqKvF"}
|
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { useDeferredValue as t, useState as n } from "react";
|
|
3
|
+
import { CheckCircle2 as r, Play as i, Plus as a, X as o, XCircle as s } from "lucide-react";
|
|
4
|
+
import { gql as c } from "@apollo/client";
|
|
5
|
+
import { useMutation as l, useQuery as u } from "@apollo/client/react";
|
|
6
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
7
|
+
import { AlertDialog as p, AlertDialogAction as m, AlertDialogCancel as h, AlertDialogContent as g, AlertDialogDescription as _, AlertDialogFooter as v, AlertDialogHeader as y, AlertDialogTitle as b, AlertDialogTrigger as x, Badge as S, Button as C, Checkbox as w, Input as T, ResponsiveDialog as E, ResponsiveDialogContent as D, ResponsiveDialogDescription as O, ResponsiveDialogFooter as k, ResponsiveDialogHeader as A, ResponsiveDialogTitle as j, ResponsiveDialogTrigger as M } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
//#region src/planmagnet/sprints/SprintControls.tsx
|
|
9
|
+
var N = {
|
|
10
|
+
start: ["PLANNING"],
|
|
11
|
+
complete: ["ACTIVE", "REVIEW"],
|
|
12
|
+
cancel: [
|
|
13
|
+
"PLANNING",
|
|
14
|
+
"ACTIVE",
|
|
15
|
+
"REVIEW"
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
function P(e) {
|
|
19
|
+
return Object.keys(N).filter((t) => N[t].includes(e));
|
|
20
|
+
}
|
|
21
|
+
function F(e) {
|
|
22
|
+
return e === "COMPLETED" || e === "CANCELLED";
|
|
23
|
+
}
|
|
24
|
+
var I = [
|
|
25
|
+
"GetSprints",
|
|
26
|
+
"GetSprintDetail",
|
|
27
|
+
"GetSprintForEdit",
|
|
28
|
+
"GetDashboardData"
|
|
29
|
+
], L = c`
|
|
30
|
+
mutation SprintControlsStart($id: ID!) {
|
|
31
|
+
planmagnetStartSprint(id: $id) {
|
|
32
|
+
id
|
|
33
|
+
status
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`, R = c`
|
|
37
|
+
mutation SprintControlsComplete($id: ID!) {
|
|
38
|
+
planmagnetCompleteSprint(id: $id) {
|
|
39
|
+
id
|
|
40
|
+
status
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`, z = c`
|
|
44
|
+
mutation SprintControlsCancel($id: ID!) {
|
|
45
|
+
planmagnetCancelSprint(id: $id) {
|
|
46
|
+
id
|
|
47
|
+
status
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
function B({ message: e }) {
|
|
52
|
+
return /* @__PURE__ */ d("p", {
|
|
53
|
+
role: "alert",
|
|
54
|
+
className: "text-sm text-destructive",
|
|
55
|
+
children: e
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function V({ sprintId: e, sprintName: t, status: a }) {
|
|
59
|
+
let o = {
|
|
60
|
+
refetchQueries: I,
|
|
61
|
+
awaitRefetchQueries: !0
|
|
62
|
+
}, [c, u] = l(L, o), [p, m] = l(R, o), [h, g] = l(z, o), [_, v] = n(null), y = u.loading || m.loading || g.loading, b = P(a);
|
|
63
|
+
if (b.length === 0) return null;
|
|
64
|
+
let x = async (t) => {
|
|
65
|
+
v(null);
|
|
66
|
+
try {
|
|
67
|
+
await t({ variables: { id: e } });
|
|
68
|
+
} catch (e) {
|
|
69
|
+
v(e instanceof Error ? e.message : "The sprint could not be updated.");
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
return /* @__PURE__ */ f("div", {
|
|
73
|
+
className: "flex flex-col items-end gap-2",
|
|
74
|
+
"data-testid": "sprint-lifecycle-actions",
|
|
75
|
+
children: [/* @__PURE__ */ f("div", {
|
|
76
|
+
className: "flex gap-2",
|
|
77
|
+
children: [
|
|
78
|
+
b.includes("start") && /* @__PURE__ */ f(C, {
|
|
79
|
+
size: "sm",
|
|
80
|
+
onClick: () => x(c),
|
|
81
|
+
disabled: y,
|
|
82
|
+
children: [/* @__PURE__ */ d(i, { className: "mr-1 h-4 w-4" }), "Start sprint"]
|
|
83
|
+
}),
|
|
84
|
+
b.includes("complete") && /* @__PURE__ */ d(H, {
|
|
85
|
+
label: "Complete sprint",
|
|
86
|
+
icon: /* @__PURE__ */ d(r, { className: "mr-1 h-4 w-4" }),
|
|
87
|
+
title: `Complete ${t}?`,
|
|
88
|
+
description: "The sprint is closed and its work items can no longer be changed from it. This cannot be undone.",
|
|
89
|
+
confirmLabel: "Complete sprint",
|
|
90
|
+
disabled: y,
|
|
91
|
+
onConfirm: () => x(p)
|
|
92
|
+
}),
|
|
93
|
+
b.includes("cancel") && /* @__PURE__ */ d(H, {
|
|
94
|
+
label: "Cancel sprint",
|
|
95
|
+
variant: "outline",
|
|
96
|
+
icon: /* @__PURE__ */ d(s, { className: "mr-1 h-4 w-4" }),
|
|
97
|
+
title: `Cancel ${t}?`,
|
|
98
|
+
description: "The sprint is abandoned and cannot be restarted. Its work items stay in it for the record. This cannot be undone.",
|
|
99
|
+
confirmLabel: "Cancel sprint",
|
|
100
|
+
destructive: !0,
|
|
101
|
+
disabled: y,
|
|
102
|
+
onConfirm: () => x(h)
|
|
103
|
+
})
|
|
104
|
+
]
|
|
105
|
+
}), _ && /* @__PURE__ */ d(B, { message: _ })]
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function H({ label: e, icon: t, title: n, description: r, confirmLabel: i, onConfirm: a, disabled: o, destructive: s = !1, variant: c = "default" }) {
|
|
109
|
+
return /* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(x, {
|
|
110
|
+
asChild: !0,
|
|
111
|
+
children: /* @__PURE__ */ f(C, {
|
|
112
|
+
size: "sm",
|
|
113
|
+
variant: c,
|
|
114
|
+
disabled: o,
|
|
115
|
+
children: [t, e]
|
|
116
|
+
})
|
|
117
|
+
}), /* @__PURE__ */ f(g, { children: [/* @__PURE__ */ f(y, { children: [/* @__PURE__ */ d(b, { children: n }), /* @__PURE__ */ d(_, { children: r })] }), /* @__PURE__ */ f(v, { children: [/* @__PURE__ */ d(h, { children: "Keep it" }), /* @__PURE__ */ d(m, {
|
|
118
|
+
onClick: a,
|
|
119
|
+
className: s ? "bg-destructive text-destructive-foreground hover:bg-destructive/90" : "",
|
|
120
|
+
children: i
|
|
121
|
+
})] })] })] });
|
|
122
|
+
}
|
|
123
|
+
var U = c`
|
|
124
|
+
mutation SprintControlsAddItems($id: ID!, $input: PlanmagnetSprintWorkItemsInput!) {
|
|
125
|
+
planmagnetAddWorkItemsToSprint(id: $id, input: $input) {
|
|
126
|
+
id
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
`, W = c`
|
|
130
|
+
mutation SprintControlsRemoveItems($id: ID!, $input: PlanmagnetSprintWorkItemsInput!) {
|
|
131
|
+
planmagnetRemoveWorkItemsFromSprint(id: $id, input: $input) {
|
|
132
|
+
id
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
`, G = c`
|
|
136
|
+
query SprintControlsCandidates($workspaceId: ID!, $filter: PlanmagnetWorkItemFilter) {
|
|
137
|
+
planmagnetWorkItems(workspaceId: $workspaceId, filter: $filter, pagination: { first: 100 }) {
|
|
138
|
+
totalCount
|
|
139
|
+
items {
|
|
140
|
+
id
|
|
141
|
+
key
|
|
142
|
+
title
|
|
143
|
+
status
|
|
144
|
+
storyPoints
|
|
145
|
+
sprintId
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
`, K = (e) => e.toLowerCase().replace(/_/g, " ");
|
|
150
|
+
function q({ sprintId: e, projectId: t, status: r, items: i }) {
|
|
151
|
+
let a = F(r), [s, c] = l(W, {
|
|
152
|
+
refetchQueries: I,
|
|
153
|
+
awaitRefetchQueries: !0
|
|
154
|
+
}), [u, p] = n(null), [m, h] = n(null), g = async (t) => {
|
|
155
|
+
p(null), h(t);
|
|
156
|
+
try {
|
|
157
|
+
await s({ variables: {
|
|
158
|
+
id: e,
|
|
159
|
+
input: { workItemIds: [t] }
|
|
160
|
+
} });
|
|
161
|
+
} catch (e) {
|
|
162
|
+
p(e instanceof Error ? e.message : "The work item could not be removed.");
|
|
163
|
+
} finally {
|
|
164
|
+
h(null);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
return /* @__PURE__ */ f("div", {
|
|
168
|
+
className: "space-y-3",
|
|
169
|
+
"data-testid": "sprint-work-items",
|
|
170
|
+
children: [
|
|
171
|
+
/* @__PURE__ */ f("div", {
|
|
172
|
+
className: "flex items-center justify-between",
|
|
173
|
+
children: [/* @__PURE__ */ f("p", {
|
|
174
|
+
className: "text-sm text-muted-foreground",
|
|
175
|
+
children: [
|
|
176
|
+
i.length,
|
|
177
|
+
" work item",
|
|
178
|
+
i.length === 1 ? "" : "s"
|
|
179
|
+
]
|
|
180
|
+
}), !a && /* @__PURE__ */ d(J, {
|
|
181
|
+
sprintId: e,
|
|
182
|
+
projectId: t
|
|
183
|
+
})]
|
|
184
|
+
}),
|
|
185
|
+
a && /* @__PURE__ */ f("p", {
|
|
186
|
+
className: "text-xs text-muted-foreground",
|
|
187
|
+
children: [
|
|
188
|
+
"This sprint is ",
|
|
189
|
+
K(r),
|
|
190
|
+
"; its work items can no longer be changed."
|
|
191
|
+
]
|
|
192
|
+
}),
|
|
193
|
+
u && /* @__PURE__ */ d(B, { message: u }),
|
|
194
|
+
i.length === 0 ? /* @__PURE__ */ d("p", {
|
|
195
|
+
className: "text-sm text-muted-foreground text-center py-4",
|
|
196
|
+
children: "No work items in this sprint yet."
|
|
197
|
+
}) : /* @__PURE__ */ d("ul", {
|
|
198
|
+
className: "space-y-1",
|
|
199
|
+
children: i.map((e) => /* @__PURE__ */ f("li", {
|
|
200
|
+
className: "flex items-center justify-between gap-2 border rounded-md p-2 text-sm",
|
|
201
|
+
children: [/* @__PURE__ */ f("span", {
|
|
202
|
+
className: "min-w-0 truncate",
|
|
203
|
+
children: [/* @__PURE__ */ d("span", {
|
|
204
|
+
className: "text-muted-foreground mr-2",
|
|
205
|
+
children: e.key
|
|
206
|
+
}), e.title]
|
|
207
|
+
}), /* @__PURE__ */ f("span", {
|
|
208
|
+
className: "flex items-center gap-2 shrink-0",
|
|
209
|
+
children: [
|
|
210
|
+
/* @__PURE__ */ d(S, {
|
|
211
|
+
variant: "secondary",
|
|
212
|
+
className: "text-xs capitalize",
|
|
213
|
+
children: K(e.status)
|
|
214
|
+
}),
|
|
215
|
+
/* @__PURE__ */ f("span", {
|
|
216
|
+
className: "text-xs text-muted-foreground",
|
|
217
|
+
children: [e.storyPoints ?? 0, " pts"]
|
|
218
|
+
}),
|
|
219
|
+
!a && /* @__PURE__ */ d(C, {
|
|
220
|
+
size: "sm",
|
|
221
|
+
variant: "ghost",
|
|
222
|
+
"aria-label": `Remove ${e.key} from sprint`,
|
|
223
|
+
disabled: c.loading,
|
|
224
|
+
onClick: () => g(e.id),
|
|
225
|
+
children: m === e.id ? "…" : /* @__PURE__ */ d(o, { className: "h-4 w-4" })
|
|
226
|
+
})
|
|
227
|
+
]
|
|
228
|
+
})]
|
|
229
|
+
}, e.id))
|
|
230
|
+
})
|
|
231
|
+
]
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
function J({ sprintId: r, projectId: i }) {
|
|
235
|
+
let { workspaceId: o } = e(), [s, c] = n(!1), [p, m] = n(""), h = t(p.trim()), [g, _] = n(/* @__PURE__ */ new Set()), [v, y] = n(null), { data: b, loading: x } = u(G, {
|
|
236
|
+
variables: {
|
|
237
|
+
workspaceId: o ?? "",
|
|
238
|
+
filter: {
|
|
239
|
+
projectId: i,
|
|
240
|
+
...h ? { search: h } : {}
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
skip: !s || !o,
|
|
244
|
+
fetchPolicy: "network-only"
|
|
245
|
+
}), [N, P] = l(U, {
|
|
246
|
+
refetchQueries: I,
|
|
247
|
+
awaitRefetchQueries: !0
|
|
248
|
+
}), F = (b?.planmagnetWorkItems.items ?? []).filter((e) => e.sprintId !== r), L = b?.planmagnetWorkItems.totalCount ?? 0, R = (e, t) => _((n) => {
|
|
249
|
+
let r = new Set(n);
|
|
250
|
+
return t ? r.add(e) : r.delete(e), r;
|
|
251
|
+
}), z = () => {
|
|
252
|
+
_(/* @__PURE__ */ new Set()), m(""), y(null);
|
|
253
|
+
};
|
|
254
|
+
return /* @__PURE__ */ f(E, {
|
|
255
|
+
open: s,
|
|
256
|
+
onOpenChange: (e) => {
|
|
257
|
+
c(e), e || z();
|
|
258
|
+
},
|
|
259
|
+
children: [/* @__PURE__ */ d(M, {
|
|
260
|
+
asChild: !0,
|
|
261
|
+
children: /* @__PURE__ */ f(C, {
|
|
262
|
+
size: "sm",
|
|
263
|
+
variant: "outline",
|
|
264
|
+
children: [/* @__PURE__ */ d(a, { className: "mr-1 h-4 w-4" }), "Add work items"]
|
|
265
|
+
})
|
|
266
|
+
}), /* @__PURE__ */ f(D, {
|
|
267
|
+
className: "max-w-lg",
|
|
268
|
+
children: [
|
|
269
|
+
/* @__PURE__ */ f(A, { children: [/* @__PURE__ */ d(j, { children: "Add work items" }), /* @__PURE__ */ d(O, { children: "Work items from this sprint's project. An item already in another sprint moves here." })] }),
|
|
270
|
+
/* @__PURE__ */ d(T, {
|
|
271
|
+
value: p,
|
|
272
|
+
onChange: (e) => m(e.target.value),
|
|
273
|
+
placeholder: "Search by title",
|
|
274
|
+
"aria-label": "Search work items"
|
|
275
|
+
}),
|
|
276
|
+
/* @__PURE__ */ f("div", {
|
|
277
|
+
className: "max-h-80 overflow-y-auto space-y-1",
|
|
278
|
+
"data-testid": "sprint-add-candidates",
|
|
279
|
+
children: [
|
|
280
|
+
x && /* @__PURE__ */ d("p", {
|
|
281
|
+
className: "text-sm text-muted-foreground py-4 text-center",
|
|
282
|
+
children: "Loading…"
|
|
283
|
+
}),
|
|
284
|
+
!x && F.length === 0 && /* @__PURE__ */ d("p", {
|
|
285
|
+
className: "text-sm text-muted-foreground py-4 text-center",
|
|
286
|
+
children: h ? "No matching work items." : "Every work item in this project is already in this sprint."
|
|
287
|
+
}),
|
|
288
|
+
F.map((e) => {
|
|
289
|
+
let t = `sprint-add-${e.id}`;
|
|
290
|
+
return /* @__PURE__ */ f("label", {
|
|
291
|
+
htmlFor: t,
|
|
292
|
+
className: "flex items-center gap-3 border rounded-md p-2 text-sm cursor-pointer hover:bg-muted",
|
|
293
|
+
children: [
|
|
294
|
+
/* @__PURE__ */ d(w, {
|
|
295
|
+
id: t,
|
|
296
|
+
checked: g.has(e.id),
|
|
297
|
+
onCheckedChange: (t) => R(e.id, t === !0)
|
|
298
|
+
}),
|
|
299
|
+
/* @__PURE__ */ f("span", {
|
|
300
|
+
className: "min-w-0 flex-1 truncate",
|
|
301
|
+
children: [/* @__PURE__ */ d("span", {
|
|
302
|
+
className: "text-muted-foreground mr-2",
|
|
303
|
+
children: e.key
|
|
304
|
+
}), e.title]
|
|
305
|
+
}),
|
|
306
|
+
e.sprintId && /* @__PURE__ */ d("span", {
|
|
307
|
+
className: "text-xs text-muted-foreground shrink-0",
|
|
308
|
+
children: "in another sprint"
|
|
309
|
+
}),
|
|
310
|
+
/* @__PURE__ */ d(S, {
|
|
311
|
+
variant: "secondary",
|
|
312
|
+
className: "text-xs capitalize shrink-0",
|
|
313
|
+
children: K(e.status)
|
|
314
|
+
})
|
|
315
|
+
]
|
|
316
|
+
}, e.id);
|
|
317
|
+
}),
|
|
318
|
+
L > 100 && /* @__PURE__ */ f("p", {
|
|
319
|
+
className: "text-xs text-muted-foreground pt-2",
|
|
320
|
+
children: [
|
|
321
|
+
"Showing the first 100 of ",
|
|
322
|
+
L,
|
|
323
|
+
". Search to narrow the list."
|
|
324
|
+
]
|
|
325
|
+
})
|
|
326
|
+
]
|
|
327
|
+
}),
|
|
328
|
+
v && /* @__PURE__ */ d(B, { message: v }),
|
|
329
|
+
/* @__PURE__ */ d(k, { children: /* @__PURE__ */ d(C, {
|
|
330
|
+
onClick: async () => {
|
|
331
|
+
y(null);
|
|
332
|
+
try {
|
|
333
|
+
await N({ variables: {
|
|
334
|
+
id: r,
|
|
335
|
+
input: { workItemIds: [...g] }
|
|
336
|
+
} }), z(), c(!1);
|
|
337
|
+
} catch (e) {
|
|
338
|
+
y(e instanceof Error ? e.message : "The work items could not be added.");
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
disabled: g.size === 0 || P.loading,
|
|
342
|
+
"data-testid": "sprint-add-confirm",
|
|
343
|
+
children: P.loading ? "Adding…" : `Add ${g.size} work item${g.size === 1 ? "" : "s"}`
|
|
344
|
+
}) })
|
|
345
|
+
]
|
|
346
|
+
})]
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
var Y = c`
|
|
350
|
+
mutation SprintControlsCreate($input: PlanmagnetCreateSprintInput!) {
|
|
351
|
+
planmagnetCreateSprint(input: $input) {
|
|
352
|
+
id
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
`, X = c`
|
|
356
|
+
query SprintControlsProjects($workspaceId: ID!) {
|
|
357
|
+
planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {
|
|
358
|
+
items {
|
|
359
|
+
id
|
|
360
|
+
name
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
`, Z = {
|
|
365
|
+
name: "",
|
|
366
|
+
projectId: "",
|
|
367
|
+
startDate: "",
|
|
368
|
+
endDate: "",
|
|
369
|
+
goal: "",
|
|
370
|
+
capacity: ""
|
|
371
|
+
};
|
|
372
|
+
function Q({ onCreated: t }) {
|
|
373
|
+
let { workspaceId: r } = e(), [i, o] = n(!1), [s, c] = n(Z), [p, m] = n(null), { data: h } = u(X, {
|
|
374
|
+
variables: { workspaceId: r ?? "" },
|
|
375
|
+
skip: !i || !r
|
|
376
|
+
}), g = h?.planmagnetProjects.items ?? [], _ = s.projectId || g[0]?.id || "", [v, { loading: y }] = l(Y, {
|
|
377
|
+
refetchQueries: I,
|
|
378
|
+
awaitRefetchQueries: !0
|
|
379
|
+
}), b = s.capacity.trim() === "" ? void 0 : Number(s.capacity), x = b !== void 0 && (!Number.isInteger(b) || b < 0), S = !!(s.startDate && s.endDate && s.endDate < s.startDate), w = s.name.trim() !== "" && _ !== "" && !x && !S && !y;
|
|
380
|
+
return /* @__PURE__ */ f(E, {
|
|
381
|
+
open: i,
|
|
382
|
+
onOpenChange: (e) => {
|
|
383
|
+
o(e), e || m(null);
|
|
384
|
+
},
|
|
385
|
+
children: [/* @__PURE__ */ d(M, {
|
|
386
|
+
asChild: !0,
|
|
387
|
+
children: /* @__PURE__ */ f(C, {
|
|
388
|
+
size: "sm",
|
|
389
|
+
children: [/* @__PURE__ */ d(a, { className: "mr-1 h-4 w-4" }), "New sprint"]
|
|
390
|
+
})
|
|
391
|
+
}), /* @__PURE__ */ f(D, {
|
|
392
|
+
className: "max-w-lg",
|
|
393
|
+
children: [
|
|
394
|
+
/* @__PURE__ */ f(A, { children: [/* @__PURE__ */ d(j, { children: "New sprint" }), /* @__PURE__ */ d(O, { children: "The sprint starts in Planning. Start it when it is ready." })] }),
|
|
395
|
+
/* @__PURE__ */ f("div", {
|
|
396
|
+
className: "space-y-3",
|
|
397
|
+
children: [
|
|
398
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
399
|
+
htmlFor: "new-sprint-name",
|
|
400
|
+
className: "text-sm font-medium",
|
|
401
|
+
children: "Name"
|
|
402
|
+
}), /* @__PURE__ */ d(T, {
|
|
403
|
+
id: "new-sprint-name",
|
|
404
|
+
className: "mt-1",
|
|
405
|
+
value: s.name,
|
|
406
|
+
onChange: (e) => c({
|
|
407
|
+
...s,
|
|
408
|
+
name: e.target.value
|
|
409
|
+
})
|
|
410
|
+
})] }),
|
|
411
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
412
|
+
htmlFor: "new-sprint-project",
|
|
413
|
+
className: "text-sm font-medium",
|
|
414
|
+
children: "Project"
|
|
415
|
+
}), /* @__PURE__ */ f("select", {
|
|
416
|
+
id: "new-sprint-project",
|
|
417
|
+
className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
|
|
418
|
+
value: _,
|
|
419
|
+
onChange: (e) => c({
|
|
420
|
+
...s,
|
|
421
|
+
projectId: e.target.value
|
|
422
|
+
}),
|
|
423
|
+
children: [g.length === 0 && /* @__PURE__ */ d("option", {
|
|
424
|
+
value: "",
|
|
425
|
+
children: "No projects"
|
|
426
|
+
}), g.map((e) => /* @__PURE__ */ d("option", {
|
|
427
|
+
value: e.id,
|
|
428
|
+
children: e.name
|
|
429
|
+
}, e.id))]
|
|
430
|
+
})] }),
|
|
431
|
+
/* @__PURE__ */ f("div", {
|
|
432
|
+
className: "grid grid-cols-2 gap-3",
|
|
433
|
+
children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
434
|
+
htmlFor: "new-sprint-start",
|
|
435
|
+
className: "text-sm font-medium",
|
|
436
|
+
children: "Start date"
|
|
437
|
+
}), /* @__PURE__ */ d(T, {
|
|
438
|
+
id: "new-sprint-start",
|
|
439
|
+
type: "date",
|
|
440
|
+
className: "mt-1",
|
|
441
|
+
value: s.startDate,
|
|
442
|
+
onChange: (e) => c({
|
|
443
|
+
...s,
|
|
444
|
+
startDate: e.target.value
|
|
445
|
+
})
|
|
446
|
+
})] }), /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
447
|
+
htmlFor: "new-sprint-end",
|
|
448
|
+
className: "text-sm font-medium",
|
|
449
|
+
children: "End date"
|
|
450
|
+
}), /* @__PURE__ */ d(T, {
|
|
451
|
+
id: "new-sprint-end",
|
|
452
|
+
type: "date",
|
|
453
|
+
className: "mt-1",
|
|
454
|
+
value: s.endDate,
|
|
455
|
+
onChange: (e) => c({
|
|
456
|
+
...s,
|
|
457
|
+
endDate: e.target.value
|
|
458
|
+
})
|
|
459
|
+
})] })]
|
|
460
|
+
}),
|
|
461
|
+
S && /* @__PURE__ */ d(B, { message: "The end date is before the start date." }),
|
|
462
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
463
|
+
htmlFor: "new-sprint-capacity",
|
|
464
|
+
className: "text-sm font-medium",
|
|
465
|
+
children: "Capacity (story points)"
|
|
466
|
+
}), /* @__PURE__ */ d(T, {
|
|
467
|
+
id: "new-sprint-capacity",
|
|
468
|
+
type: "number",
|
|
469
|
+
min: 0,
|
|
470
|
+
className: "mt-1",
|
|
471
|
+
value: s.capacity,
|
|
472
|
+
onChange: (e) => c({
|
|
473
|
+
...s,
|
|
474
|
+
capacity: e.target.value
|
|
475
|
+
})
|
|
476
|
+
})] }),
|
|
477
|
+
x && /* @__PURE__ */ d(B, { message: "Capacity must be a whole number, 0 or more." }),
|
|
478
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
479
|
+
htmlFor: "new-sprint-goal",
|
|
480
|
+
className: "text-sm font-medium",
|
|
481
|
+
children: "Goal"
|
|
482
|
+
}), /* @__PURE__ */ d(T, {
|
|
483
|
+
id: "new-sprint-goal",
|
|
484
|
+
className: "mt-1",
|
|
485
|
+
value: s.goal,
|
|
486
|
+
onChange: (e) => c({
|
|
487
|
+
...s,
|
|
488
|
+
goal: e.target.value
|
|
489
|
+
})
|
|
490
|
+
})] })
|
|
491
|
+
]
|
|
492
|
+
}),
|
|
493
|
+
p && /* @__PURE__ */ d(B, { message: p }),
|
|
494
|
+
/* @__PURE__ */ d(k, { children: /* @__PURE__ */ d(C, {
|
|
495
|
+
onClick: async () => {
|
|
496
|
+
m(null);
|
|
497
|
+
try {
|
|
498
|
+
let e = (await v({ variables: { input: {
|
|
499
|
+
name: s.name.trim(),
|
|
500
|
+
projectId: _,
|
|
501
|
+
startDate: s.startDate || void 0,
|
|
502
|
+
endDate: s.endDate || void 0,
|
|
503
|
+
goal: s.goal.trim() || void 0,
|
|
504
|
+
capacity: b
|
|
505
|
+
} } })).data?.planmagnetCreateSprint.id;
|
|
506
|
+
c(Z), o(!1), e && t(e);
|
|
507
|
+
} catch (e) {
|
|
508
|
+
m(e instanceof Error ? e.message : "The sprint could not be created.");
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
disabled: !w,
|
|
512
|
+
"data-testid": "new-sprint-submit",
|
|
513
|
+
children: y ? "Creating…" : "Create sprint"
|
|
514
|
+
}) })
|
|
515
|
+
]
|
|
516
|
+
})]
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
//#endregion
|
|
520
|
+
export { Q as NewSprintDialog, V as SprintLifecycleActions, q as SprintWorkItemsPanel };
|
|
521
|
+
|
|
522
|
+
//# sourceMappingURL=SprintControls.js.map
|