@burdenoff/microfe-planmagnet 2026.925.16 → 2026.925.18

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReleaseWorkItemsCard.js","names":[],"sources":["../../../src/planmagnet/releases/ReleaseWorkItemsCard.tsx"],"sourcesContent":["import { useDeferredValue, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Input,\n} from '@burdenoff/fe-libs/ui';\nimport { Plus, X } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\n\n/**\n * What is in this release (BOFF-7931). Until the service could write a\n * release's work items this card never rendered: every release was empty and\n * its scope silently fell back to its whole project. Items can be added from\n * the release's project only; the service refuses anything else and says why.\n */\n\nconst CANDIDATES = gql`\n query ReleaseWorkItemCandidates($workspaceId: ID!, $filter: PlanmagnetWorkItemFilter) {\n planmagnetWorkItems(workspaceId: $workspaceId, filter: $filter, pagination: { first: 20 }) {\n items {\n id\n key\n title\n status\n }\n }\n }\n`;\nconst ADD = gql`\n mutation ReleaseAddWorkItems($releaseId: ID!, $workItemIds: [ID!]!) {\n planmagnetAddWorkItemsToRelease(releaseId: $releaseId, workItemIds: $workItemIds) {\n id\n }\n }\n`;\nconst REMOVE = gql`\n mutation ReleaseRemoveWorkItems($releaseId: ID!, $workItemIds: [ID!]!) {\n planmagnetRemoveWorkItemsFromRelease(releaseId: $releaseId, workItemIds: $workItemIds) {\n id\n }\n }\n`;\n\nexport interface ReleaseWorkItem {\n id: string;\n title: string;\n status: string;\n type: string;\n priority: string;\n}\n\nexport function ReleaseWorkItemsCard({\n releaseId,\n projectId,\n items,\n}: {\n releaseId: string;\n projectId: string | null;\n items: ReleaseWorkItem[];\n}) {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [adding, setAdding] = useState(false);\n const [search, setSearch] = useState('');\n const [error, setError] = useState<string | null>(null);\n const term = useDeferredValue(search.trim());\n\n const candidates = useQuery<{\n planmagnetWorkItems: { items: { id: string; key: string; title: string; status: string }[] };\n }>(CANDIDATES, {\n variables: { workspaceId, filter: { projectId, ...(term ? { search: term } : {}) } },\n skip: !adding || !workspaceId || !projectId,\n });\n const refetch = {\n refetchQueries: ['GetRelease', 'ReleaseReadiness'],\n awaitRefetchQueries: true,\n };\n const [add, addState] = useMutation(ADD, refetch);\n const [remove, removeState] = useMutation(REMOVE, refetch);\n\n const inRelease = new Set(items.map((i) => i.id));\n const offered = (candidates.data?.planmagnetWorkItems.items ?? []).filter(\n (c) => !inRelease.has(c.id)\n );\n\n const change = async (action: () => Promise<{ error?: unknown }>, fallback: string) => {\n setError(null);\n try {\n await throwOnError(action());\n } catch (e) {\n setError(e instanceof Error ? e.message : fallback);\n }\n };\n\n return (\n <Card data-testid=\"release-work-items\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle>Work Items ({items.length})</CardTitle>\n {projectId && !adding && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => setAdding(true)}\n data-testid=\"release-items-add\"\n >\n <Plus className=\"mr-1 h-4 w-4\" /> Add work items\n </Button>\n )}\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {error}\n </p>\n )}\n {items.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">\n No work items are in this release yet, so its overview and release notes cover\n everything in its project.\n </p>\n )}\n {items.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between gap-2 rounded-md border p-3\"\n data-testid=\"release-item\"\n >\n <button\n type=\"button\"\n className=\"min-w-0 text-left\"\n onClick={() =>\n navigate(`/tasks/task-detail-page?taskId=${encodeURIComponent(item.id)}`)\n }\n >\n <p className=\"truncate text-sm font-medium hover:underline\">{item.title}</p>\n <p className=\"text-xs capitalize text-muted-foreground\">\n {item.type.toLowerCase()} &middot; {item.priority.toLowerCase()} priority\n </p>\n </button>\n <span className=\"flex shrink-0 items-center gap-1\">\n <Badge variant=\"outline\" className=\"text-xs capitalize\">\n {item.status.toLowerCase().replace('_', ' ')}\n </Badge>\n <Button\n size=\"icon\"\n variant=\"ghost\"\n aria-label={`Remove ${item.title} from this release`}\n disabled={removeState.loading}\n onClick={() =>\n change(\n () => remove({ variables: { releaseId, workItemIds: [item.id] } }),\n 'The work item could not be removed.'\n )\n }\n >\n <X className=\"h-4 w-4\" />\n </Button>\n </span>\n </div>\n ))}\n {adding && (\n <div className=\"space-y-2 border-t pt-3\">\n <div className=\"flex gap-2\">\n <Input\n aria-label=\"Find work items in this release's project\"\n placeholder=\"Find work items in this project\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setAdding(false);\n setSearch('');\n }}\n >\n Done\n </Button>\n </div>\n {candidates.loading && !candidates.data && (\n <p className=\"text-sm text-muted-foreground\">Loading…</p>\n )}\n {candidates.error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n Work items could not be loaded: {candidates.error.message}\n </p>\n )}\n {candidates.data && offered.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No other work items match.</p>\n )}\n <ul className=\"space-y-1\">\n {offered.map((c) => (\n <li key={c.id} className=\"flex items-center justify-between gap-2 text-sm\">\n <span className=\"min-w-0 truncate\">\n <span className=\"text-muted-foreground\">{c.key}</span> {c.title}\n </span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 shrink-0 text-xs\"\n disabled={addState.loading}\n data-testid=\"release-item-candidate-add\"\n onClick={() =>\n change(\n () => add({ variables: { releaseId, workItemIds: [c.id] } }),\n 'The work item could not be added.'\n )\n }\n >\n Add\n </Button>\n </li>\n ))}\n </ul>\n </div>\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-planmagnet",
3
- "version": "2026.925.16",
3
+ "version": "2026.925.18",
4
4
  "description": "Burdenoff PlanMagnet Microfrontend",
5
5
  "type": "module",
6
6
  "files": [