@burdenoff/microfe-planmagnet 2026.925.10 → 2026.925.12

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,4 @@
1
+ export declare function WorkItemActivity({ workItemId }: {
2
+ workItemId: string;
3
+ }): import("react/jsx-runtime").JSX.Element;
4
+ //# sourceMappingURL=WorkItemActivity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkItemActivity.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/comments/WorkItemActivity.tsx"],"names":[],"mappings":"AAyDA,wBAAgB,gBAAgB,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,2CA+CtE"}
@@ -0,0 +1,97 @@
1
+ import { PersonName as e } from "../people/People.js";
2
+ import { gql as t } from "@apollo/client";
3
+ import { useQuery as n } from "@apollo/client/react";
4
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
5
+ import { Card as a, CardContent as o, CardHeader as s, CardTitle as c } from "@burdenoff/fe-libs/ui";
6
+ //#region src/planmagnet/comments/WorkItemActivity.tsx
7
+ var l = t`
8
+ query WorkItemActivity($workItemId: ID!) {
9
+ planmagnetWorkItemActivity(workItemId: $workItemId, first: 50) {
10
+ totalCount
11
+ truncated
12
+ items {
13
+ id
14
+ kind
15
+ actorId
16
+ at
17
+ fromStatus
18
+ toStatus
19
+ excerpt
20
+ }
21
+ }
22
+ }
23
+ `, u = (e) => e ? e.charAt(0) + e.slice(1).toLowerCase().replace(/_/g, " ") : "";
24
+ function d(e) {
25
+ switch (e.kind) {
26
+ case "CREATED": return e.toStatus ? `Created in ${u(e.toStatus)}` : "Created";
27
+ case "STATUS_CHANGED": return `Moved from ${u(e.fromStatus)} to ${u(e.toStatus)}`;
28
+ case "COMMENTED": return `Commented: “${e.excerpt ?? ""}”`;
29
+ default: return e.kind;
30
+ }
31
+ }
32
+ function f({ workItemId: t }) {
33
+ let { data: u, loading: f, error: p } = n(l, {
34
+ variables: { workItemId: t },
35
+ skip: !t
36
+ }), m = u?.planmagnetWorkItemActivity, h = m?.items ?? [];
37
+ return /* @__PURE__ */ i(a, {
38
+ "data-testid": "work-item-activity",
39
+ children: [/* @__PURE__ */ r(s, { children: /* @__PURE__ */ r(c, { children: "Activity" }) }), /* @__PURE__ */ i(o, {
40
+ className: "space-y-3",
41
+ children: [
42
+ f && !u && /* @__PURE__ */ r("p", {
43
+ className: "text-sm text-muted-foreground",
44
+ children: "Loading activity…"
45
+ }),
46
+ p && /* @__PURE__ */ i("p", {
47
+ role: "alert",
48
+ className: "text-sm text-destructive",
49
+ children: ["Activity could not be loaded: ", p.message]
50
+ }),
51
+ /* @__PURE__ */ r("ol", {
52
+ className: "space-y-3",
53
+ children: h.map((t, n) => /* @__PURE__ */ i("li", {
54
+ className: "flex gap-3",
55
+ "data-testid": "activity-event",
56
+ children: [/* @__PURE__ */ i("div", {
57
+ className: "flex flex-col items-center",
58
+ children: [/* @__PURE__ */ r("div", { className: "h-2.5 w-2.5 rounded-full bg-primary mt-1.5" }), n < h.length - 1 && /* @__PURE__ */ r("div", { className: "w-px flex-1 bg-border" })]
59
+ }), /* @__PURE__ */ i("div", {
60
+ className: "pb-1 min-w-0",
61
+ children: [/* @__PURE__ */ r("p", {
62
+ className: "text-sm break-words",
63
+ children: d(t)
64
+ }), /* @__PURE__ */ i("p", {
65
+ className: "text-xs text-muted-foreground",
66
+ children: [
67
+ t.actorId ? /* @__PURE__ */ r(e, { id: t.actorId }) : "Unknown",
68
+ " ·",
69
+ " ",
70
+ new Date(t.at).toLocaleString()
71
+ ]
72
+ })]
73
+ })]
74
+ }, t.id))
75
+ }),
76
+ m?.truncated && /* @__PURE__ */ i("p", {
77
+ className: "text-xs text-muted-foreground",
78
+ children: [
79
+ "Showing the latest ",
80
+ h.length,
81
+ " of ",
82
+ m.totalCount,
83
+ " events."
84
+ ]
85
+ }),
86
+ /* @__PURE__ */ r("p", {
87
+ className: "text-xs text-muted-foreground",
88
+ children: "Creation, status changes and comments are recorded. Other edits are not tracked yet."
89
+ })
90
+ ]
91
+ })]
92
+ });
93
+ }
94
+ //#endregion
95
+ export { f as WorkItemActivity };
96
+
97
+ //# sourceMappingURL=WorkItemActivity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkItemActivity.js","names":[],"sources":["../../../src/planmagnet/comments/WorkItemActivity.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { PersonName } from '../people/People';\n\n/**\n * A work item's recorded history (P1.4). This card used to synthesise three\n * entries client-side, including \"Status changed to X\" attributed to whoever\n * was assigned, at the item's updatedAt. That was fabricated history. It now\n * shows only what the server has recorded: creation, status changes and\n * comments. Other edits are not recorded yet, and the card says so.\n */\n\nconst ACTIVITY = gql`\n query WorkItemActivity($workItemId: ID!) {\n planmagnetWorkItemActivity(workItemId: $workItemId, first: 50) {\n totalCount\n truncated\n items {\n id\n kind\n actorId\n at\n fromStatus\n toStatus\n excerpt\n }\n }\n }\n`;\n\ninterface ActivityEvent {\n id: string;\n kind: string;\n actorId: string | null;\n at: string;\n fromStatus: string | null;\n toStatus: string | null;\n excerpt: string | null;\n}\n\nconst statusLabel = (status: string | null) =>\n status ? status.charAt(0) + status.slice(1).toLowerCase().replace(/_/g, ' ') : '';\n\nfunction describe(event: ActivityEvent): string {\n switch (event.kind) {\n case 'CREATED':\n return event.toStatus ? `Created in ${statusLabel(event.toStatus)}` : 'Created';\n case 'STATUS_CHANGED':\n return `Moved from ${statusLabel(event.fromStatus)} to ${statusLabel(event.toStatus)}`;\n case 'COMMENTED':\n return `Commented: “${event.excerpt ?? ''}”`;\n default:\n return event.kind;\n }\n}\n\nexport function WorkItemActivity({ workItemId }: { workItemId: string }) {\n const { data, loading, error } = useQuery<{\n planmagnetWorkItemActivity: { totalCount: number; truncated: boolean; items: ActivityEvent[] };\n }>(ACTIVITY, { variables: { workItemId }, skip: !workItemId });\n const activity = data?.planmagnetWorkItemActivity;\n const events = activity?.items ?? [];\n\n return (\n <Card data-testid=\"work-item-activity\">\n <CardHeader>\n <CardTitle>Activity</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading activity…</p>}\n {error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n Activity could not be loaded: {error.message}\n </p>\n )}\n <ol className=\"space-y-3\">\n {events.map((event, i) => (\n <li key={event.id} className=\"flex gap-3\" data-testid=\"activity-event\">\n <div className=\"flex flex-col items-center\">\n <div className=\"h-2.5 w-2.5 rounded-full bg-primary mt-1.5\" />\n {i < events.length - 1 && <div className=\"w-px flex-1 bg-border\" />}\n </div>\n <div className=\"pb-1 min-w-0\">\n <p className=\"text-sm break-words\">{describe(event)}</p>\n <p className=\"text-xs text-muted-foreground\">\n {event.actorId ? <PersonName id={event.actorId} /> : 'Unknown'} ·{' '}\n {new Date(event.at).toLocaleString()}\n </p>\n </div>\n </li>\n ))}\n </ol>\n {activity?.truncated && (\n <p className=\"text-xs text-muted-foreground\">\n Showing the latest {events.length} of {activity.totalCount} events.\n </p>\n )}\n <p className=\"text-xs text-muted-foreground\">\n Creation, status changes and comments are recorded. Other edits are not tracked yet.\n </p>\n </CardContent>\n </Card>\n );\n}\n"],"mappings":";;;;;;AAaA,IAAM,IAAW,CAAG;;;;;;;;;;;;;;;;GA4Bd,KAAe,MACnB,IAAS,EAAO,OAAO,EAAE,GAAG,EAAO,MAAM,EAAE,CAAC,aAAa,CAAC,QAAQ,MAAM,IAAI,GAAG;AAEjF,SAAS,EAAS,GAA8B;AAC9C,SAAQ,EAAM,MAAd;EACE,KAAK,UACH,QAAO,EAAM,WAAW,cAAc,EAAY,EAAM,SAAS,KAAK;EACxE,KAAK,iBACH,QAAO,cAAc,EAAY,EAAM,WAAW,CAAC,MAAM,EAAY,EAAM,SAAS;EACtF,KAAK,YACH,QAAO,eAAe,EAAM,WAAW,GAAG;EAC5C,QACE,QAAO,EAAM;;;AAInB,SAAgB,EAAiB,EAAE,iBAAsC;CACvE,IAAM,EAAE,SAAM,YAAS,aAAU,EAE9B,GAAU;EAAE,WAAW,EAAE,eAAY;EAAE,MAAM,CAAC;EAAY,CAAC,EACxD,IAAW,GAAM,4BACjB,IAAS,GAAU,SAAS,EAAE;AAEpC,QACE,kBAAC,GAAD;EAAM,eAAY;YAAlB,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,YAAoB,CAAA,EACpB,CAAA,EACb,kBAAC,GAAD;GAAa,WAAU;aAAvB;IACG,KAAW,CAAC,KAAQ,kBAAC,KAAD;KAAG,WAAU;eAAgC;KAAqB,CAAA;IACtF,KACC,kBAAC,KAAD;KAAG,MAAK;KAAQ,WAAU;eAA1B,CAAqD,kCACpB,EAAM,QACnC;;IAEN,kBAAC,MAAD;KAAI,WAAU;eACX,EAAO,KAAK,GAAO,MAClB,kBAAC,MAAD;MAAmB,WAAU;MAAa,eAAY;gBAAtD,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAK,WAAU,8CAA+C,CAAA,EAC7D,IAAI,EAAO,SAAS,KAAK,kBAAC,OAAD,EAAK,WAAU,yBAA0B,CAAA,CAC/D;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAuB,EAAS,EAAM;QAAK,CAAA,EACxD,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAM,UAAU,kBAAC,GAAD,EAAY,IAAI,EAAM,SAAW,CAAA,GAAG;SAAU;SAAG;SACjE,IAAI,KAAK,EAAM,GAAG,CAAC,gBAAgB;SAClC;UACA;SACH;QAZI,EAAM,GAYV,CACL;KACC,CAAA;IACJ,GAAU,aACT,kBAAC,KAAD;KAAG,WAAU;eAAb;MAA6C;MACvB,EAAO;MAAO;MAAK,EAAS;MAAW;MACzD;;IAEN,kBAAC,KAAD;KAAG,WAAU;eAAgC;KAEzC,CAAA;IACQ;KACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"WorkItemComments.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/comments/WorkItemComments.tsx"],"names":[],"mappings":"AAqGA,wBAAgB,gBAAgB,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,2CA+GtE"}
1
+ {"version":3,"file":"WorkItemComments.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/comments/WorkItemComments.tsx"],"names":[],"mappings":"AAqGA,wBAAgB,gBAAgB,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,2CAkHtE"}
@@ -59,7 +59,7 @@ function N({ workItemId: t }) {
59
59
  },
60
60
  skip: !t
61
61
  }), [k, j] = o(D, {
62
- refetchQueries: ["WorkItemComments"],
62
+ refetchQueries: ["WorkItemComments", "WorkItemActivity"],
63
63
  awaitRefetchQueries: !0
64
64
  }), N = g?.planmagnetWorkItemComments, F = N?.items ?? [], I = u.trim(), L = I.length > w;
65
65
  return /* @__PURE__ */ l(b, {
@@ -162,7 +162,7 @@ function N({ workItemId: t }) {
162
162
  }
163
163
  function P({ comment: e, mine: i, onError: a }) {
164
164
  let [s, b] = r(!1), [x, S] = r(e.body), C = a, w = {
165
- refetchQueries: ["WorkItemComments"],
165
+ refetchQueries: ["WorkItemComments", "WorkItemActivity"],
166
166
  awaitRefetchQueries: !0
167
167
  }, [T, E] = o(O, w), [D, M] = o(k, w);
168
168
  return /* @__PURE__ */ l("li", {
@@ -1 +1 @@
1
- {"version":3,"file":"WorkItemComments.js","names":[],"sources":["../../../src/planmagnet/comments/WorkItemComments.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { MessageSquare } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { PersonName } from '../people/People';\nimport { throwOnError } from '../utils/mutationResult';\n\n/**\n * Discussion on a work item (P1.4). Anyone who can read the item sees the\n * thread; posting needs edit rights on work items; only a comment's author\n * can edit or delete it. The server enforces all three and says why it refused\n * (shown inline). The controls here only avoid offering what would be refused.\n */\n\nconst MAX_LENGTH = 10_000;\nconst PAGE = 50;\n\nconst COMMENTS = gql`\n query WorkItemComments($workItemId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetWorkItemComments(workItemId: $workItemId, pagination: $pagination) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n authorId\n body\n createdAt\n editedAt\n }\n }\n }\n`;\nconst ADD = gql`\n mutation WorkItemCommentAdd($input: PlanmagnetAddWorkItemCommentInput!) {\n planmagnetAddWorkItemComment(input: $input) {\n id\n }\n }\n`;\nconst EDIT = gql`\n mutation WorkItemCommentEdit($id: ID!, $body: String!) {\n planmagnetEditWorkItemComment(id: $id, body: $body) {\n id\n body\n editedAt\n }\n }\n`;\nconst DELETE = gql`\n mutation WorkItemCommentDelete($id: ID!) {\n planmagnetDeleteWorkItemComment(id: $id)\n }\n`;\n\ninterface Comment {\n id: string;\n authorId: string;\n body: string;\n createdAt: string;\n editedAt: string | null;\n}\n\ninterface CommentsData {\n planmagnetWorkItemComments: {\n totalCount: number;\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n items: Comment[];\n };\n}\n\nconst messageOf = (e: unknown, fallback: string) => (e instanceof Error ? e.message : fallback);\nconst when = (iso: string) => new Date(iso).toLocaleString();\n\nfunction ErrorLine({ message }: { message: string }) {\n return (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {message}\n </p>\n );\n}\n\nexport function WorkItemComments({ workItemId }: { workItemId: string }) {\n const { currentUser } = usePlanMagnet();\n const [draft, setDraft] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [loadingMore, setLoadingMore] = useState(false);\n\n const {\n data,\n loading,\n error: loadError,\n fetchMore,\n } = useQuery<CommentsData>(COMMENTS, {\n variables: { workItemId, pagination: { first: PAGE } },\n skip: !workItemId,\n });\n const refetch = { refetchQueries: ['WorkItemComments'], awaitRefetchQueries: true };\n const [add, addState] = useMutation(ADD, refetch);\n\n const thread = data?.planmagnetWorkItemComments;\n const comments = thread?.items ?? [];\n const trimmed = draft.trim();\n const tooLong = trimmed.length > MAX_LENGTH;\n\n const post = async () => {\n setError(null);\n try {\n await throwOnError(add({ variables: { input: { workItemId, body: trimmed } } }));\n setDraft('');\n } catch (e) {\n setError(messageOf(e, 'The comment could not be posted.'));\n }\n };\n\n const loadMore = async () => {\n const after = thread?.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 planmagnetWorkItemComments: {\n ...fetchMoreResult.planmagnetWorkItemComments,\n items: [\n ...prev.planmagnetWorkItemComments.items,\n ...fetchMoreResult.planmagnetWorkItemComments.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n return (\n <Card data-testid=\"work-item-comments\">\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <MessageSquare className=\"h-4 w-4\" />\n Discussion {thread ? `(${thread.totalCount})` : ''}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {error && <ErrorLine message={error} />}\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading comments…</p>}\n {loadError && <ErrorLine message={`Comments could not be loaded: ${loadError.message}`} />}\n {thread && comments.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No comments yet.</p>\n )}\n <ol className=\"space-y-3\">\n {comments.map((comment) => (\n <CommentRow\n key={comment.id}\n comment={comment}\n mine={comment.authorId === currentUser?.id}\n onError={setError}\n />\n ))}\n </ol>\n {thread?.pageInfo.hasNextPage && (\n <Button size=\"sm\" variant=\"outline\" onClick={loadMore} disabled={loadingMore}>\n {loadingMore ? 'Loading…' : `Show more (${comments.length} of ${thread.totalCount})`}\n </Button>\n )}\n <div className=\"space-y-2 border-t pt-4\">\n <label htmlFor={`comment-${workItemId}`} className=\"sr-only\">\n Add a comment\n </label>\n <textarea\n id={`comment-${workItemId}`}\n className=\"w-full min-h-[80px] rounded-md border border-input bg-background px-3 py-2 text-sm\"\n placeholder=\"Add a comment…\"\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n />\n {tooLong && <ErrorLine message={`A comment can be at most ${MAX_LENGTH} characters.`} />}\n <div className=\"flex justify-end\">\n <Button\n size=\"sm\"\n onClick={post}\n disabled={!trimmed || tooLong || addState.loading}\n data-testid=\"comment-post\"\n >\n {addState.loading ? 'Posting…' : 'Comment'}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n );\n}\n\n/**\n * Refusals are reported to the PANEL, not kept in the row: a refused edit or\n * delete still refetches the thread, and when the comment is gone (deleted\n * elsewhere) the row unmounts, taking a row-level message with it. Seen in the\n * prod verification: the comment vanished and the reason was never shown.\n */\nfunction CommentRow({\n comment,\n mine,\n onError,\n}: {\n comment: Comment;\n mine: boolean;\n onError: (message: string | null) => void;\n}) {\n const [editing, setEditing] = useState(false);\n const [text, setText] = useState(comment.body);\n const setError = onError;\n const refetch = { refetchQueries: ['WorkItemComments'], awaitRefetchQueries: true };\n const [edit, editState] = useMutation(EDIT, refetch);\n const [remove, removeState] = useMutation(DELETE, refetch);\n\n const save = async () => {\n setError(null);\n try {\n await throwOnError(edit({ variables: { id: comment.id, body: text.trim() } }));\n setEditing(false);\n } catch (e) {\n setError(messageOf(e, 'The comment could not be saved.'));\n }\n };\n\n return (\n <li className=\"rounded-md border p-3 space-y-1\" data-testid=\"comment\">\n <div className=\"flex items-center justify-between gap-2 text-xs text-muted-foreground\">\n <span>\n <span className=\"font-medium text-foreground\">\n <PersonName id={comment.authorId} />\n </span>{' '}\n · {when(comment.createdAt)}\n {comment.editedAt && <span title={`Edited ${when(comment.editedAt)}`}> · edited</span>}\n </span>\n {mine && !editing && (\n <span className=\"flex gap-1\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 px-2 text-xs\"\n onClick={() => setEditing(true)}\n >\n Edit\n </Button>\n <AlertDialog>\n <AlertDialogTrigger asChild>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 px-2 text-xs\"\n disabled={removeState.loading}\n >\n Delete\n </Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete this comment?</AlertDialogTitle>\n <AlertDialogDescription>\n It is removed from the discussion for everyone.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Keep it</AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n onClick={async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: comment.id } }));\n } catch (e) {\n setError(messageOf(e, 'The comment could not be deleted.'));\n }\n }}\n >\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </span>\n )}\n </div>\n {editing ? (\n <div className=\"space-y-2\">\n <textarea\n aria-label=\"Edit comment\"\n className=\"w-full min-h-[60px] rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n />\n <div className=\"flex justify-end gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n setEditing(false);\n setText(comment.body);\n setError(null);\n }}\n >\n Cancel\n </Button>\n <Button size=\"sm\" onClick={save} disabled={!text.trim() || editState.loading}>\n {editState.loading ? 'Saving…' : 'Save'}\n </Button>\n </div>\n </div>\n ) : (\n <p className=\"text-sm whitespace-pre-wrap break-words\">{comment.body}</p>\n )}\n </li>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WorkItemComments.js","names":[],"sources":["../../../src/planmagnet/comments/WorkItemComments.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { MessageSquare } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { PersonName } from '../people/People';\nimport { throwOnError } from '../utils/mutationResult';\n\n/**\n * Discussion on a work item (P1.4). Anyone who can read the item sees the\n * thread; posting needs edit rights on work items; only a comment's author\n * can edit or delete it. The server enforces all three and says why it refused\n * (shown inline). The controls here only avoid offering what would be refused.\n */\n\nconst MAX_LENGTH = 10_000;\nconst PAGE = 50;\n\nconst COMMENTS = gql`\n query WorkItemComments($workItemId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetWorkItemComments(workItemId: $workItemId, pagination: $pagination) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n authorId\n body\n createdAt\n editedAt\n }\n }\n }\n`;\nconst ADD = gql`\n mutation WorkItemCommentAdd($input: PlanmagnetAddWorkItemCommentInput!) {\n planmagnetAddWorkItemComment(input: $input) {\n id\n }\n }\n`;\nconst EDIT = gql`\n mutation WorkItemCommentEdit($id: ID!, $body: String!) {\n planmagnetEditWorkItemComment(id: $id, body: $body) {\n id\n body\n editedAt\n }\n }\n`;\nconst DELETE = gql`\n mutation WorkItemCommentDelete($id: ID!) {\n planmagnetDeleteWorkItemComment(id: $id)\n }\n`;\n\ninterface Comment {\n id: string;\n authorId: string;\n body: string;\n createdAt: string;\n editedAt: string | null;\n}\n\ninterface CommentsData {\n planmagnetWorkItemComments: {\n totalCount: number;\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n items: Comment[];\n };\n}\n\nconst messageOf = (e: unknown, fallback: string) => (e instanceof Error ? e.message : fallback);\nconst when = (iso: string) => new Date(iso).toLocaleString();\n\nfunction ErrorLine({ message }: { message: string }) {\n return (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {message}\n </p>\n );\n}\n\nexport function WorkItemComments({ workItemId }: { workItemId: string }) {\n const { currentUser } = usePlanMagnet();\n const [draft, setDraft] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [loadingMore, setLoadingMore] = useState(false);\n\n const {\n data,\n loading,\n error: loadError,\n fetchMore,\n } = useQuery<CommentsData>(COMMENTS, {\n variables: { workItemId, pagination: { first: PAGE } },\n skip: !workItemId,\n });\n const refetch = {\n refetchQueries: ['WorkItemComments', 'WorkItemActivity'],\n awaitRefetchQueries: true,\n };\n const [add, addState] = useMutation(ADD, refetch);\n\n const thread = data?.planmagnetWorkItemComments;\n const comments = thread?.items ?? [];\n const trimmed = draft.trim();\n const tooLong = trimmed.length > MAX_LENGTH;\n\n const post = async () => {\n setError(null);\n try {\n await throwOnError(add({ variables: { input: { workItemId, body: trimmed } } }));\n setDraft('');\n } catch (e) {\n setError(messageOf(e, 'The comment could not be posted.'));\n }\n };\n\n const loadMore = async () => {\n const after = thread?.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 planmagnetWorkItemComments: {\n ...fetchMoreResult.planmagnetWorkItemComments,\n items: [\n ...prev.planmagnetWorkItemComments.items,\n ...fetchMoreResult.planmagnetWorkItemComments.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n return (\n <Card data-testid=\"work-item-comments\">\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <MessageSquare className=\"h-4 w-4\" />\n Discussion {thread ? `(${thread.totalCount})` : ''}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {error && <ErrorLine message={error} />}\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading comments…</p>}\n {loadError && <ErrorLine message={`Comments could not be loaded: ${loadError.message}`} />}\n {thread && comments.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No comments yet.</p>\n )}\n <ol className=\"space-y-3\">\n {comments.map((comment) => (\n <CommentRow\n key={comment.id}\n comment={comment}\n mine={comment.authorId === currentUser?.id}\n onError={setError}\n />\n ))}\n </ol>\n {thread?.pageInfo.hasNextPage && (\n <Button size=\"sm\" variant=\"outline\" onClick={loadMore} disabled={loadingMore}>\n {loadingMore ? 'Loading…' : `Show more (${comments.length} of ${thread.totalCount})`}\n </Button>\n )}\n <div className=\"space-y-2 border-t pt-4\">\n <label htmlFor={`comment-${workItemId}`} className=\"sr-only\">\n Add a comment\n </label>\n <textarea\n id={`comment-${workItemId}`}\n className=\"w-full min-h-[80px] rounded-md border border-input bg-background px-3 py-2 text-sm\"\n placeholder=\"Add a comment…\"\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n />\n {tooLong && <ErrorLine message={`A comment can be at most ${MAX_LENGTH} characters.`} />}\n <div className=\"flex justify-end\">\n <Button\n size=\"sm\"\n onClick={post}\n disabled={!trimmed || tooLong || addState.loading}\n data-testid=\"comment-post\"\n >\n {addState.loading ? 'Posting…' : 'Comment'}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n );\n}\n\n/**\n * Refusals are reported to the PANEL, not kept in the row: a refused edit or\n * delete still refetches the thread, and when the comment is gone (deleted\n * elsewhere) the row unmounts, taking a row-level message with it. Seen in the\n * prod verification: the comment vanished and the reason was never shown.\n */\nfunction CommentRow({\n comment,\n mine,\n onError,\n}: {\n comment: Comment;\n mine: boolean;\n onError: (message: string | null) => void;\n}) {\n const [editing, setEditing] = useState(false);\n const [text, setText] = useState(comment.body);\n const setError = onError;\n const refetch = {\n refetchQueries: ['WorkItemComments', 'WorkItemActivity'],\n awaitRefetchQueries: true,\n };\n const [edit, editState] = useMutation(EDIT, refetch);\n const [remove, removeState] = useMutation(DELETE, refetch);\n\n const save = async () => {\n setError(null);\n try {\n await throwOnError(edit({ variables: { id: comment.id, body: text.trim() } }));\n setEditing(false);\n } catch (e) {\n setError(messageOf(e, 'The comment could not be saved.'));\n }\n };\n\n return (\n <li className=\"rounded-md border p-3 space-y-1\" data-testid=\"comment\">\n <div className=\"flex items-center justify-between gap-2 text-xs text-muted-foreground\">\n <span>\n <span className=\"font-medium text-foreground\">\n <PersonName id={comment.authorId} />\n </span>{' '}\n · {when(comment.createdAt)}\n {comment.editedAt && <span title={`Edited ${when(comment.editedAt)}`}> · edited</span>}\n </span>\n {mine && !editing && (\n <span className=\"flex gap-1\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 px-2 text-xs\"\n onClick={() => setEditing(true)}\n >\n Edit\n </Button>\n <AlertDialog>\n <AlertDialogTrigger asChild>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 px-2 text-xs\"\n disabled={removeState.loading}\n >\n Delete\n </Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete this comment?</AlertDialogTitle>\n <AlertDialogDescription>\n It is removed from the discussion for everyone.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Keep it</AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n onClick={async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: comment.id } }));\n } catch (e) {\n setError(messageOf(e, 'The comment could not be deleted.'));\n }\n }}\n >\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </span>\n )}\n </div>\n {editing ? (\n <div className=\"space-y-2\">\n <textarea\n aria-label=\"Edit comment\"\n className=\"w-full min-h-[60px] rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n />\n <div className=\"flex justify-end gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n setEditing(false);\n setText(comment.body);\n setError(null);\n }}\n >\n Cancel\n </Button>\n <Button size=\"sm\" onClick={save} disabled={!text.trim() || editState.loading}>\n {editState.loading ? 'Saving…' : 'Save'}\n </Button>\n </div>\n </div>\n ) : (\n <p className=\"text-sm whitespace-pre-wrap break-words\">{comment.body}</p>\n )}\n </li>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsReleaseDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsReleaseDetailPagePage.tsx"],"names":[],"mappings":"AA0DA,wBAAgB,6BAA6B,4CA2M5C"}
1
+ {"version":3,"file":"ProductsReleaseDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsReleaseDetailPagePage.tsx"],"names":[],"mappings":"AA2DA,wBAAgB,6BAA6B,4CAgM5C"}
@@ -1,14 +1,15 @@
1
1
  import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
2
2
  import { PersonName as t } from "../people/People.js";
3
- import { Calendar as n, Clock as r, GitBranch as i, Loader2 as a } from "lucide-react";
4
- import { useParams as o } from "react-router";
5
- import { gql as s } from "@apollo/client";
6
- import { useQuery as c } from "@apollo/client/react";
7
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
8
- import { Badge as d, Card as f, CardContent as p, CardHeader as m, CardTitle as h } from "@burdenoff/fe-libs/ui";
9
- import { Markdown as g } from "@burdenoff/fe-libs/shared/components";
3
+ import { ReleaseNotesCard as n } from "../releases/ReleaseNotesCard.js";
4
+ import { Calendar as r, Clock as i, GitBranch as a, Loader2 as o } from "lucide-react";
5
+ import { useParams as s } from "react-router";
6
+ import { gql as c } from "@apollo/client";
7
+ import { useQuery as l } from "@apollo/client/react";
8
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
9
+ import { Badge as f, Card as p, CardContent as m, CardHeader as h, CardTitle as g } from "@burdenoff/fe-libs/ui";
10
+ import { Markdown as _ } from "@burdenoff/fe-libs/shared/components";
10
11
  //#region src/planmagnet/pages/ProductsReleaseDetailPagePage.tsx
11
- var _ = s`
12
+ var v = c`
12
13
  query GetRelease($id: ID!) {
13
14
  planmagnetRelease(id: $id) {
14
15
  id
@@ -35,7 +36,7 @@ var _ = s`
35
36
  }
36
37
  }
37
38
  }
38
- `, v = {
39
+ `, y = {
39
40
  DRAFT: "bg-gray-100 text-gray-800",
40
41
  PLANNING: "bg-yellow-100 text-yellow-800",
41
42
  IN_PROGRESS: "bg-blue-100 text-blue-800",
@@ -44,89 +45,89 @@ var _ = s`
44
45
  CANCELLED: "bg-red-100 text-red-800",
45
46
  ROLLED_BACK: "bg-red-100 text-red-800"
46
47
  };
47
- function y() {
48
- let { workspaceId: s } = e(), y = o().id ?? "", { data: b, loading: x, error: S } = c(_, {
49
- variables: { id: y },
50
- skip: !y
48
+ function b() {
49
+ let { workspaceId: c } = e(), b = s().id ?? "", { data: x, loading: S, error: C } = l(v, {
50
+ variables: { id: b },
51
+ skip: !b
51
52
  });
52
- if (x) return /* @__PURE__ */ l("div", {
53
+ if (S) return /* @__PURE__ */ u("div", {
53
54
  className: "flex items-center justify-center p-12",
54
- children: /* @__PURE__ */ l(a, { className: "h-8 w-8 animate-spin text-muted-foreground" })
55
+ children: /* @__PURE__ */ u(o, { className: "h-8 w-8 animate-spin text-muted-foreground" })
55
56
  });
56
- if (S) return /* @__PURE__ */ u("div", {
57
+ if (C) return /* @__PURE__ */ d("div", {
57
58
  className: "p-6 text-destructive",
58
- children: ["Failed to load release: ", S.message]
59
+ children: ["Failed to load release: ", C.message]
59
60
  });
60
- let C = b?.planmagnetRelease;
61
- if (!C) return /* @__PURE__ */ l("div", {
61
+ let w = x?.planmagnetRelease;
62
+ if (!w) return /* @__PURE__ */ u("div", {
62
63
  className: "p-6 text-muted-foreground",
63
64
  children: "Release not found."
64
65
  });
65
- let w = C.workItems ?? [], T = w.filter((e) => e.type === "STORY" || e.type === "FEATURE"), E = w.filter((e) => e.type === "BUG");
66
- return /* @__PURE__ */ u("div", {
66
+ let T = w.workItems ?? [], E = T.filter((e) => e.type === "STORY" || e.type === "FEATURE"), D = T.filter((e) => e.type === "BUG");
67
+ return /* @__PURE__ */ d("div", {
67
68
  className: "space-y-6 p-6",
68
- children: [/* @__PURE__ */ u("div", {
69
+ children: [/* @__PURE__ */ d("div", {
69
70
  className: "flex items-center justify-between",
70
- children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ u("div", {
71
+ children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ d("div", {
71
72
  className: "flex items-center gap-2",
72
73
  children: [
73
- /* @__PURE__ */ l(i, { className: "h-5 w-5 text-primary" }),
74
- /* @__PURE__ */ l("h1", {
74
+ /* @__PURE__ */ u(a, { className: "h-5 w-5 text-primary" }),
75
+ /* @__PURE__ */ u("h1", {
75
76
  className: "text-2xl font-semibold",
76
- children: C.version
77
+ children: w.version
77
78
  }),
78
- /* @__PURE__ */ l("span", {
79
- className: `text-xs px-2 py-0.5 rounded-full capitalize ${v[C.status] ?? "bg-gray-100"}`,
80
- children: C.status.toLowerCase().replace("_", " ")
79
+ /* @__PURE__ */ u("span", {
80
+ className: `text-xs px-2 py-0.5 rounded-full capitalize ${y[w.status] ?? "bg-gray-100"}`,
81
+ children: w.status.toLowerCase().replace("_", " ")
81
82
  })
82
83
  ]
83
- }), /* @__PURE__ */ l("p", {
84
+ }), /* @__PURE__ */ u("p", {
84
85
  className: "text-sm text-muted-foreground mt-1",
85
- children: C.name
86
- })] }), /* @__PURE__ */ l(d, {
86
+ children: w.name
87
+ })] }), /* @__PURE__ */ u(f, {
87
88
  variant: "outline",
88
89
  className: "capitalize",
89
- children: C.status.toLowerCase().replace("_", " ")
90
+ children: w.status.toLowerCase().replace("_", " ")
90
91
  })]
91
- }), /* @__PURE__ */ u("div", {
92
+ }), /* @__PURE__ */ d("div", {
92
93
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
93
- children: [/* @__PURE__ */ u("div", {
94
+ children: [/* @__PURE__ */ d("div", {
94
95
  className: "lg:col-span-2 space-y-6",
95
96
  children: [
96
- /* @__PURE__ */ u(f, { children: [/* @__PURE__ */ l(m, { children: /* @__PURE__ */ l(h, { children: "Overview" }) }), /* @__PURE__ */ u(p, {
97
+ /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ u(h, { children: /* @__PURE__ */ u(g, { children: "Overview" }) }), /* @__PURE__ */ d(m, {
97
98
  className: "space-y-4",
98
- children: [/* @__PURE__ */ l("div", {
99
+ children: [/* @__PURE__ */ u("div", {
99
100
  className: "text-sm prose prose-sm dark:prose-invert max-w-none",
100
- children: C.description ? /* @__PURE__ */ l(g, { children: C.description }) : /* @__PURE__ */ l("p", { children: "No description provided." })
101
- }), /* @__PURE__ */ u("div", {
101
+ children: w.description ? /* @__PURE__ */ u(_, { children: w.description }) : /* @__PURE__ */ u("p", { children: "No description provided." })
102
+ }), /* @__PURE__ */ d("div", {
102
103
  className: "grid grid-cols-3 gap-4",
103
104
  children: [
104
- /* @__PURE__ */ u("div", {
105
+ /* @__PURE__ */ d("div", {
105
106
  className: "text-center p-3 bg-muted rounded-md",
106
- children: [/* @__PURE__ */ l("p", {
107
+ children: [/* @__PURE__ */ u("p", {
107
108
  className: "text-lg font-semibold mt-1",
108
- children: T.length
109
- }), /* @__PURE__ */ l("p", {
109
+ children: E.length
110
+ }), /* @__PURE__ */ u("p", {
110
111
  className: "text-xs text-muted-foreground",
111
112
  children: "Features"
112
113
  })]
113
114
  }),
114
- /* @__PURE__ */ u("div", {
115
+ /* @__PURE__ */ d("div", {
115
116
  className: "text-center p-3 bg-muted rounded-md",
116
- children: [/* @__PURE__ */ l("p", {
117
+ children: [/* @__PURE__ */ u("p", {
117
118
  className: "text-lg font-semibold mt-1",
118
- children: E.length
119
- }), /* @__PURE__ */ l("p", {
119
+ children: D.length
120
+ }), /* @__PURE__ */ u("p", {
120
121
  className: "text-xs text-muted-foreground",
121
122
  children: "Bug Fixes"
122
123
  })]
123
124
  }),
124
- /* @__PURE__ */ u("div", {
125
+ /* @__PURE__ */ d("div", {
125
126
  className: "text-center p-3 bg-muted rounded-md",
126
- children: [/* @__PURE__ */ l("p", {
127
+ children: [/* @__PURE__ */ u("p", {
127
128
  className: "text-lg font-semibold mt-1",
128
- children: w.length
129
- }), /* @__PURE__ */ l("p", {
129
+ children: T.length
130
+ }), /* @__PURE__ */ u("p", {
130
131
  className: "text-xs text-muted-foreground",
131
132
  children: "Total Items"
132
133
  })]
@@ -134,22 +135,22 @@ function y() {
134
135
  ]
135
136
  })]
136
137
  })] }),
137
- C.releaseNotes && /* @__PURE__ */ u(f, { children: [/* @__PURE__ */ l(m, { children: /* @__PURE__ */ l(h, { children: "Release Notes" }) }), /* @__PURE__ */ l(p, { children: /* @__PURE__ */ l("div", {
138
- className: "text-sm prose prose-sm dark:prose-invert max-w-none",
139
- children: /* @__PURE__ */ l(g, { children: C.releaseNotes })
140
- }) })] }),
141
- w.length > 0 && /* @__PURE__ */ u(f, { children: [/* @__PURE__ */ l(m, { children: /* @__PURE__ */ u(h, { children: [
138
+ /* @__PURE__ */ u(n, {
139
+ releaseId: w.id,
140
+ notes: w.releaseNotes ?? null
141
+ }),
142
+ T.length > 0 && /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ u(h, { children: /* @__PURE__ */ d(g, { children: [
142
143
  "Work Items (",
143
- w.length,
144
+ T.length,
144
145
  ")"
145
- ] }) }), /* @__PURE__ */ l(p, {
146
+ ] }) }), /* @__PURE__ */ u(m, {
146
147
  className: "space-y-2",
147
- children: w.map((e) => /* @__PURE__ */ u("div", {
148
+ children: T.map((e) => /* @__PURE__ */ d("div", {
148
149
  className: "flex items-center justify-between border rounded-md p-3",
149
- children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
150
+ children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
150
151
  className: "text-sm font-medium",
151
152
  children: e.title
152
- }), /* @__PURE__ */ u("p", {
153
+ }), /* @__PURE__ */ d("p", {
153
154
  className: "text-xs text-muted-foreground capitalize",
154
155
  children: [
155
156
  e.type.toLowerCase(),
@@ -157,7 +158,7 @@ function y() {
157
158
  e.priority.toLowerCase(),
158
159
  " priority"
159
160
  ]
160
- })] }), /* @__PURE__ */ l(d, {
161
+ })] }), /* @__PURE__ */ u(f, {
161
162
  variant: "outline",
162
163
  className: "text-xs capitalize",
163
164
  children: e.status.toLowerCase().replace("_", " ")
@@ -165,89 +166,89 @@ function y() {
165
166
  }, e.id))
166
167
  })] })
167
168
  ]
168
- }), /* @__PURE__ */ u("div", {
169
+ }), /* @__PURE__ */ d("div", {
169
170
  className: "space-y-6",
170
171
  children: [
171
- /* @__PURE__ */ u(f, { children: [/* @__PURE__ */ l(m, { children: /* @__PURE__ */ l(h, { children: "Timeline" }) }), /* @__PURE__ */ u(p, {
172
+ /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ u(h, { children: /* @__PURE__ */ u(g, { children: "Timeline" }) }), /* @__PURE__ */ d(m, {
172
173
  className: "space-y-3 text-sm",
173
- children: [/* @__PURE__ */ u("div", {
174
+ children: [/* @__PURE__ */ d("div", {
174
175
  className: "flex items-center gap-2",
175
176
  children: [
176
- /* @__PURE__ */ l(n, { className: "h-4 w-4 text-muted-foreground" }),
177
- /* @__PURE__ */ l("span", {
177
+ /* @__PURE__ */ u(r, { className: "h-4 w-4 text-muted-foreground" }),
178
+ /* @__PURE__ */ u("span", {
178
179
  className: "text-muted-foreground",
179
180
  children: "Planned:"
180
181
  }),
181
- /* @__PURE__ */ l("span", {
182
+ /* @__PURE__ */ u("span", {
182
183
  className: "font-medium",
183
- children: C.plannedDate ? new Date(C.plannedDate).toLocaleDateString() : "Not set"
184
+ children: w.plannedDate ? new Date(w.plannedDate).toLocaleDateString() : "Not set"
184
185
  })
185
186
  ]
186
- }), C.releasedAt && /* @__PURE__ */ u("div", {
187
+ }), w.releasedAt && /* @__PURE__ */ d("div", {
187
188
  className: "flex items-center gap-2",
188
189
  children: [
189
- /* @__PURE__ */ l(r, { className: "h-4 w-4 text-green-500" }),
190
- /* @__PURE__ */ l("span", {
190
+ /* @__PURE__ */ u(i, { className: "h-4 w-4 text-green-500" }),
191
+ /* @__PURE__ */ u("span", {
191
192
  className: "text-muted-foreground",
192
193
  children: "Released:"
193
194
  }),
194
- /* @__PURE__ */ l("span", {
195
+ /* @__PURE__ */ u("span", {
195
196
  className: "font-medium",
196
- children: new Date(C.releasedAt).toLocaleDateString()
197
+ children: new Date(w.releasedAt).toLocaleDateString()
197
198
  })
198
199
  ]
199
200
  })]
200
201
  })] }),
201
- /* @__PURE__ */ u(f, { children: [/* @__PURE__ */ l(m, { children: /* @__PURE__ */ l(h, { children: "Details" }) }), /* @__PURE__ */ u(p, {
202
+ /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ u(h, { children: /* @__PURE__ */ u(g, { children: "Details" }) }), /* @__PURE__ */ d(m, {
202
203
  className: "space-y-2 text-sm",
203
204
  children: [
204
- /* @__PURE__ */ u("div", {
205
+ /* @__PURE__ */ d("div", {
205
206
  className: "flex justify-between",
206
- children: [/* @__PURE__ */ l("span", {
207
+ children: [/* @__PURE__ */ u("span", {
207
208
  className: "text-muted-foreground",
208
209
  children: "Version"
209
- }), /* @__PURE__ */ l("span", {
210
+ }), /* @__PURE__ */ u("span", {
210
211
  className: "font-medium",
211
- children: C.version
212
+ children: w.version
212
213
  })]
213
214
  }),
214
- /* @__PURE__ */ u("div", {
215
+ /* @__PURE__ */ d("div", {
215
216
  className: "flex justify-between",
216
- children: [/* @__PURE__ */ l("span", {
217
+ children: [/* @__PURE__ */ u("span", {
217
218
  className: "text-muted-foreground",
218
219
  children: "Status"
219
- }), /* @__PURE__ */ l(d, {
220
+ }), /* @__PURE__ */ u(f, {
220
221
  variant: "outline",
221
222
  className: "text-xs capitalize",
222
- children: C.status.toLowerCase().replace("_", " ")
223
+ children: w.status.toLowerCase().replace("_", " ")
223
224
  })]
224
225
  }),
225
- /* @__PURE__ */ u("div", {
226
+ /* @__PURE__ */ d("div", {
226
227
  className: "flex justify-between",
227
- children: [/* @__PURE__ */ l("span", {
228
+ children: [/* @__PURE__ */ u("span", {
228
229
  className: "text-muted-foreground",
229
230
  children: "Owner"
230
- }), /* @__PURE__ */ l("span", { children: /* @__PURE__ */ l(t, { id: C.ownerId }) })]
231
+ }), /* @__PURE__ */ u("span", { children: /* @__PURE__ */ u(t, { id: w.ownerId }) })]
231
232
  }),
232
- /* @__PURE__ */ u("div", {
233
+ /* @__PURE__ */ d("div", {
233
234
  className: "flex justify-between",
234
- children: [/* @__PURE__ */ l("span", {
235
+ children: [/* @__PURE__ */ u("span", {
235
236
  className: "text-muted-foreground",
236
237
  children: "Created"
237
- }), /* @__PURE__ */ l("span", { children: new Date(C.createdAt).toLocaleDateString() })]
238
+ }), /* @__PURE__ */ u("span", { children: new Date(w.createdAt).toLocaleDateString() })]
238
239
  }),
239
- /* @__PURE__ */ u("div", {
240
+ /* @__PURE__ */ d("div", {
240
241
  className: "flex justify-between",
241
- children: [/* @__PURE__ */ l("span", {
242
+ children: [/* @__PURE__ */ u("span", {
242
243
  className: "text-muted-foreground",
243
244
  children: "Updated"
244
- }), /* @__PURE__ */ l("span", { children: new Date(C.updatedAt).toLocaleDateString() })]
245
+ }), /* @__PURE__ */ u("span", { children: new Date(w.updatedAt).toLocaleDateString() })]
245
246
  })
246
247
  ]
247
248
  })] }),
248
- C.tags?.length > 0 && /* @__PURE__ */ u(f, { children: [/* @__PURE__ */ l(m, { children: /* @__PURE__ */ l(h, { children: "Tags" }) }), /* @__PURE__ */ l(p, {
249
+ w.tags?.length > 0 && /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ u(h, { children: /* @__PURE__ */ u(g, { children: "Tags" }) }), /* @__PURE__ */ u(m, {
249
250
  className: "flex flex-wrap gap-1",
250
- children: C.tags.map((e) => /* @__PURE__ */ l(d, {
251
+ children: w.tags.map((e) => /* @__PURE__ */ u(f, {
251
252
  variant: "outline",
252
253
  className: "text-xs",
253
254
  children: e
@@ -259,6 +260,6 @@ function y() {
259
260
  });
260
261
  }
261
262
  //#endregion
262
- export { y as ProductsReleaseDetailPagePage };
263
+ export { b as ProductsReleaseDetailPagePage };
263
264
 
264
265
  //# sourceMappingURL=ProductsReleaseDetailPagePage.js.map