@burdenoff/microfe-planmagnet 2026.928.7 → 2026.928.9

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 (25) hide show
  1. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoutes.js +18 -14
  3. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  4. package/dist/planmagnet/automation/AutomationRuleFields.d.ts.map +1 -1
  5. package/dist/planmagnet/automation/AutomationRuleFields.js +13 -12
  6. package/dist/planmagnet/automation/AutomationRuleFields.js.map +1 -1
  7. package/dist/planmagnet/controls/WorkItemControls.d.ts +21 -0
  8. package/dist/planmagnet/controls/WorkItemControls.d.ts.map +1 -1
  9. package/dist/planmagnet/controls/WorkItemControls.js +34 -15
  10. package/dist/planmagnet/controls/WorkItemControls.js.map +1 -1
  11. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.d.ts.map +1 -1
  12. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.js +153 -63
  13. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.js.map +1 -1
  14. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts +0 -7
  15. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
  16. package/dist/planmagnet/pages/ReportsReportsPagePage.js +517 -39
  17. package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -1
  18. package/dist/planmagnet/pages/TasksMyWorkPagePage.d.ts +2 -0
  19. package/dist/planmagnet/pages/TasksMyWorkPagePage.d.ts.map +1 -0
  20. package/dist/planmagnet/pages/TasksMyWorkPagePage.js +257 -0
  21. package/dist/planmagnet/pages/TasksMyWorkPagePage.js.map +1 -0
  22. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.d.ts.map +1 -1
  23. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.js +77 -70
  24. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.js.map +1 -1
  25. package/package.json +1 -1
@@ -0,0 +1,257 @@
1
+ import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
+ import { WORK_ITEM_PRIORITIES as n, WorkItemAssigneeSelect as r, WorkItemDueDateInput as i, WorkItemEnumSelect as a, WorkItemStatusSelect as o } from "../controls/WorkItemControls.js";
3
+ import { rowLinkProps as s, stopRowActivation as c } from "../utils/rowNavigation.js";
4
+ import { useState as l } from "react";
5
+ import { CalendarClock as u, ListChecks as d } from "lucide-react";
6
+ import { gql as f } from "@apollo/client";
7
+ import { useQuery as p } from "@apollo/client/react";
8
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
9
+ import { useI18n as g } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
10
+ import { Badge as _, Card as v, CardContent as y, Switch as b } from "@burdenoff/fe-libs/ui";
11
+ //#region src/planmagnet/pages/TasksMyWorkPagePage.tsx
12
+ var x = f`
13
+ query GetMyWorkItems(
14
+ $workspaceId: ID!
15
+ $filter: PlanmagnetMyWorkItemFilter
16
+ $pagination: PlanmagnetPaginationInput
17
+ ) {
18
+ planmagnetMyWorkItems(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {
19
+ items {
20
+ id
21
+ key
22
+ title
23
+ status
24
+ priority
25
+ dueDate
26
+ storyPoints
27
+ isOverdue
28
+ dueGroup
29
+ assigneeId
30
+ project {
31
+ id
32
+ name
33
+ }
34
+ sprint {
35
+ id
36
+ name
37
+ }
38
+ }
39
+ pageInfo {
40
+ hasNextPage
41
+ endCursor
42
+ }
43
+ totalCount
44
+ }
45
+ }
46
+ `, S = 50, C = [
47
+ "OVERDUE",
48
+ "DUE_TODAY",
49
+ "DUE_THIS_WEEK",
50
+ "DUE_LATER",
51
+ "NO_DUE_DATE"
52
+ ], w = {
53
+ OVERDUE: "destructive",
54
+ DUE_TODAY: "warning",
55
+ DUE_THIS_WEEK: "info",
56
+ DUE_LATER: "secondary",
57
+ NO_DUE_DATE: "secondary"
58
+ };
59
+ function T(e, t) {
60
+ switch (t) {
61
+ case "OVERDUE": return e("planmagnet.myWork.group.overdue", "Overdue");
62
+ case "DUE_TODAY": return e("planmagnet.myWork.group.dueToday", "Due today");
63
+ case "DUE_THIS_WEEK": return e("planmagnet.myWork.group.dueThisWeek", "Due this week");
64
+ case "DUE_LATER": return e("planmagnet.myWork.group.dueLater", "Due later");
65
+ default: return e("planmagnet.myWork.group.noDueDate", "No due date");
66
+ }
67
+ }
68
+ function E() {
69
+ let { workspaceId: f } = e(), E = t(), { t: D } = g(), [O, k] = l(!1), [A, j] = l(!1), { data: M, loading: N, error: P, fetchMore: F } = p(x, {
70
+ variables: {
71
+ workspaceId: f,
72
+ filter: { includeClosed: O },
73
+ pagination: { first: S }
74
+ },
75
+ skip: !f
76
+ }), I = async () => {
77
+ let e = M?.planmagnetMyWorkItems?.pageInfo?.endCursor;
78
+ if (e) {
79
+ j(!0);
80
+ try {
81
+ await F({
82
+ variables: { pagination: {
83
+ first: S,
84
+ after: e
85
+ } },
86
+ updateQuery: (e, { fetchMoreResult: t }) => ({ planmagnetMyWorkItems: {
87
+ ...t.planmagnetMyWorkItems,
88
+ items: [...e.planmagnetMyWorkItems.items, ...t.planmagnetMyWorkItems.items]
89
+ } })
90
+ });
91
+ } finally {
92
+ j(!1);
93
+ }
94
+ }
95
+ };
96
+ if (N && !M) return /* @__PURE__ */ m("div", {
97
+ className: "flex items-center justify-center p-12",
98
+ children: /* @__PURE__ */ m("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
99
+ });
100
+ if (P) return /* @__PURE__ */ h("div", {
101
+ className: "p-6 text-destructive",
102
+ children: [
103
+ D("planmagnet.myWork.errorLoading", "My work could not be loaded"),
104
+ ": ",
105
+ P.message
106
+ ]
107
+ });
108
+ let L = M?.planmagnetMyWorkItems?.items ?? [], R = M?.planmagnetMyWorkItems?.totalCount ?? L.length, z = M?.planmagnetMyWorkItems?.pageInfo?.hasNextPage ?? !1, B = C.map((e) => ({
109
+ group: e,
110
+ items: L.filter((t) => t.dueGroup === e)
111
+ })).filter((e) => e.items.length > 0);
112
+ return /* @__PURE__ */ h("div", {
113
+ className: "space-y-4 p-6",
114
+ children: [/* @__PURE__ */ h("div", {
115
+ className: "flex flex-wrap items-center justify-between gap-3",
116
+ children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ h("h1", {
117
+ className: "text-2xl font-semibold flex items-center gap-2",
118
+ children: [/* @__PURE__ */ m(d, { className: "h-6 w-6" }), D("planmagnet.myWork.title", "My Work")]
119
+ }), /* @__PURE__ */ m("p", {
120
+ className: "text-sm text-muted-foreground",
121
+ "data-testid": "my-work-total",
122
+ children: D("planmagnet.myWork.subtitle", "{{count}} item(s) assigned to you across every project", { count: R })
123
+ })] }), /* @__PURE__ */ h("label", {
124
+ className: "flex items-center gap-2 text-sm text-muted-foreground",
125
+ children: [/* @__PURE__ */ m(b, {
126
+ checked: O,
127
+ onCheckedChange: k,
128
+ "aria-label": D("planmagnet.myWork.includeClosed", "Show completed"),
129
+ "data-testid": "my-work-include-closed"
130
+ }), D("planmagnet.myWork.includeClosed", "Show completed")]
131
+ })]
132
+ }), L.length === 0 ? /* @__PURE__ */ m(v, { children: /* @__PURE__ */ h(y, {
133
+ className: "flex flex-col items-center gap-2 py-12 text-center",
134
+ children: [
135
+ /* @__PURE__ */ m(u, { className: "h-8 w-8 text-muted-foreground" }),
136
+ /* @__PURE__ */ m("p", {
137
+ className: "text-sm font-medium",
138
+ children: D("planmagnet.myWork.empty", "No work assigned to you")
139
+ }),
140
+ /* @__PURE__ */ m("p", {
141
+ className: "text-xs text-muted-foreground",
142
+ children: O ? D("planmagnet.myWork.emptyHintClosed", "Nothing is assigned to you in any project yet.") : D("planmagnet.myWork.emptyHint", "Nothing open is assigned to you. Toggle \"Show completed\" to see recently finished work.")
143
+ })
144
+ ]
145
+ }) }) : /* @__PURE__ */ h("div", {
146
+ className: "space-y-6",
147
+ children: [B.map(({ group: e, items: t }) => /* @__PURE__ */ h("section", {
148
+ className: "space-y-2",
149
+ "data-testid": `my-work-group-${e}`,
150
+ children: [/* @__PURE__ */ h("div", {
151
+ className: "flex items-center gap-2",
152
+ children: [/* @__PURE__ */ m(_, {
153
+ variant: w[e],
154
+ children: T(D, e)
155
+ }), /* @__PURE__ */ h("span", {
156
+ className: "text-xs text-muted-foreground",
157
+ children: [
158
+ t.length,
159
+ " ",
160
+ t.length === 1 ? "item" : "items"
161
+ ]
162
+ })]
163
+ }), /* @__PURE__ */ m("div", {
164
+ className: "space-y-2",
165
+ children: t.map((e) => /* @__PURE__ */ m(v, {
166
+ ...s(() => E(`/tasks/task-detail-page?taskId=${encodeURIComponent(e.id)}`), e.title),
167
+ children: /* @__PURE__ */ h(y, {
168
+ className: "flex flex-wrap items-center gap-3 p-3",
169
+ children: [/* @__PURE__ */ h("div", {
170
+ className: "min-w-[12rem] flex-1 space-y-1",
171
+ children: [/* @__PURE__ */ m("p", {
172
+ className: "text-sm font-medium leading-tight",
173
+ children: e.title
174
+ }), /* @__PURE__ */ h("div", {
175
+ className: "flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground",
176
+ children: [
177
+ /* @__PURE__ */ m("span", { children: e.key }),
178
+ e.project && /* @__PURE__ */ m(_, {
179
+ variant: "outline",
180
+ children: e.project.name
181
+ }),
182
+ e.sprint && /* @__PURE__ */ m(_, {
183
+ variant: "secondary",
184
+ children: e.sprint.name
185
+ }),
186
+ e.dueDate && /* @__PURE__ */ h("span", {
187
+ className: e.isOverdue ? "text-destructive font-medium" : void 0,
188
+ children: ["Due ", new Date(e.dueDate).toLocaleDateString()]
189
+ }),
190
+ e.storyPoints != null && /* @__PURE__ */ h("span", { children: [e.storyPoints, " pts"] })
191
+ ]
192
+ })]
193
+ }), /* @__PURE__ */ h("div", {
194
+ className: "flex flex-wrap items-center gap-2",
195
+ onClick: c,
196
+ onKeyDown: (e) => e.stopPropagation(),
197
+ children: [
198
+ e.project ? /* @__PURE__ */ m(o, {
199
+ workItemId: e.id,
200
+ projectId: e.project.id,
201
+ value: e.status
202
+ }) : /* @__PURE__ */ m(a, {
203
+ workItemId: e.id,
204
+ field: "status",
205
+ value: e.status,
206
+ options: [
207
+ "BACKLOG",
208
+ "TODO",
209
+ "IN_PROGRESS",
210
+ "IN_REVIEW",
211
+ "TESTING",
212
+ "DONE",
213
+ "CANCELLED"
214
+ ],
215
+ label: "Status"
216
+ }),
217
+ /* @__PURE__ */ m(a, {
218
+ workItemId: e.id,
219
+ field: "priority",
220
+ value: e.priority,
221
+ options: n,
222
+ label: "Priority"
223
+ }),
224
+ /* @__PURE__ */ m(r, {
225
+ workItemId: e.id,
226
+ assigneeId: e.assigneeId
227
+ }),
228
+ /* @__PURE__ */ m(i, {
229
+ workItemId: e.id,
230
+ dueDate: e.dueDate
231
+ })
232
+ ]
233
+ })]
234
+ })
235
+ }, e.id))
236
+ })]
237
+ }, e)), z && /* @__PURE__ */ m("div", {
238
+ className: "flex justify-center",
239
+ children: /* @__PURE__ */ m("button", {
240
+ type: "button",
241
+ onClick: () => void I(),
242
+ disabled: A,
243
+ className: "px-3 py-1.5 text-sm rounded-md bg-muted text-foreground hover:bg-muted/80 disabled:opacity-50",
244
+ "data-testid": "my-work-load-more",
245
+ children: A ? D("planmagnet.myWork.loadingMore", "Loading…") : D("planmagnet.myWork.loadMore", "Load more ({{shown}} of {{total}})", {
246
+ shown: L.length,
247
+ total: R
248
+ })
249
+ })
250
+ })]
251
+ })]
252
+ });
253
+ }
254
+ //#endregion
255
+ export { E as TasksMyWorkPagePage };
256
+
257
+ //# sourceMappingURL=TasksMyWorkPagePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TasksMyWorkPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TasksMyWorkPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Badge, Card, CardContent, Switch } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { CalendarClock, ListChecks } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps, stopRowActivation } from '../utils/rowNavigation';\nimport {\n WORK_ITEM_PRIORITIES,\n WorkItemAssigneeSelect,\n WorkItemDueDateInput,\n WorkItemEnumSelect,\n WorkItemStatusSelect,\n} from '../controls/WorkItemControls';\n\n/**\n * My Work (BOFF-8048): the caller's own assigned work items across every project in the workspace, grouped by\n * due-date bucket (overdue first) and sorted the way `planmagnetMyWorkItems` returns them — server-computed, so\n * two clients (or two loads of the same client) never disagree on the grouping. See §3.2 of the BOFF-8048 design.\n *\n * Identity is implicit: there is no `actorId` argument on the query (it reads `x-actor-id` like every other\n * self-filtered list here), so there is nothing for this page to pass or fake.\n *\n * NOTE: `planmagnetMyWorkItems`, and the `isOverdue`/`dueGroup` fields on WorkItem, ship with the BOFF-8048 backend\n * PR. Until that PR deploys and the Hive CDN reconciles, this page's query will 400 in a live environment and\n * `bun run validate:schema` will fail on it locally (graphql/wspace.graphql has not been re-pulled with the new\n * fields yet) — expected, not a bug in this page. Re-run `schema-pull.sh` after the backend ships.\n */\n\nconst GET_MY_WORK_ITEMS = gql`\n query GetMyWorkItems(\n $workspaceId: ID!\n $filter: PlanmagnetMyWorkItemFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetMyWorkItems(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n key\n title\n status\n priority\n dueDate\n storyPoints\n isOverdue\n dueGroup\n assigneeId\n project {\n id\n name\n }\n sprint {\n id\n name\n }\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n totalCount\n }\n }\n`;\n\nconst PAGE_SIZE = 50;\n\ninterface MyWorkItemNode {\n id: string;\n key: string;\n title: string;\n status: string;\n priority: string;\n dueDate: string | null;\n storyPoints: number | null;\n isOverdue: boolean;\n dueGroup: 'OVERDUE' | 'DUE_TODAY' | 'DUE_THIS_WEEK' | 'DUE_LATER' | 'NO_DUE_DATE';\n assigneeId: string | null;\n project: { id: string; name: string } | null;\n sprint: { id: string; name: string } | null;\n}\n\ninterface MyWorkData {\n planmagnetMyWorkItems: {\n items: MyWorkItemNode[];\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n totalCount: number;\n };\n}\n\n// Server order already IS the group order (bucketRank, then due date, then priority, then key) — this list only\n// says how to label/style each bucket and which buckets to render as sections, never re-sorts.\nconst DUE_GROUP_ORDER = [\n 'OVERDUE',\n 'DUE_TODAY',\n 'DUE_THIS_WEEK',\n 'DUE_LATER',\n 'NO_DUE_DATE',\n] as const;\n\nconst DUE_GROUP_BADGE: Record<\n (typeof DUE_GROUP_ORDER)[number],\n 'destructive' | 'warning' | 'info' | 'secondary'\n> = {\n OVERDUE: 'destructive',\n DUE_TODAY: 'warning',\n DUE_THIS_WEEK: 'info',\n DUE_LATER: 'secondary',\n NO_DUE_DATE: 'secondary',\n};\n\nfunction dueGroupLabel(t: (key: string, fallback: string) => string, group: string): string {\n switch (group) {\n case 'OVERDUE':\n return t('planmagnet.myWork.group.overdue', 'Overdue');\n case 'DUE_TODAY':\n return t('planmagnet.myWork.group.dueToday', 'Due today');\n case 'DUE_THIS_WEEK':\n return t('planmagnet.myWork.group.dueThisWeek', 'Due this week');\n case 'DUE_LATER':\n return t('planmagnet.myWork.group.dueLater', 'Due later');\n default:\n return t('planmagnet.myWork.group.noDueDate', 'No due date');\n }\n}\n\nexport function TasksMyWorkPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const { t } = useI18n();\n const [includeClosed, setIncludeClosed] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n\n const { data, loading, error, fetchMore } = useQuery<MyWorkData>(GET_MY_WORK_ITEMS, {\n variables: { workspaceId, filter: { includeClosed }, pagination: { first: PAGE_SIZE } },\n skip: !workspaceId,\n });\n\n const loadMore = async () => {\n const after = data?.planmagnetMyWorkItems?.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 planmagnetMyWorkItems: {\n ...fetchMoreResult.planmagnetMyWorkItems,\n items: [\n ...prev.planmagnetMyWorkItems.items,\n ...fetchMoreResult.planmagnetMyWorkItems.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n // Only before the first load: a refetch (toggling \"Show completed\", or an inline edit's own list refetch) must\n // not blank the page — that would also wipe out any row-level refusal message still showing (BOFF-8043).\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 (\n <div className=\"p-6 text-destructive\">\n {t('planmagnet.myWork.errorLoading', 'My work could not be loaded')}: {error.message}\n </div>\n );\n }\n\n const items = data?.planmagnetMyWorkItems?.items ?? [];\n const total = data?.planmagnetMyWorkItems?.totalCount ?? items.length;\n const hasMore = data?.planmagnetMyWorkItems?.pageInfo?.hasNextPage ?? false;\n\n const groups = DUE_GROUP_ORDER.map((group) => ({\n group,\n items: items.filter((item) => item.dueGroup === group),\n })).filter((g) => g.items.length > 0);\n\n return (\n <div className=\"space-y-4 p-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold flex items-center gap-2\">\n <ListChecks className=\"h-6 w-6\" />\n {t('planmagnet.myWork.title', 'My Work')}\n </h1>\n <p className=\"text-sm text-muted-foreground\" data-testid=\"my-work-total\">\n {t(\n 'planmagnet.myWork.subtitle',\n '{{count}} item(s) assigned to you across every project',\n {\n count: total,\n }\n )}\n </p>\n </div>\n <label className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Switch\n checked={includeClosed}\n onCheckedChange={setIncludeClosed}\n aria-label={t('planmagnet.myWork.includeClosed', 'Show completed')}\n data-testid=\"my-work-include-closed\"\n />\n {t('planmagnet.myWork.includeClosed', 'Show completed')}\n </label>\n </div>\n\n {items.length === 0 ? (\n <Card>\n <CardContent className=\"flex flex-col items-center gap-2 py-12 text-center\">\n <CalendarClock className=\"h-8 w-8 text-muted-foreground\" />\n <p className=\"text-sm font-medium\">\n {t('planmagnet.myWork.empty', 'No work assigned to you')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {includeClosed\n ? t(\n 'planmagnet.myWork.emptyHintClosed',\n 'Nothing is assigned to you in any project yet.'\n )\n : t(\n 'planmagnet.myWork.emptyHint',\n 'Nothing open is assigned to you. Toggle \"Show completed\" to see recently finished work.'\n )}\n </p>\n </CardContent>\n </Card>\n ) : (\n <div className=\"space-y-6\">\n {groups.map(({ group, items: groupItems }) => (\n <section key={group} className=\"space-y-2\" data-testid={`my-work-group-${group}`}>\n <div className=\"flex items-center gap-2\">\n <Badge variant={DUE_GROUP_BADGE[group]}>{dueGroupLabel(t, group)}</Badge>\n <span className=\"text-xs text-muted-foreground\">\n {groupItems.length} {groupItems.length === 1 ? 'item' : 'items'}\n </span>\n </div>\n <div className=\"space-y-2\">\n {groupItems.map((item) => (\n <Card\n key={item.id}\n {...rowLinkProps(\n () =>\n navigate(`/tasks/task-detail-page?taskId=${encodeURIComponent(item.id)}`),\n item.title\n )}\n >\n <CardContent className=\"flex flex-wrap items-center gap-3 p-3\">\n <div className=\"min-w-[12rem] flex-1 space-y-1\">\n <p className=\"text-sm font-medium leading-tight\">{item.title}</p>\n <div className=\"flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground\">\n <span>{item.key}</span>\n {item.project && <Badge variant=\"outline\">{item.project.name}</Badge>}\n {item.sprint && <Badge variant=\"secondary\">{item.sprint.name}</Badge>}\n {item.dueDate && (\n <span\n className={\n item.isOverdue ? 'text-destructive font-medium' : undefined\n }\n >\n Due {new Date(item.dueDate).toLocaleDateString()}\n </span>\n )}\n {item.storyPoints != null && <span>{item.storyPoints} pts</span>}\n </div>\n </div>\n <div\n className=\"flex flex-wrap items-center gap-2\"\n onClick={stopRowActivation}\n onKeyDown={(e) => e.stopPropagation()}\n >\n {item.project ? (\n <WorkItemStatusSelect\n workItemId={item.id}\n projectId={item.project.id}\n value={item.status}\n />\n ) : (\n <WorkItemEnumSelect\n workItemId={item.id}\n field=\"status\"\n value={item.status}\n options={[\n 'BACKLOG',\n 'TODO',\n 'IN_PROGRESS',\n 'IN_REVIEW',\n 'TESTING',\n 'DONE',\n 'CANCELLED',\n ]}\n label=\"Status\"\n />\n )}\n <WorkItemEnumSelect\n workItemId={item.id}\n field=\"priority\"\n value={item.priority}\n options={WORK_ITEM_PRIORITIES}\n label=\"Priority\"\n />\n <WorkItemAssigneeSelect workItemId={item.id} assigneeId={item.assigneeId} />\n <WorkItemDueDateInput workItemId={item.id} dueDate={item.dueDate} />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n </section>\n ))}\n {hasMore && (\n <div className=\"flex justify-center\">\n <button\n type=\"button\"\n onClick={() => void 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=\"my-work-load-more\"\n >\n {loadingMore\n ? t('planmagnet.myWork.loadingMore', 'Loading…')\n : t('planmagnet.myWork.loadMore', 'Load more ({{shown}} of {{total}})', {\n shown: items.length,\n total,\n })}\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TemplatesTemplatesPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/TemplatesTemplatesPagePage.tsx"],"names":[],"mappings":"AA2DA,wBAAgB,0BAA0B,4CAsLzC"}
1
+ {"version":3,"file":"TemplatesTemplatesPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/TemplatesTemplatesPagePage.tsx"],"names":[],"mappings":"AA2DA,wBAAgB,0BAA0B,4CAqMzC"}
@@ -3,13 +3,13 @@ import { detailPath as n, stopRowActivation as r } from "../utils/rowNavigation.
3
3
  import { ApplyTemplateDialog as i, NewTemplateFromProjectDialog as a } from "../create/CreateDialogs.js";
4
4
  import { TemplateDeleteButton as o } from "../controls/TemplateControls.js";
5
5
  import { useDeferredValue as s, useState as c } from "react";
6
- import { LayoutTemplate as l, Pencil as u, Search as d } from "lucide-react";
7
- import { gql as f } from "@apollo/client";
8
- import { useQuery as p } from "@apollo/client/react";
9
- import { jsx as m, jsxs as h } from "react/jsx-runtime";
10
- import { Badge as g, Button as _, EmptyState as v, Input as y } from "@burdenoff/fe-libs/ui";
6
+ import { LayoutTemplate as l, Pencil as u, Plus as d, Search as f } from "lucide-react";
7
+ import { gql as p } from "@apollo/client";
8
+ import { useQuery as m } from "@apollo/client/react";
9
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
10
+ import { Badge as _, Button as v, EmptyState as y, Input as b } from "@burdenoff/fe-libs/ui";
11
11
  //#region src/planmagnet/pages/TemplatesTemplatesPagePage.tsx
12
- var b = f`
12
+ var x = p`
13
13
  query TemplatesList(
14
14
  $workspaceId: ID!
15
15
  $filter: PlanmagnetProjectTemplateFilter
@@ -37,27 +37,27 @@ var b = f`
37
37
  }
38
38
  }
39
39
  }
40
- `, x = 50;
41
- function S() {
42
- let { workspaceId: f } = e(), S = t(), [C, w] = c(""), [T, E] = c(!1), [D, O] = c(!1), k = s(C.trim()), A = {
43
- ...k ? { search: k } : {},
44
- ...T ? { isPublished: !0 } : {}
45
- }, { data: j, loading: M, error: N, fetchMore: P } = p(b, {
40
+ `, S = 50;
41
+ function C() {
42
+ let { workspaceId: p } = e(), C = t(), [w, T] = c(""), [E, D] = c(!1), [O, k] = c(!1), A = s(w.trim()), j = {
43
+ ...A ? { search: A } : {},
44
+ ...E ? { isPublished: !0 } : {}
45
+ }, { data: M, loading: N, error: P, fetchMore: F } = m(x, {
46
46
  variables: {
47
- workspaceId: f ?? "",
48
- filter: A,
49
- pagination: { first: x }
47
+ workspaceId: p ?? "",
48
+ filter: j,
49
+ pagination: { first: S }
50
50
  },
51
- skip: !f,
51
+ skip: !p,
52
52
  fetchPolicy: "cache-and-network"
53
- }), F = j?.planmagnetProjectTemplates, I = F?.items ?? [], L = async () => {
54
- let e = F?.pageInfo.endCursor;
53
+ }), I = M?.planmagnetProjectTemplates, L = I?.items ?? [], R = A !== "" || E, z = !!I && L.length === 0 && !R, B = async () => {
54
+ let e = I?.pageInfo.endCursor;
55
55
  if (e) {
56
- O(!0);
56
+ k(!0);
57
57
  try {
58
- await P({
58
+ await F({
59
59
  variables: { pagination: {
60
- first: x,
60
+ first: S,
61
61
  after: e
62
62
  } },
63
63
  updateQuery: (e, { fetchMoreResult: t }) => ({ planmagnetProjectTemplates: {
@@ -66,99 +66,106 @@ function S() {
66
66
  } })
67
67
  });
68
68
  } finally {
69
- O(!1);
69
+ k(!1);
70
70
  }
71
71
  }
72
72
  };
73
- return /* @__PURE__ */ h("div", {
73
+ return /* @__PURE__ */ g("div", {
74
74
  className: "space-y-6 p-6",
75
75
  "data-testid": "templates-page",
76
76
  children: [
77
- /* @__PURE__ */ h("div", {
77
+ /* @__PURE__ */ g("div", {
78
78
  className: "flex flex-wrap items-center justify-between gap-3",
79
- children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h1", {
79
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("h1", {
80
80
  className: "text-2xl font-semibold",
81
81
  children: "Project templates"
82
- }), /* @__PURE__ */ m("p", {
82
+ }), /* @__PURE__ */ h("p", {
83
83
  className: "text-sm text-muted-foreground",
84
- children: F ? `${F.totalCount} template${F.totalCount === 1 ? "" : "s"}` : "Reusable project configurations — capture one from a project, or start a project from one"
85
- })] }), /* @__PURE__ */ m(a, { onCreated: (e) => S(n("/templates/edit-template-page", e)) })]
84
+ children: I ? `${I.totalCount} template${I.totalCount === 1 ? "" : "s"}` : "Reusable project configurations — capture one from a project, or start a project from one"
85
+ })] }), !z && /* @__PURE__ */ h(a, { onCreated: (e) => C(n("/templates/edit-template-page", e)) })]
86
86
  }),
87
- /* @__PURE__ */ h("div", {
87
+ /* @__PURE__ */ g("div", {
88
88
  className: "flex flex-wrap items-center gap-3",
89
- children: [/* @__PURE__ */ h("div", {
89
+ children: [/* @__PURE__ */ g("div", {
90
90
  className: "relative w-full max-w-sm",
91
- children: [/* @__PURE__ */ m(d, { className: "absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" }), /* @__PURE__ */ m(y, {
91
+ children: [/* @__PURE__ */ h(f, { className: "absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" }), /* @__PURE__ */ h(b, {
92
92
  "aria-label": "Search templates",
93
93
  placeholder: "Search by name or description",
94
94
  className: "pl-8",
95
- value: C,
96
- onChange: (e) => w(e.target.value)
95
+ value: w,
96
+ onChange: (e) => T(e.target.value)
97
97
  })]
98
- }), /* @__PURE__ */ m("button", {
98
+ }), /* @__PURE__ */ h("button", {
99
99
  type: "button",
100
- "aria-pressed": T,
101
- onClick: () => E((e) => !e),
102
- className: `px-3 py-1.5 text-sm rounded-md ${T ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"}`,
100
+ "aria-pressed": E,
101
+ onClick: () => D((e) => !e),
102
+ className: `px-3 py-1.5 text-sm rounded-md ${E ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"}`,
103
103
  children: "Published only"
104
104
  })]
105
105
  }),
106
- N && /* @__PURE__ */ h("p", {
106
+ P && /* @__PURE__ */ g("p", {
107
107
  role: "alert",
108
108
  className: "text-sm text-destructive",
109
- children: ["Templates could not be loaded: ", N.message]
109
+ children: ["Templates could not be loaded: ", P.message]
110
110
  }),
111
- M && !j && /* @__PURE__ */ m("p", {
111
+ N && !M && /* @__PURE__ */ h("p", {
112
112
  className: "text-sm text-muted-foreground",
113
113
  children: "Loading templates…"
114
114
  }),
115
- F && I.length === 0 && /* @__PURE__ */ m(v, {
116
- icon: /* @__PURE__ */ m(l, { className: "h-8 w-8 text-muted-foreground" }),
117
- title: k || T ? "No templates match" : "No templates yet",
115
+ I && L.length === 0 && /* @__PURE__ */ h(y, {
116
+ icon: /* @__PURE__ */ h(l, { className: "h-8 w-8 text-muted-foreground" }),
117
+ title: R ? "No templates match" : "No templates yet",
118
118
  description: "Save an existing project's setup as a template, then start new projects from it in one step.",
119
- action: k || T ? void 0 : /* @__PURE__ */ m(a, { onCreated: (e) => S(n("/templates/edit-template-page", e)) })
119
+ action: z ? /* @__PURE__ */ h(a, {
120
+ onCreated: (e) => C(n("/templates/edit-template-page", e)),
121
+ trigger: /* @__PURE__ */ g(v, {
122
+ size: "sm",
123
+ "data-testid": "new-template-empty-trigger",
124
+ children: [/* @__PURE__ */ h(d, { className: "mr-1 h-4 w-4" }), "New template"]
125
+ })
126
+ }) : void 0
120
127
  }),
121
- /* @__PURE__ */ m("ul", {
128
+ /* @__PURE__ */ h("ul", {
122
129
  className: "grid grid-cols-1 gap-3 md:grid-cols-2",
123
- children: I.map((e) => /* @__PURE__ */ h("li", {
130
+ children: L.map((e) => /* @__PURE__ */ g("li", {
124
131
  "data-testid": "template-row",
125
132
  className: "flex flex-col gap-2 rounded-md border p-3",
126
133
  children: [
127
- /* @__PURE__ */ h("div", {
134
+ /* @__PURE__ */ g("div", {
128
135
  className: "flex items-start justify-between gap-2",
129
- children: [/* @__PURE__ */ h("div", {
136
+ children: [/* @__PURE__ */ g("div", {
130
137
  className: "min-w-0",
131
- children: [/* @__PURE__ */ m("p", {
138
+ children: [/* @__PURE__ */ h("p", {
132
139
  className: "truncate text-sm font-medium",
133
140
  children: e.name
134
- }), /* @__PURE__ */ m("p", {
141
+ }), /* @__PURE__ */ h("p", {
135
142
  className: "truncate text-xs text-muted-foreground",
136
143
  children: e.description ?? "No description"
137
144
  })]
138
- }), /* @__PURE__ */ h("div", {
145
+ }), /* @__PURE__ */ g("div", {
139
146
  className: "flex shrink-0 gap-1",
140
- children: [e.isBuiltIn && /* @__PURE__ */ m(g, {
147
+ children: [e.isBuiltIn && /* @__PURE__ */ h(_, {
141
148
  variant: "secondary",
142
149
  children: "Built-in"
143
- }), /* @__PURE__ */ m(g, {
150
+ }), /* @__PURE__ */ h(_, {
144
151
  variant: e.isPublished ? "success" : "outline",
145
152
  children: e.isPublished ? "Published" : "Draft"
146
153
  })]
147
154
  })]
148
155
  }),
149
- /* @__PURE__ */ h("div", {
156
+ /* @__PURE__ */ g("div", {
150
157
  className: "flex flex-wrap items-center gap-1",
151
- children: [e.category && /* @__PURE__ */ m(g, {
158
+ children: [e.category && /* @__PURE__ */ h(_, {
152
159
  variant: "outline",
153
160
  className: "text-xs",
154
161
  children: e.category
155
- }), e.tags.map((e) => /* @__PURE__ */ m(g, {
162
+ }), e.tags.map((e) => /* @__PURE__ */ h(_, {
156
163
  variant: "outline",
157
164
  className: "text-xs",
158
165
  children: e
159
166
  }, e))]
160
167
  }),
161
- /* @__PURE__ */ h("p", {
168
+ /* @__PURE__ */ g("p", {
162
169
  className: "text-xs text-muted-foreground",
163
170
  children: [
164
171
  "Used ",
@@ -167,42 +174,42 @@ function S() {
167
174
  e.usageCount === 1 ? "" : "s"
168
175
  ]
169
176
  }),
170
- /* @__PURE__ */ h("div", {
177
+ /* @__PURE__ */ g("div", {
171
178
  className: "flex items-center justify-between gap-2 border-t pt-2",
172
179
  onClick: r,
173
- children: [/* @__PURE__ */ h("div", {
180
+ children: [/* @__PURE__ */ g("div", {
174
181
  className: "flex gap-2",
175
- children: [/* @__PURE__ */ h(_, {
182
+ children: [/* @__PURE__ */ g(v, {
176
183
  size: "sm",
177
184
  variant: "outline",
178
- onClick: () => S(n("/templates/edit-template-page", e.id)),
185
+ onClick: () => C(n("/templates/edit-template-page", e.id)),
179
186
  "data-testid": "template-edit",
180
- children: [/* @__PURE__ */ m(u, { className: "mr-1 h-4 w-4" }), "Edit"]
181
- }), /* @__PURE__ */ m(o, {
187
+ children: [/* @__PURE__ */ h(u, { className: "mr-1 h-4 w-4" }), "Edit"]
188
+ }), /* @__PURE__ */ h(o, {
182
189
  templateId: e.id,
183
190
  name: e.name,
184
191
  isBuiltIn: e.isBuiltIn
185
192
  })]
186
- }), /* @__PURE__ */ m(i, {
193
+ }), /* @__PURE__ */ h(i, {
187
194
  templateId: e.id,
188
195
  templateName: e.name,
189
- onApplied: (e) => S(n("/projects/project-detail-page", e))
196
+ onApplied: (e) => C(n("/projects/project-detail-page", e))
190
197
  })]
191
198
  })
192
199
  ]
193
200
  }, e.id))
194
201
  }),
195
- F?.pageInfo.hasNextPage && /* @__PURE__ */ m(_, {
202
+ I?.pageInfo.hasNextPage && /* @__PURE__ */ h(v, {
196
203
  size: "sm",
197
204
  variant: "outline",
198
- onClick: () => void L(),
199
- disabled: D,
200
- children: D ? "Loading…" : `Show more (${I.length} of ${F.totalCount})`
205
+ onClick: () => void B(),
206
+ disabled: O,
207
+ children: O ? "Loading…" : `Show more (${L.length} of ${I.totalCount})`
201
208
  })
202
209
  ]
203
210
  });
204
211
  }
205
212
  //#endregion
206
- export { S as TemplatesTemplatesPagePage };
213
+ export { C as TemplatesTemplatesPagePage };
207
214
 
208
215
  //# sourceMappingURL=TemplatesTemplatesPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TemplatesTemplatesPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TemplatesTemplatesPagePage.tsx"],"sourcesContent":["import { useDeferredValue, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Badge, Button, EmptyState, Input } from '@burdenoff/fe-libs/ui';\nimport { LayoutTemplate, Pencil, Search } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { ApplyTemplateDialog, NewTemplateFromProjectDialog } from '../create/CreateDialogs';\nimport { TemplateDeleteButton } from '../controls/TemplateControls';\nimport { detailPath, stopRowActivation } from '../utils/rowNavigation';\n\n/**\n * Project templates browse surface (BOFF-8047, capability register \"Project templates\": model/API/authz were\n * already real and prod-verified — `planmagnetProjectTemplates`/Create/Update/Delete/Apply/CreateFromProject —\n * gate 3 (UI) was the only gap; \"No UI anywhere\" until now.\n */\n\nconst TEMPLATES = gql`\n query TemplatesList(\n $workspaceId: ID!\n $filter: PlanmagnetProjectTemplateFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetProjectTemplates(\n workspaceId: $workspaceId\n filter: $filter\n pagination: $pagination\n ) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n name\n description\n category\n isBuiltIn\n isPublished\n tags\n usageCount\n }\n }\n }\n`;\n\ninterface TemplateRow {\n id: string;\n name: string;\n description: string | null;\n category: string | null;\n isBuiltIn: boolean;\n isPublished: boolean;\n tags: string[];\n usageCount: number;\n}\n\nconst PAGE = 50;\n\nexport function TemplatesTemplatesPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [search, setSearch] = useState('');\n const [publishedOnly, setPublishedOnly] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n const term = useDeferredValue(search.trim());\n\n const filter = {\n ...(term ? { search: term } : {}),\n ...(publishedOnly ? { isPublished: true } : {}),\n };\n\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetProjectTemplates: {\n totalCount: number;\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n items: TemplateRow[];\n };\n }>(TEMPLATES, {\n variables: { workspaceId: workspaceId ?? '', filter, pagination: { first: PAGE } },\n skip: !workspaceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const list = data?.planmagnetProjectTemplates;\n const templates = list?.items ?? [];\n\n const loadMore = async () => {\n const after = list?.pageInfo.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: PAGE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetProjectTemplates: {\n ...fetchMoreResult.planmagnetProjectTemplates,\n items: [\n ...prev.planmagnetProjectTemplates.items,\n ...fetchMoreResult.planmagnetProjectTemplates.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\" data-testid=\"templates-page\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Project templates</h1>\n <p className=\"text-sm text-muted-foreground\">\n {list\n ? `${list.totalCount} template${list.totalCount === 1 ? '' : 's'}`\n : 'Reusable project configurations — capture one from a project, or start a project from one'}\n </p>\n </div>\n <NewTemplateFromProjectDialog\n onCreated={(id) => navigate(detailPath('/templates/edit-template-page', id))}\n />\n </div>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"relative w-full max-w-sm\">\n <Search className=\"absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground\" />\n <Input\n aria-label=\"Search templates\"\n placeholder=\"Search by name or description\"\n className=\"pl-8\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n <button\n type=\"button\"\n aria-pressed={publishedOnly}\n onClick={() => setPublishedOnly((v) => !v)}\n className={`px-3 py-1.5 text-sm rounded-md ${publishedOnly ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted'}`}\n >\n Published only\n </button>\n </div>\n\n {error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n Templates could not be loaded: {error.message}\n </p>\n )}\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading templates…</p>}\n {list && templates.length === 0 && (\n <EmptyState\n icon={<LayoutTemplate className=\"h-8 w-8 text-muted-foreground\" />}\n title={term || publishedOnly ? 'No templates match' : 'No templates yet'}\n description=\"Save an existing project's setup as a template, then start new projects from it in one step.\"\n action={\n !(term || publishedOnly) ? (\n <NewTemplateFromProjectDialog\n onCreated={(id) => navigate(detailPath('/templates/edit-template-page', id))}\n />\n ) : undefined\n }\n />\n )}\n\n <ul className=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\n {templates.map((template) => (\n <li\n key={template.id}\n data-testid=\"template-row\"\n className=\"flex flex-col gap-2 rounded-md border p-3\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">{template.name}</p>\n <p className=\"truncate text-xs text-muted-foreground\">\n {template.description ?? 'No description'}\n </p>\n </div>\n <div className=\"flex shrink-0 gap-1\">\n {template.isBuiltIn && <Badge variant=\"secondary\">Built-in</Badge>}\n <Badge variant={template.isPublished ? 'success' : 'outline'}>\n {template.isPublished ? 'Published' : 'Draft'}\n </Badge>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-1\">\n {template.category && (\n <Badge variant=\"outline\" className=\"text-xs\">\n {template.category}\n </Badge>\n )}\n {template.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n Used {template.usageCount} time{template.usageCount === 1 ? '' : 's'}\n </p>\n <div\n className=\"flex items-center justify-between gap-2 border-t pt-2\"\n onClick={stopRowActivation}\n >\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(detailPath('/templates/edit-template-page', template.id))}\n data-testid=\"template-edit\"\n >\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <TemplateDeleteButton\n templateId={template.id}\n name={template.name}\n isBuiltIn={template.isBuiltIn}\n />\n </div>\n <ApplyTemplateDialog\n templateId={template.id}\n templateName={template.name}\n onApplied={(projectId) =>\n navigate(detailPath('/projects/project-detail-page', projectId))\n }\n />\n </div>\n </li>\n ))}\n </ul>\n {list?.pageInfo.hasNextPage && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => void loadMore()} disabled={loadingMore}>\n {loadingMore ? 'Loading…' : `Show more (${templates.length} of ${list.totalCount})`}\n </Button>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TemplatesTemplatesPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TemplatesTemplatesPagePage.tsx"],"sourcesContent":["import { useDeferredValue, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Badge, Button, EmptyState, Input } from '@burdenoff/fe-libs/ui';\nimport { LayoutTemplate, Pencil, Plus, Search } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { ApplyTemplateDialog, NewTemplateFromProjectDialog } from '../create/CreateDialogs';\nimport { TemplateDeleteButton } from '../controls/TemplateControls';\nimport { detailPath, stopRowActivation } from '../utils/rowNavigation';\n\n/**\n * Project templates browse surface (BOFF-8047, capability register \"Project templates\": model/API/authz were\n * already real and prod-verified — `planmagnetProjectTemplates`/Create/Update/Delete/Apply/CreateFromProject —\n * gate 3 (UI) was the only gap; \"No UI anywhere\" until now.\n */\n\nconst TEMPLATES = gql`\n query TemplatesList(\n $workspaceId: ID!\n $filter: PlanmagnetProjectTemplateFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetProjectTemplates(\n workspaceId: $workspaceId\n filter: $filter\n pagination: $pagination\n ) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n name\n description\n category\n isBuiltIn\n isPublished\n tags\n usageCount\n }\n }\n }\n`;\n\ninterface TemplateRow {\n id: string;\n name: string;\n description: string | null;\n category: string | null;\n isBuiltIn: boolean;\n isPublished: boolean;\n tags: string[];\n usageCount: number;\n}\n\nconst PAGE = 50;\n\nexport function TemplatesTemplatesPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [search, setSearch] = useState('');\n const [publishedOnly, setPublishedOnly] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n const term = useDeferredValue(search.trim());\n\n const filter = {\n ...(term ? { search: term } : {}),\n ...(publishedOnly ? { isPublished: true } : {}),\n };\n\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetProjectTemplates: {\n totalCount: number;\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n items: TemplateRow[];\n };\n }>(TEMPLATES, {\n variables: { workspaceId: workspaceId ?? '', filter, pagination: { first: PAGE } },\n skip: !workspaceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const list = data?.planmagnetProjectTemplates;\n const templates = list?.items ?? [];\n const isFiltered = term !== '' || publishedOnly;\n // The EmptyState below only offers its own \"New template\" action once the list is confirmed empty AND\n // unfiltered (same gate as its `title`/`description`). Whenever that action is showing, drop the header's\n // button instead of duplicating it (BOFF-8047 prod verification: both rendered at once, sharing\n // data-testid=\"new-template-trigger\"). Keep the header's button for every OTHER state — loading, a\n // populated list, or a filtered-to-empty list, where the EmptyState offers no action of its own.\n const showsEmptyStateAction = !!list && templates.length === 0 && !isFiltered;\n\n const loadMore = async () => {\n const after = list?.pageInfo.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: PAGE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetProjectTemplates: {\n ...fetchMoreResult.planmagnetProjectTemplates,\n items: [\n ...prev.planmagnetProjectTemplates.items,\n ...fetchMoreResult.planmagnetProjectTemplates.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\" data-testid=\"templates-page\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Project templates</h1>\n <p className=\"text-sm text-muted-foreground\">\n {list\n ? `${list.totalCount} template${list.totalCount === 1 ? '' : 's'}`\n : 'Reusable project configurations — capture one from a project, or start a project from one'}\n </p>\n </div>\n {!showsEmptyStateAction && (\n <NewTemplateFromProjectDialog\n onCreated={(id) => navigate(detailPath('/templates/edit-template-page', id))}\n />\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"relative w-full max-w-sm\">\n <Search className=\"absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground\" />\n <Input\n aria-label=\"Search templates\"\n placeholder=\"Search by name or description\"\n className=\"pl-8\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n <button\n type=\"button\"\n aria-pressed={publishedOnly}\n onClick={() => setPublishedOnly((v) => !v)}\n className={`px-3 py-1.5 text-sm rounded-md ${publishedOnly ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted'}`}\n >\n Published only\n </button>\n </div>\n\n {error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n Templates could not be loaded: {error.message}\n </p>\n )}\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading templates…</p>}\n {list && templates.length === 0 && (\n <EmptyState\n icon={<LayoutTemplate className=\"h-8 w-8 text-muted-foreground\" />}\n title={isFiltered ? 'No templates match' : 'No templates yet'}\n description=\"Save an existing project's setup as a template, then start new projects from it in one step.\"\n action={\n showsEmptyStateAction ? (\n <NewTemplateFromProjectDialog\n onCreated={(id) => navigate(detailPath('/templates/edit-template-page', id))}\n trigger={\n <Button size=\"sm\" data-testid=\"new-template-empty-trigger\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New template\n </Button>\n }\n />\n ) : undefined\n }\n />\n )}\n\n <ul className=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\n {templates.map((template) => (\n <li\n key={template.id}\n data-testid=\"template-row\"\n className=\"flex flex-col gap-2 rounded-md border p-3\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">{template.name}</p>\n <p className=\"truncate text-xs text-muted-foreground\">\n {template.description ?? 'No description'}\n </p>\n </div>\n <div className=\"flex shrink-0 gap-1\">\n {template.isBuiltIn && <Badge variant=\"secondary\">Built-in</Badge>}\n <Badge variant={template.isPublished ? 'success' : 'outline'}>\n {template.isPublished ? 'Published' : 'Draft'}\n </Badge>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-1\">\n {template.category && (\n <Badge variant=\"outline\" className=\"text-xs\">\n {template.category}\n </Badge>\n )}\n {template.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n Used {template.usageCount} time{template.usageCount === 1 ? '' : 's'}\n </p>\n <div\n className=\"flex items-center justify-between gap-2 border-t pt-2\"\n onClick={stopRowActivation}\n >\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(detailPath('/templates/edit-template-page', template.id))}\n data-testid=\"template-edit\"\n >\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <TemplateDeleteButton\n templateId={template.id}\n name={template.name}\n isBuiltIn={template.isBuiltIn}\n />\n </div>\n <ApplyTemplateDialog\n templateId={template.id}\n templateName={template.name}\n onApplied={(projectId) =>\n navigate(detailPath('/projects/project-detail-page', projectId))\n }\n />\n </div>\n </li>\n ))}\n </ul>\n {list?.pageInfo.hasNextPage && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => void loadMore()} disabled={loadingMore}>\n {loadingMore ? 'Loading…' : `Show more (${templates.length} of ${list.totalCount})`}\n </Button>\n )}\n </div>\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.928.7",
3
+ "version": "2026.928.9",
4
4
  "description": "Burdenoff PlanMagnet Microfrontend",
5
5
  "type": "module",
6
6
  "files": [