@burdenoff/microfe-planmagnet 2026.927.3 → 2026.927.4

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.
Files changed (66) hide show
  1. package/dist/planmagnet/controls/FeatureControls.d.ts +26 -0
  2. package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -0
  3. package/dist/planmagnet/controls/FeatureControls.js +318 -0
  4. package/dist/planmagnet/controls/FeatureControls.js.map +1 -0
  5. package/dist/planmagnet/controls/MilestoneControls.d.ts +17 -0
  6. package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -0
  7. package/dist/planmagnet/controls/MilestoneControls.js +200 -0
  8. package/dist/planmagnet/controls/MilestoneControls.js.map +1 -0
  9. package/dist/planmagnet/controls/ObjectiveControls.d.ts +31 -0
  10. package/dist/planmagnet/controls/ObjectiveControls.d.ts.map +1 -0
  11. package/dist/planmagnet/controls/ObjectiveControls.js +296 -0
  12. package/dist/planmagnet/controls/ObjectiveControls.js.map +1 -0
  13. package/dist/planmagnet/controls/Pickers.d.ts +26 -0
  14. package/dist/planmagnet/controls/Pickers.d.ts.map +1 -0
  15. package/dist/planmagnet/controls/Pickers.js +171 -0
  16. package/dist/planmagnet/controls/Pickers.js.map +1 -0
  17. package/dist/planmagnet/controls/ProjectControls.js +7 -7
  18. package/dist/planmagnet/controls/ReleaseControls.d.ts +14 -0
  19. package/dist/planmagnet/controls/ReleaseControls.d.ts.map +1 -0
  20. package/dist/planmagnet/controls/ReleaseControls.js +108 -0
  21. package/dist/planmagnet/controls/ReleaseControls.js.map +1 -0
  22. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  23. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +186 -152
  24. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  25. package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.d.ts.map +1 -1
  26. package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js +63 -56
  27. package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js.map +1 -1
  28. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
  29. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +229 -214
  30. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
  31. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  32. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +142 -114
  33. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  34. package/dist/planmagnet/pages/ProductsEditReleasePagePage.d.ts.map +1 -1
  35. package/dist/planmagnet/pages/ProductsEditReleasePagePage.js +134 -105
  36. package/dist/planmagnet/pages/ProductsEditReleasePagePage.js.map +1 -1
  37. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  38. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +102 -108
  39. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  40. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
  41. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +119 -94
  42. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  43. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
  44. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +107 -106
  45. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
  46. package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
  47. package/dist/planmagnet/pages/ProductsReleasesPagePage.js +2 -0
  48. package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
  49. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +6 -6
  50. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts.map +1 -1
  51. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +55 -63
  52. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -1
  53. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +4 -4
  54. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +123 -128
  55. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
  56. package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
  57. package/dist/planmagnet/pages/TasksEditTaskPagePage.js +139 -107
  58. package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
  59. package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
  60. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +173 -126
  61. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
  62. package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
  63. package/dist/planmagnet/pages/TasksTasksPagePage.js +1 -0
  64. package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
  65. package/dist/planmagnet/roadmaps/RoadmapControls.js +23 -23
  66. package/package.json +1 -1
@@ -194,6 +194,7 @@ function w() {
194
194
  className: "flex gap-1",
195
195
  children: [
196
196
  "all",
197
+ "none",
197
198
  "low",
198
199
  "medium",
199
200
  "high",
@@ -1 +1 @@
1
- {"version":3,"file":"TasksTasksPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TasksTasksPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent } from '@burdenoff/fe-libs/ui';\nimport { Filter } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { ProjectBoard } from '../board/ProjectBoard';\nimport { ExportWorkItemsButton } from '../export/ExportWorkItemsButton';\n\nconst GET_WORK_ITEMS = gql`\n query GetWorkItems(\n $workspaceId: ID!\n $filter: PlanmagnetWorkItemFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetWorkItems(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n title\n type\n status\n priority\n key\n assigneeId\n reporterId\n sprintId\n storyPoints\n estimateMinutes\n dueDate\n labels\n tags\n createdAt\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n totalCount\n }\n }\n`;\n\n// Server-side counts (QA H2): the list returns at most 100 rows per page, so\n// counting the loaded page undercounted every workspace past 100 items.\nconst GET_WORK_ITEM_COUNTS = gql`\n query GetWorkItemCounts($workspaceId: ID!, $filter: PlanmagnetWorkItemFilter) {\n planmagnetWorkItemCounts(workspaceId: $workspaceId, filter: $filter) {\n total\n byStatus {\n status\n count\n }\n }\n }\n`;\n\nconst PAGE_SIZE = 100;\n\nconst GET_BOARD_PROJECTS = gql`\n query GetBoardProjects($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n }\n }\n }\n`;\n\ninterface WorkItemNode {\n id: string;\n title: string;\n type: string;\n status: string;\n priority: string;\n key: string;\n assigneeId: string | null;\n reporterId: string | null;\n sprintId: string | null;\n storyPoints: number | null;\n estimateMinutes: number | null;\n dueDate: string | null;\n labels: string[];\n tags: string[];\n createdAt: string;\n}\n\nconst columns = [\n 'BACKLOG',\n 'TODO',\n 'IN_PROGRESS',\n 'IN_REVIEW',\n 'TESTING',\n 'DONE',\n 'CANCELLED',\n] as const;\n\nconst colLabel: Record<string, string> = {\n BACKLOG: 'Backlog',\n TODO: 'To Do',\n IN_PROGRESS: 'In Progress',\n IN_REVIEW: 'In Review',\n TESTING: 'Testing',\n DONE: 'Done',\n CANCELLED: 'Cancelled',\n};\n\nconst colColor: Record<string, string> = {\n BACKLOG: 'border-t-gray-400',\n TODO: 'border-t-blue-400',\n IN_PROGRESS: 'border-t-yellow-400',\n IN_REVIEW: 'border-t-purple-400',\n TESTING: 'border-t-teal-400',\n DONE: 'border-t-green-400',\n CANCELLED: 'border-t-muted-foreground',\n};\n\nconst priorityColor: Record<string, string> = {\n LOW: 'bg-gray-100 text-gray-700',\n MEDIUM: 'bg-blue-100 text-blue-700',\n HIGH: 'bg-orange-100 text-orange-700',\n CRITICAL: 'bg-red-100 text-red-700',\n};\n\nconst typeColor: Record<string, string> = {\n STORY: 'bg-green-100 text-green-700',\n BUG: 'bg-red-100 text-red-700',\n TASK: 'bg-blue-100 text-blue-700',\n EPIC: 'bg-purple-100 text-purple-700',\n // The API serves SUB_TASK (the stored SUBTASK is mapped by the service), so the old SUBTASK key never matched.\n SUB_TASK: 'bg-gray-100 text-gray-700',\n};\n\nexport function TasksTasksPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [priorityFilter, setPriorityFilter] = useState<string>('all');\n const [typeFilter, setTypeFilter] = useState<string>('all');\n // P1.2: the Task Board is a project's real board (server-side placement,\n // drag-and-drop, WIP limits). null = not chosen yet -> the first project;\n // '' = the cross-project status overview, which is NOT a board.\n const [boardProject, setBoardProject] = useState<string | null>(null);\n const { data: projectsData } = useQuery(GET_BOARD_PROJECTS, {\n variables: { workspaceId },\n skip: !workspaceId,\n });\n const boardProjects = projectsData?.planmagnetProjects?.items ?? [];\n const selectedProject = boardProject ?? boardProjects[0]?.id ?? '';\n const selectedProjectName = boardProjects.find((p) => p.id === selectedProject)?.name ?? '';\n\n // Filters go to the server so the loaded items AND the counts both reflect\n // every matching item, not just the first page.\n const filter = {\n ...(priorityFilter !== 'all' && { priority: priorityFilter.toUpperCase() }),\n ...(typeFilter !== 'all' && { type: typeFilter.toUpperCase() }),\n };\n const [loadingMore, setLoadingMore] = useState(false);\n\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetWorkItems: {\n items: WorkItemNode[];\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n totalCount: number;\n };\n }>(GET_WORK_ITEMS, {\n variables: { workspaceId, filter, pagination: { first: PAGE_SIZE } },\n // The list only feeds the cross-project overview; a board loads its own cards.\n skip: !workspaceId || !!selectedProject,\n });\n const { data: countsData } = useQuery<{\n planmagnetWorkItemCounts: { total: number; byStatus: { status: string; count: number }[] };\n }>(GET_WORK_ITEM_COUNTS, {\n // On a project board the header counts THAT project, without the overview's priority/type filters (their\n // buttons are hidden there, but their state survived and silently narrowed the count). It used to count the\n // whole workspace above a single project's board (BOFF-7943).\n variables: { workspaceId, filter: selectedProject ? { projectId: selectedProject } : filter },\n skip: !workspaceId,\n });\n\n const loadMore = async () => {\n const after = data?.planmagnetWorkItems?.pageInfo?.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: PAGE_SIZE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetWorkItems: {\n ...fetchMoreResult.planmagnetWorkItems,\n items: [\n ...prev.planmagnetWorkItems.items,\n ...fetchMoreResult.planmagnetWorkItems.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n // Only before the first load: a refetch or a filter change must not blank the board.\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-red-500\">Error: {error.message}</div>;\n }\n\n const tasks = data?.planmagnetWorkItems?.items ?? [];\n\n const counts = countsData?.planmagnetWorkItemCounts;\n // Never fall back to the loaded page's length: on a board nothing is loaded here, so that read \"0 tasks\".\n const total: number | undefined =\n counts?.total ?? (selectedProject ? undefined : data?.planmagnetWorkItems?.totalCount);\n const countFor = (status: string) =>\n counts?.byStatus.find((c) => c.status === status)?.count ??\n tasks.filter((t) => t.status === status).length;\n const hasMore = data?.planmagnetWorkItems?.pageInfo?.hasNextPage ?? false;\n\n return (\n <div className=\"space-y-4 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Task Board</h1>\n <label className=\"sr-only\" htmlFor=\"task-board-project\">\n Project\n </label>\n <select\n id=\"task-board-project\"\n className=\"mt-2 rounded-md border border-input bg-background px-2 py-1 text-sm\"\n value={selectedProject}\n onChange={(e) => setBoardProject(e.target.value)}\n >\n {boardProjects.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n </option>\n ))}\n <option value=\"\">All projects — status overview</option>\n </select>\n <div className=\"mt-2\">\n <ExportWorkItemsButton projectId={selectedProject || undefined} />\n </div>\n <p className=\"text-sm text-muted-foreground\" data-testid=\"task-board-total\">\n {total === undefined\n ? 'Counting tasks…'\n : selectedProject\n ? `${total} ${total === 1 ? 'task' : 'tasks'} in ${selectedProjectName}`\n : `${total} tasks across all projects${tasks.length < total ? ` · showing ${tasks.length}` : ''}`}\n </p>\n </div>\n </div>\n {selectedProject ? (\n <ProjectBoard projectId={selectedProject} projectName={selectedProjectName} />\n ) : (\n <>\n <div className=\"flex gap-4 items-center flex-wrap\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n <div className=\"flex gap-1\">\n {['all', 'low', 'medium', 'high', 'critical'].map((p) => (\n <button\n key={p}\n onClick={() => setPriorityFilter(p)}\n className={`px-2 py-1 text-xs rounded capitalize ${priorityFilter === p ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {p}\n </button>\n ))}\n </div>\n <div className=\"flex gap-1\">\n {/* sub_task → SUB_TASK: filtering by it works since svc 1f4db8e (it was a masked 500). */}\n {['all', 'story', 'bug', 'task', 'epic', 'sub_task'].map((t) => (\n <button\n key={t}\n onClick={() => setTypeFilter(t)}\n className={`px-2 py-1 text-xs rounded capitalize ${typeFilter === t ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {t === 'sub_task' ? 'sub-task' : t}\n </button>\n ))}\n </div>\n </div>\n <div className=\"grid grid-cols-7 gap-3 overflow-x-auto\">\n {columns.map((col) => {\n const colTasks = tasks.filter((t) => t.status === col);\n return (\n <div key={col} className=\"min-w-[200px]\">\n <div className={`border-t-2 ${colColor[col]} rounded-t-md p-2 bg-muted/50`}>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm font-medium\">{colLabel[col]}</span>\n <span\n className=\"text-xs bg-muted rounded-full px-1.5\"\n data-testid={`task-col-count-${col}`}\n >\n {countFor(col)}\n </span>\n </div>\n </div>\n <div className=\"space-y-2 mt-2\">\n {colTasks.map((task) => (\n <Card\n key={task.id}\n {...rowLinkProps(\n () =>\n navigate(\n `/tasks/task-detail-page?taskId=${encodeURIComponent(task.id)}`\n ),\n task.title\n )}\n >\n <CardContent className=\"p-3 space-y-2\">\n <p className=\"text-sm font-medium leading-tight\">{task.title}</p>\n <p className=\"text-xs text-muted-foreground\">{task.key}</p>\n <div className=\"flex gap-1 flex-wrap\">\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${priorityColor[task.priority] ?? ''}`}\n >\n {task.priority.toLowerCase()}\n </span>\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${typeColor[task.type] ?? ''}`}\n >\n {task.type.toLowerCase()}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <div className=\"flex gap-1\">\n {(task.labels ?? []).slice(0, 2).map((l) => (\n <span key={l} className=\"text-[9px] px-1 py-0.5 rounded bg-muted\">\n {l}\n </span>\n ))}\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] font-medium bg-primary/10 text-primary rounded-full h-5 w-5 flex items-center justify-center\">\n {task.storyPoints ?? 0}\n </span>\n </div>\n </div>\n <div className=\"flex justify-between text-[10px] text-muted-foreground\">\n <span>\n {task.assigneeId ? <PersonName id={task.assigneeId} /> : 'Unassigned'}\n </span>\n <span>\n {task.dueDate ? new Date(task.dueDate).toLocaleDateString() : ''}\n </span>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n </div>\n );\n })}\n </div>\n {hasMore && (\n <div className=\"flex justify-center\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loadingMore}\n className=\"px-3 py-1.5 text-sm rounded-md bg-muted text-foreground hover:bg-muted/80 disabled:opacity-50\"\n data-testid=\"task-board-load-more\"\n >\n {loadingMore ? 'Loading…' : `Load more (${tasks.length} of ${total})`}\n </button>\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TasksTasksPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TasksTasksPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent } from '@burdenoff/fe-libs/ui';\nimport { Filter } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { ProjectBoard } from '../board/ProjectBoard';\nimport { ExportWorkItemsButton } from '../export/ExportWorkItemsButton';\n\nconst GET_WORK_ITEMS = gql`\n query GetWorkItems(\n $workspaceId: ID!\n $filter: PlanmagnetWorkItemFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetWorkItems(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n title\n type\n status\n priority\n key\n assigneeId\n reporterId\n sprintId\n storyPoints\n estimateMinutes\n dueDate\n labels\n tags\n createdAt\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n totalCount\n }\n }\n`;\n\n// Server-side counts (QA H2): the list returns at most 100 rows per page, so\n// counting the loaded page undercounted every workspace past 100 items.\nconst GET_WORK_ITEM_COUNTS = gql`\n query GetWorkItemCounts($workspaceId: ID!, $filter: PlanmagnetWorkItemFilter) {\n planmagnetWorkItemCounts(workspaceId: $workspaceId, filter: $filter) {\n total\n byStatus {\n status\n count\n }\n }\n }\n`;\n\nconst PAGE_SIZE = 100;\n\nconst GET_BOARD_PROJECTS = gql`\n query GetBoardProjects($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n }\n }\n }\n`;\n\ninterface WorkItemNode {\n id: string;\n title: string;\n type: string;\n status: string;\n priority: string;\n key: string;\n assigneeId: string | null;\n reporterId: string | null;\n sprintId: string | null;\n storyPoints: number | null;\n estimateMinutes: number | null;\n dueDate: string | null;\n labels: string[];\n tags: string[];\n createdAt: string;\n}\n\nconst columns = [\n 'BACKLOG',\n 'TODO',\n 'IN_PROGRESS',\n 'IN_REVIEW',\n 'TESTING',\n 'DONE',\n 'CANCELLED',\n] as const;\n\nconst colLabel: Record<string, string> = {\n BACKLOG: 'Backlog',\n TODO: 'To Do',\n IN_PROGRESS: 'In Progress',\n IN_REVIEW: 'In Review',\n TESTING: 'Testing',\n DONE: 'Done',\n CANCELLED: 'Cancelled',\n};\n\nconst colColor: Record<string, string> = {\n BACKLOG: 'border-t-gray-400',\n TODO: 'border-t-blue-400',\n IN_PROGRESS: 'border-t-yellow-400',\n IN_REVIEW: 'border-t-purple-400',\n TESTING: 'border-t-teal-400',\n DONE: 'border-t-green-400',\n CANCELLED: 'border-t-muted-foreground',\n};\n\nconst priorityColor: Record<string, string> = {\n LOW: 'bg-gray-100 text-gray-700',\n MEDIUM: 'bg-blue-100 text-blue-700',\n HIGH: 'bg-orange-100 text-orange-700',\n CRITICAL: 'bg-red-100 text-red-700',\n};\n\nconst typeColor: Record<string, string> = {\n STORY: 'bg-green-100 text-green-700',\n BUG: 'bg-red-100 text-red-700',\n TASK: 'bg-blue-100 text-blue-700',\n EPIC: 'bg-purple-100 text-purple-700',\n // The API serves SUB_TASK (the stored SUBTASK is mapped by the service), so the old SUBTASK key never matched.\n SUB_TASK: 'bg-gray-100 text-gray-700',\n};\n\nexport function TasksTasksPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [priorityFilter, setPriorityFilter] = useState<string>('all');\n const [typeFilter, setTypeFilter] = useState<string>('all');\n // P1.2: the Task Board is a project's real board (server-side placement,\n // drag-and-drop, WIP limits). null = not chosen yet -> the first project;\n // '' = the cross-project status overview, which is NOT a board.\n const [boardProject, setBoardProject] = useState<string | null>(null);\n const { data: projectsData } = useQuery(GET_BOARD_PROJECTS, {\n variables: { workspaceId },\n skip: !workspaceId,\n });\n const boardProjects = projectsData?.planmagnetProjects?.items ?? [];\n const selectedProject = boardProject ?? boardProjects[0]?.id ?? '';\n const selectedProjectName = boardProjects.find((p) => p.id === selectedProject)?.name ?? '';\n\n // Filters go to the server so the loaded items AND the counts both reflect\n // every matching item, not just the first page.\n const filter = {\n ...(priorityFilter !== 'all' && { priority: priorityFilter.toUpperCase() }),\n ...(typeFilter !== 'all' && { type: typeFilter.toUpperCase() }),\n };\n const [loadingMore, setLoadingMore] = useState(false);\n\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetWorkItems: {\n items: WorkItemNode[];\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n totalCount: number;\n };\n }>(GET_WORK_ITEMS, {\n variables: { workspaceId, filter, pagination: { first: PAGE_SIZE } },\n // The list only feeds the cross-project overview; a board loads its own cards.\n skip: !workspaceId || !!selectedProject,\n });\n const { data: countsData } = useQuery<{\n planmagnetWorkItemCounts: { total: number; byStatus: { status: string; count: number }[] };\n }>(GET_WORK_ITEM_COUNTS, {\n // On a project board the header counts THAT project, without the overview's priority/type filters (their\n // buttons are hidden there, but their state survived and silently narrowed the count). It used to count the\n // whole workspace above a single project's board (BOFF-7943).\n variables: { workspaceId, filter: selectedProject ? { projectId: selectedProject } : filter },\n skip: !workspaceId,\n });\n\n const loadMore = async () => {\n const after = data?.planmagnetWorkItems?.pageInfo?.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: PAGE_SIZE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetWorkItems: {\n ...fetchMoreResult.planmagnetWorkItems,\n items: [\n ...prev.planmagnetWorkItems.items,\n ...fetchMoreResult.planmagnetWorkItems.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n // Only before the first load: a refetch or a filter change must not blank the board.\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-red-500\">Error: {error.message}</div>;\n }\n\n const tasks = data?.planmagnetWorkItems?.items ?? [];\n\n const counts = countsData?.planmagnetWorkItemCounts;\n // Never fall back to the loaded page's length: on a board nothing is loaded here, so that read \"0 tasks\".\n const total: number | undefined =\n counts?.total ?? (selectedProject ? undefined : data?.planmagnetWorkItems?.totalCount);\n const countFor = (status: string) =>\n counts?.byStatus.find((c) => c.status === status)?.count ??\n tasks.filter((t) => t.status === status).length;\n const hasMore = data?.planmagnetWorkItems?.pageInfo?.hasNextPage ?? false;\n\n return (\n <div className=\"space-y-4 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Task Board</h1>\n <label className=\"sr-only\" htmlFor=\"task-board-project\">\n Project\n </label>\n <select\n id=\"task-board-project\"\n className=\"mt-2 rounded-md border border-input bg-background px-2 py-1 text-sm\"\n value={selectedProject}\n onChange={(e) => setBoardProject(e.target.value)}\n >\n {boardProjects.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n </option>\n ))}\n <option value=\"\">All projects — status overview</option>\n </select>\n <div className=\"mt-2\">\n <ExportWorkItemsButton projectId={selectedProject || undefined} />\n </div>\n <p className=\"text-sm text-muted-foreground\" data-testid=\"task-board-total\">\n {total === undefined\n ? 'Counting tasks…'\n : selectedProject\n ? `${total} ${total === 1 ? 'task' : 'tasks'} in ${selectedProjectName}`\n : `${total} tasks across all projects${tasks.length < total ? ` · showing ${tasks.length}` : ''}`}\n </p>\n </div>\n </div>\n {selectedProject ? (\n <ProjectBoard projectId={selectedProject} projectName={selectedProjectName} />\n ) : (\n <>\n <div className=\"flex gap-4 items-center flex-wrap\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n <div className=\"flex gap-1\">\n {/* 'none' included: a NONE-priority task matched no chip (BOFF-7985). */}\n {['all', 'none', 'low', 'medium', 'high', 'critical'].map((p) => (\n <button\n key={p}\n onClick={() => setPriorityFilter(p)}\n className={`px-2 py-1 text-xs rounded capitalize ${priorityFilter === p ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {p}\n </button>\n ))}\n </div>\n <div className=\"flex gap-1\">\n {/* sub_task → SUB_TASK: filtering by it works since svc 1f4db8e (it was a masked 500). */}\n {['all', 'story', 'bug', 'task', 'epic', 'sub_task'].map((t) => (\n <button\n key={t}\n onClick={() => setTypeFilter(t)}\n className={`px-2 py-1 text-xs rounded capitalize ${typeFilter === t ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {t === 'sub_task' ? 'sub-task' : t}\n </button>\n ))}\n </div>\n </div>\n <div className=\"grid grid-cols-7 gap-3 overflow-x-auto\">\n {columns.map((col) => {\n const colTasks = tasks.filter((t) => t.status === col);\n return (\n <div key={col} className=\"min-w-[200px]\">\n <div className={`border-t-2 ${colColor[col]} rounded-t-md p-2 bg-muted/50`}>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm font-medium\">{colLabel[col]}</span>\n <span\n className=\"text-xs bg-muted rounded-full px-1.5\"\n data-testid={`task-col-count-${col}`}\n >\n {countFor(col)}\n </span>\n </div>\n </div>\n <div className=\"space-y-2 mt-2\">\n {colTasks.map((task) => (\n <Card\n key={task.id}\n {...rowLinkProps(\n () =>\n navigate(\n `/tasks/task-detail-page?taskId=${encodeURIComponent(task.id)}`\n ),\n task.title\n )}\n >\n <CardContent className=\"p-3 space-y-2\">\n <p className=\"text-sm font-medium leading-tight\">{task.title}</p>\n <p className=\"text-xs text-muted-foreground\">{task.key}</p>\n <div className=\"flex gap-1 flex-wrap\">\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${priorityColor[task.priority] ?? ''}`}\n >\n {task.priority.toLowerCase()}\n </span>\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${typeColor[task.type] ?? ''}`}\n >\n {task.type.toLowerCase()}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <div className=\"flex gap-1\">\n {(task.labels ?? []).slice(0, 2).map((l) => (\n <span key={l} className=\"text-[9px] px-1 py-0.5 rounded bg-muted\">\n {l}\n </span>\n ))}\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] font-medium bg-primary/10 text-primary rounded-full h-5 w-5 flex items-center justify-center\">\n {task.storyPoints ?? 0}\n </span>\n </div>\n </div>\n <div className=\"flex justify-between text-[10px] text-muted-foreground\">\n <span>\n {task.assigneeId ? <PersonName id={task.assigneeId} /> : 'Unassigned'}\n </span>\n <span>\n {task.dueDate ? new Date(task.dueDate).toLocaleDateString() : ''}\n </span>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n </div>\n );\n })}\n </div>\n {hasMore && (\n <div className=\"flex justify-center\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loadingMore}\n className=\"px-3 py-1.5 text-sm rounded-md bg-muted text-foreground hover:bg-muted/80 disabled:opacity-50\"\n data-testid=\"task-board-load-more\"\n >\n {loadingMore ? 'Loading…' : `Load more (${tasks.length} of ${total})`}\n </button>\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
2
  import { throwOnError as t } from "../utils/mutationResult.js";
3
- import { detailPath as n } from "../utils/rowNavigation.js";
4
- import { endBeforeStart as r } from "../utils/formValidation.js";
5
- import { ConfirmButton as i, ErrorLine as a } from "../controls/ConfirmButton.js";
6
- import { enumLabel as o } from "../controls/WorkItemControls.js";
3
+ import { ConfirmButton as n, ErrorLine as r } from "../controls/ConfirmButton.js";
4
+ import { endBeforeStart as i } from "../utils/formValidation.js";
5
+ import { enumLabel as a } from "../controls/WorkItemControls.js";
6
+ import { detailPath as o } from "../utils/rowNavigation.js";
7
7
  import { useState as s } from "react";
8
8
  import { Pencil as c, Plus as l, Trash2 as u } from "lucide-react";
9
9
  import { gql as d } from "@apollo/client";
@@ -51,7 +51,7 @@ var w = [
51
51
  planmagnetDeleteRoadmap(id: $id)
52
52
  }
53
53
  `, A = (e) => e ? e.slice(0, 10) : "";
54
- function j({ roadmapId: e, item: n, lanes: i }) {
54
+ function j({ roadmapId: e, item: n, lanes: o }) {
55
55
  let u = {
56
56
  title: n?.title ?? "",
57
57
  description: n?.description ?? "",
@@ -62,7 +62,7 @@ function j({ roadmapId: e, item: n, lanes: i }) {
62
62
  }, [d, O] = s(!1), [k, j] = s(u), [M, N] = s(null), P = {
63
63
  refetchQueries: T,
64
64
  awaitRefetchQueries: !0
65
- }, [F, I] = f(E, P), [L, R] = f(D, P), z = I.loading || R.loading, B = r(k.startDate, k.endDate), V = k.title.trim() !== "" && !B && !z, H = w.includes(k.status) ? w : [k.status, ...w], U = n ? `roadmap-item-${n.id}` : "roadmap-item-new", W = async () => {
65
+ }, [F, I] = f(E, P), [L, R] = f(D, P), z = I.loading || R.loading, B = i(k.startDate, k.endDate), V = k.title.trim() !== "" && !B && !z, H = w.includes(k.status) ? w : [k.status, ...w], U = n ? `roadmap-item-${n.id}` : "roadmap-item-new", W = async () => {
66
66
  N(null);
67
67
  let r = {
68
68
  title: k.title.trim(),
@@ -152,7 +152,7 @@ function j({ roadmapId: e, item: n, lanes: i }) {
152
152
  }),
153
153
  /* @__PURE__ */ p("datalist", {
154
154
  id: `${U}-lanes`,
155
- children: i.map((e) => /* @__PURE__ */ p("option", { value: e }, e))
155
+ children: o.map((e) => /* @__PURE__ */ p("option", { value: e }, e))
156
156
  })
157
157
  ] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
158
158
  htmlFor: `${U}-status`,
@@ -168,7 +168,7 @@ function j({ roadmapId: e, item: n, lanes: i }) {
168
168
  }),
169
169
  children: H.map((e) => /* @__PURE__ */ p("option", {
170
170
  value: e,
171
- children: o(e)
171
+ children: a(e)
172
172
  }, e))
173
173
  })] })]
174
174
  }),
@@ -204,7 +204,7 @@ function j({ roadmapId: e, item: n, lanes: i }) {
204
204
  })
205
205
  })] })]
206
206
  }),
207
- B && /* @__PURE__ */ p(a, { message: "The end date is before the start date." }),
207
+ B && /* @__PURE__ */ p(r, { message: "The end date is before the start date." }),
208
208
  /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
209
209
  htmlFor: `${U}-description`,
210
210
  className: "text-sm font-medium",
@@ -218,7 +218,7 @@ function j({ roadmapId: e, item: n, lanes: i }) {
218
218
  description: e.target.value
219
219
  })
220
220
  })] }),
221
- M && /* @__PURE__ */ p(a, { message: M })
221
+ M && /* @__PURE__ */ p(r, { message: M })
222
222
  ]
223
223
  }),
224
224
  /* @__PURE__ */ m(b, { children: [/* @__PURE__ */ p(h, {
@@ -234,39 +234,39 @@ function j({ roadmapId: e, item: n, lanes: i }) {
234
234
  })]
235
235
  });
236
236
  }
237
- function M({ id: e, title: n }) {
238
- let [r, o] = f(O, {
237
+ function M({ id: e, title: i }) {
238
+ let [a, o] = f(O, {
239
239
  refetchQueries: T,
240
240
  awaitRefetchQueries: !0
241
241
  }), [c, l] = s(null), d = async () => {
242
242
  l(null);
243
243
  try {
244
- await t(r({ variables: { id: e } }));
244
+ await t(a({ variables: { id: e } }));
245
245
  } catch (e) {
246
246
  l(e instanceof Error ? e.message : "The item could not be removed.");
247
247
  }
248
248
  };
249
249
  return /* @__PURE__ */ m("div", {
250
250
  className: "flex flex-col items-end",
251
- children: [/* @__PURE__ */ p(i, {
251
+ children: [/* @__PURE__ */ p(n, {
252
252
  label: "",
253
- ariaLabel: `Remove ${n}`,
253
+ ariaLabel: `Remove ${i}`,
254
254
  variant: "outline",
255
255
  icon: /* @__PURE__ */ p(u, { className: "h-4 w-4" }),
256
- title: `Remove "${n}" from the roadmap?`,
256
+ title: `Remove "${i}" from the roadmap?`,
257
257
  description: "The item is removed from this roadmap. This cannot be undone here.",
258
258
  confirmLabel: "Remove item",
259
259
  destructive: !0,
260
260
  disabled: o.loading,
261
261
  onConfirm: () => void d()
262
- }), c && /* @__PURE__ */ p(a, { message: c })]
262
+ }), c && /* @__PURE__ */ p(r, { message: c })]
263
263
  });
264
264
  }
265
- function N({ roadmapId: r, name: o }) {
265
+ function N({ roadmapId: i, name: a }) {
266
266
  let l = e(), [d, g] = f(k, { refetchQueries: ["ListRoadmapsPage", "ListRoadmaps"] }), [_, v] = s(null), y = async () => {
267
267
  v(null);
268
268
  try {
269
- await t(d({ variables: { id: r } })), l("/products/roadmaps-page");
269
+ await t(d({ variables: { id: i } })), l("/products/roadmaps-page");
270
270
  } catch (e) {
271
271
  v(e instanceof Error ? e.message : "The roadmap could not be deleted.");
272
272
  }
@@ -279,20 +279,20 @@ function N({ roadmapId: r, name: o }) {
279
279
  size: "sm",
280
280
  variant: "outline",
281
281
  "data-testid": "roadmap-edit",
282
- onClick: () => l(n("/products/edit-product-page", r)),
282
+ onClick: () => l(o("/products/edit-product-page", i)),
283
283
  children: [/* @__PURE__ */ p(c, { className: "mr-1 h-4 w-4" }), "Edit"]
284
- }), /* @__PURE__ */ p(i, {
284
+ }), /* @__PURE__ */ p(n, {
285
285
  label: "Delete",
286
286
  variant: "outline",
287
287
  icon: /* @__PURE__ */ p(u, { className: "mr-1 h-4 w-4" }),
288
- title: `Delete ${o}?`,
288
+ title: `Delete ${a}?`,
289
289
  description: "The roadmap and its items are deleted. This cannot be undone here.",
290
290
  confirmLabel: "Delete roadmap",
291
291
  destructive: !0,
292
292
  disabled: g.loading,
293
293
  onConfirm: () => void y()
294
294
  })]
295
- }), _ && /* @__PURE__ */ p(a, { message: _ })]
295
+ }), _ && /* @__PURE__ */ p(r, { message: _ })]
296
296
  });
297
297
  }
298
298
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-planmagnet",
3
- "version": "2026.927.3",
3
+ "version": "2026.927.4",
4
4
  "description": "Burdenoff PlanMagnet Microfrontend",
5
5
  "type": "module",
6
6
  "files": [