@burdenoff/microfe-theculturepost 2026.704.2 → 2026.705.2

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 (91) hide show
  1. package/dist/theculturepost/TheCulturePostRoot.js +63 -45
  2. package/dist/theculturepost/TheCulturePostRoot.js.map +1 -1
  3. package/dist/theculturepost/TheCulturePostRoutes.js +17 -9
  4. package/dist/theculturepost/TheCulturePostRoutes.js.map +1 -1
  5. package/dist/theculturepost/components/AddCitationDialog.js +131 -102
  6. package/dist/theculturepost/components/AddCitationDialog.js.map +1 -1
  7. package/dist/theculturepost/components/ArchiveItemCard.js +56 -43
  8. package/dist/theculturepost/components/ArchiveItemCard.js.map +1 -1
  9. package/dist/theculturepost/components/AudioPlayer.js +74 -0
  10. package/dist/theculturepost/components/AudioPlayer.js.map +1 -0
  11. package/dist/theculturepost/components/CitationList.js +215 -105
  12. package/dist/theculturepost/components/CitationList.js.map +1 -1
  13. package/dist/theculturepost/components/CollectionCard.js +32 -32
  14. package/dist/theculturepost/components/CollectionCard.js.map +1 -1
  15. package/dist/theculturepost/components/CreateArchiveItemDialog.js +133 -93
  16. package/dist/theculturepost/components/CreateArchiveItemDialog.js.map +1 -1
  17. package/dist/theculturepost/components/CreateCollectionDialog.js +91 -70
  18. package/dist/theculturepost/components/CreateCollectionDialog.js.map +1 -1
  19. package/dist/theculturepost/components/CreateStoryDialog.js +311 -132
  20. package/dist/theculturepost/components/CreateStoryDialog.js.map +1 -1
  21. package/dist/theculturepost/components/DecorativeCover.js +78 -0
  22. package/dist/theculturepost/components/DecorativeCover.js.map +1 -0
  23. package/dist/theculturepost/components/DecorativeDivider.js +45 -0
  24. package/dist/theculturepost/components/DecorativeDivider.js.map +1 -0
  25. package/dist/theculturepost/components/DiscussionCard.js +75 -60
  26. package/dist/theculturepost/components/DiscussionCard.js.map +1 -1
  27. package/dist/theculturepost/components/HistoryTimeline.js +115 -0
  28. package/dist/theculturepost/components/HistoryTimeline.js.map +1 -0
  29. package/dist/theculturepost/components/LifecycleStepper.js +33 -0
  30. package/dist/theculturepost/components/LifecycleStepper.js.map +1 -0
  31. package/dist/theculturepost/components/OpenDiscussionDialog.js +66 -56
  32. package/dist/theculturepost/components/OpenDiscussionDialog.js.map +1 -1
  33. package/dist/theculturepost/components/OralHistoryCard.js +6 -73
  34. package/dist/theculturepost/components/OverlayBadge.js +22 -0
  35. package/dist/theculturepost/components/OverlayBadge.js.map +1 -0
  36. package/dist/theculturepost/components/PageHero.js +62 -0
  37. package/dist/theculturepost/components/PageHero.js.map +1 -0
  38. package/dist/theculturepost/components/RecordOralHistoryDialog.js +306 -131
  39. package/dist/theculturepost/components/RecordOralHistoryDialog.js.map +1 -1
  40. package/dist/theculturepost/components/ReplyThread.js +268 -139
  41. package/dist/theculturepost/components/ReplyThread.js.map +1 -1
  42. package/dist/theculturepost/components/StatusBadge.js +9 -9
  43. package/dist/theculturepost/components/StatusBadge.js.map +1 -1
  44. package/dist/theculturepost/components/StoryCard.js +54 -49
  45. package/dist/theculturepost/components/StoryCard.js.map +1 -1
  46. package/dist/theculturepost/components/TopicCard.js +60 -41
  47. package/dist/theculturepost/components/TopicCard.js.map +1 -1
  48. package/dist/theculturepost/components/Waveform.js +31 -0
  49. package/dist/theculturepost/components/Waveform.js.map +1 -0
  50. package/dist/theculturepost/components/categoryMeta.js +20 -0
  51. package/dist/theculturepost/components/categoryMeta.js.map +1 -0
  52. package/dist/theculturepost/components/index.js +41 -0
  53. package/dist/theculturepost/components/viz/BarChart.js +53 -0
  54. package/dist/theculturepost/components/viz/BarChart.js.map +1 -0
  55. package/dist/theculturepost/components/viz/Donut.js +58 -0
  56. package/dist/theculturepost/components/viz/Donut.js.map +1 -0
  57. package/dist/theculturepost/components/viz/StatTile.js +35 -0
  58. package/dist/theculturepost/components/viz/StatTile.js.map +1 -0
  59. package/dist/theculturepost/components/viz/index.js +3 -0
  60. package/dist/theculturepost/hooks/useFiles.js +1 -1
  61. package/dist/theculturepost/hooks/useFiles.js.map +1 -1
  62. package/dist/theculturepost/pages/HomePage.js +247 -124
  63. package/dist/theculturepost/pages/HomePage.js.map +1 -1
  64. package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js +319 -127
  65. package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js.map +1 -1
  66. package/dist/theculturepost/pages/archive/ArchivePage.js +265 -180
  67. package/dist/theculturepost/pages/archive/ArchivePage.js.map +1 -1
  68. package/dist/theculturepost/pages/archive/CollectionDetailPage.js +158 -143
  69. package/dist/theculturepost/pages/archive/CollectionDetailPage.js.map +1 -1
  70. package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js +155 -93
  71. package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js.map +1 -1
  72. package/dist/theculturepost/pages/discussions/DiscussionsPage.js +16 -16
  73. package/dist/theculturepost/pages/discussions/DiscussionsPage.js.map +1 -1
  74. package/dist/theculturepost/pages/governance/GovernancePage.js +293 -0
  75. package/dist/theculturepost/pages/governance/GovernancePage.js.map +1 -0
  76. package/dist/theculturepost/pages/stories/StoriesPage.js +18 -18
  77. package/dist/theculturepost/pages/stories/StoriesPage.js.map +1 -1
  78. package/dist/theculturepost/pages/stories/StoryDetailPage.js +603 -285
  79. package/dist/theculturepost/pages/stories/StoryDetailPage.js.map +1 -1
  80. package/dist/theculturepost/pages/timeline/TimelinePage.js +162 -0
  81. package/dist/theculturepost/pages/timeline/TimelinePage.js.map +1 -0
  82. package/dist/theculturepost/pages/topics/TopicDetailPage.js +239 -163
  83. package/dist/theculturepost/pages/topics/TopicDetailPage.js.map +1 -1
  84. package/dist/theculturepost/pages/topics/TopicsPage.js +76 -74
  85. package/dist/theculturepost/pages/topics/TopicsPage.js.map +1 -1
  86. package/dist/theculturepost/styles/injectDisplayFont.js +11 -0
  87. package/dist/theculturepost/styles/injectDisplayFont.js.map +1 -0
  88. package/dist/theculturepost/utils/formatters.js +6 -1
  89. package/dist/theculturepost/utils/formatters.js.map +1 -1
  90. package/package.json +1 -1
  91. package/dist/theculturepost/components/OralHistoryCard.js.map +0 -1
@@ -0,0 +1,293 @@
1
+ import { useTheCulturePost as e } from "../../context/TheCulturePostContext.js";
2
+ import "../../context/index.js";
3
+ import { PageHero as t } from "../../components/PageHero.js";
4
+ import { ErrorView as n, LoadingView as r, Section as i } from "../../components/StateViews.js";
5
+ import { formatRelativeTime as a } from "../../utils/formatters.js";
6
+ import { StatusBadge as o } from "../../components/StatusBadge.js";
7
+ import { EmptyState as s } from "../../components/EmptyState.js";
8
+ import { useTcpArchiveItems as c, useTcpCollections as l, useTcpDiscussions as u, useTcpStories as d } from "../../hooks/useTcpApi.js";
9
+ import "../../hooks/index.js";
10
+ import { StatTile as f } from "../../components/viz/StatTile.js";
11
+ import { BarChart as p } from "../../components/viz/BarChart.js";
12
+ import { Donut as m } from "../../components/viz/Donut.js";
13
+ import "../../components/index.js";
14
+ import { useMemo as h } from "react";
15
+ import { Activity as g, ArrowRight as _, BookOpen as v, Clock as y, Library as b, Lightbulb as x, MessagesSquare as S, PencilLine as C, ShieldCheck as w } from "lucide-react";
16
+ import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
17
+ import { GlassCard as O } from "@burdenoff/fe-libs/ui";
18
+ //#region src/theculturepost/pages/governance/GovernancePage.tsx
19
+ var k = 100, A = {
20
+ story: v,
21
+ discussion: S
22
+ };
23
+ function j() {
24
+ let { navigateTo: j } = e(), M = d({
25
+ status: "IDEA",
26
+ pagination: { limit: k }
27
+ }), N = d({
28
+ status: "DRAFT",
29
+ pagination: { limit: k }
30
+ }), P = d({
31
+ status: "IN_REVIEW",
32
+ pagination: { limit: k }
33
+ }), F = d({
34
+ status: "PUBLISHED",
35
+ pagination: { limit: k }
36
+ }), I = u({ pagination: { limit: k } }), L = l({ limit: k }), R = c({ pagination: { limit: k } }), z = [
37
+ ...[
38
+ M,
39
+ N,
40
+ P,
41
+ F
42
+ ],
43
+ I,
44
+ L,
45
+ R
46
+ ], B = h(() => M.data?.items ?? [], [M.data]), V = h(() => N.data?.items ?? [], [N.data]), H = h(() => P.data?.items ?? [], [P.data]), U = h(() => F.data?.items ?? [], [F.data]), W = h(() => I.data?.items ?? [], [I.data]), G = B.length, K = V.length, q = H.length, J = U.length, Y = W.length, X = W.filter((e) => e.status === "APPROVED").length, Z = Y - X, Q = L.data?.length ?? 0, ee = R.data?.items.length ?? 0, te = [
47
+ {
48
+ label: "Idea",
49
+ value: G
50
+ },
51
+ {
52
+ label: "Draft",
53
+ value: K
54
+ },
55
+ {
56
+ label: "In Review",
57
+ value: q
58
+ },
59
+ {
60
+ label: "Published",
61
+ value: J
62
+ }
63
+ ], $ = h(() => {
64
+ let e = [
65
+ ...B,
66
+ ...V,
67
+ ...H,
68
+ ...U
69
+ ].map((e) => ({
70
+ id: `story:${e.id}`,
71
+ kind: "story",
72
+ title: e.title,
73
+ status: e.status,
74
+ createdAt: e.createdAt,
75
+ onOpen: () => j(`stories/${e.id}`)
76
+ })), t = W.map((e) => ({
77
+ id: `discussion:${e.id}`,
78
+ kind: "discussion",
79
+ title: e.title,
80
+ status: e.status,
81
+ createdAt: e.createdAt,
82
+ onOpen: () => j(`discussions/${e.id}`)
83
+ }));
84
+ return [...e, ...t].sort((e, t) => new Date(t.createdAt).getTime() - new Date(e.createdAt).getTime()).slice(0, 8);
85
+ }, [
86
+ B,
87
+ V,
88
+ H,
89
+ U,
90
+ W,
91
+ j
92
+ ]), ne = z.some((e) => e.isLoading), re = z.some((e) => e.isError);
93
+ return /* @__PURE__ */ E("div", {
94
+ className: "h-full overflow-auto",
95
+ children: /* @__PURE__ */ D("div", {
96
+ className: "mx-auto flex max-w-6xl flex-col gap-6 p-4 sm:p-6",
97
+ children: [/* @__PURE__ */ E(t, {
98
+ eyebrow: "Editorial",
99
+ title: "Governance & Editorial",
100
+ description: "Oversee content quality, discussions and community participation — at a glance."
101
+ }), ne ? /* @__PURE__ */ E(r, { label: "Computing editorial metrics…" }) : re ? /* @__PURE__ */ E(n, {
102
+ title: "Couldn't load the overview",
103
+ message: "We couldn't compute the editorial metrics for this workspace.",
104
+ onRetry: () => {
105
+ z.forEach((e) => {
106
+ e.refetch();
107
+ });
108
+ }
109
+ }) : /* @__PURE__ */ D(T, { children: [
110
+ /* @__PURE__ */ E("p", {
111
+ className: "-mt-2 text-xs text-text-tertiary",
112
+ children: "Counts are computed live from the current workspace."
113
+ }),
114
+ /* @__PURE__ */ D("div", {
115
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
116
+ children: [
117
+ /* @__PURE__ */ E(f, {
118
+ icon: /* @__PURE__ */ E(v, { className: "h-5 w-5" }),
119
+ label: "Published",
120
+ value: J,
121
+ caption: "stories live"
122
+ }),
123
+ /* @__PURE__ */ E(f, {
124
+ icon: /* @__PURE__ */ E(y, { className: "h-5 w-5" }),
125
+ label: "In Review",
126
+ value: q,
127
+ caption: "awaiting editorial sign-off"
128
+ }),
129
+ /* @__PURE__ */ E(f, {
130
+ icon: /* @__PURE__ */ E(C, { className: "h-5 w-5" }),
131
+ label: "Draft",
132
+ value: K,
133
+ caption: "in progress"
134
+ }),
135
+ /* @__PURE__ */ E(f, {
136
+ icon: /* @__PURE__ */ E(x, { className: "h-5 w-5" }),
137
+ label: "Idea",
138
+ value: G,
139
+ caption: "pitched, not yet drafted"
140
+ }),
141
+ /* @__PURE__ */ E(f, {
142
+ icon: /* @__PURE__ */ E(S, { className: "h-5 w-5" }),
143
+ label: "Discussions",
144
+ value: Y,
145
+ caption: "community conversations"
146
+ }),
147
+ /* @__PURE__ */ E(f, {
148
+ icon: /* @__PURE__ */ E(b, { className: "h-5 w-5" }),
149
+ label: "Archive",
150
+ value: ee,
151
+ caption: `artifacts in ${Q} collection${Q === 1 ? "" : "s"}`
152
+ })
153
+ ]
154
+ }),
155
+ /* @__PURE__ */ D("div", {
156
+ className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
157
+ children: [/* @__PURE__ */ D(O, {
158
+ treatment: "plain",
159
+ className: "flex flex-col gap-4 p-5 sm:p-6",
160
+ children: [/* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("h2", {
161
+ className: "font-display text-lg font-semibold text-text-primary",
162
+ children: "Content pipeline"
163
+ }), /* @__PURE__ */ E("p", {
164
+ className: "mt-0.5 text-sm text-text-secondary",
165
+ children: "Stories across the editorial lifecycle."
166
+ })] }), J + q + K + G === 0 ? /* @__PURE__ */ E(s, {
167
+ icon: /* @__PURE__ */ E(v, { className: "h-9 w-9" }),
168
+ title: "No stories yet",
169
+ description: "Draft the first story to populate the pipeline."
170
+ }) : /* @__PURE__ */ E(p, {
171
+ ariaLabel: "Stories by lifecycle stage",
172
+ data: te,
173
+ height: 180
174
+ })]
175
+ }), /* @__PURE__ */ D(O, {
176
+ treatment: "plain",
177
+ className: "flex flex-col gap-4 p-5 sm:p-6",
178
+ children: [/* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("h2", {
179
+ className: "font-display text-lg font-semibold text-text-primary",
180
+ children: "Discussion health"
181
+ }), /* @__PURE__ */ E("p", {
182
+ className: "mt-0.5 text-sm text-text-secondary",
183
+ children: "Share of community discussions that are approved."
184
+ })] }), Y === 0 ? /* @__PURE__ */ E(s, {
185
+ icon: /* @__PURE__ */ E(S, { className: "h-9 w-9" }),
186
+ title: "No discussions yet",
187
+ description: "Open a discussion to start tracking conversation quality."
188
+ }) : /* @__PURE__ */ D("div", {
189
+ className: "flex flex-col items-center gap-5 sm:flex-row sm:items-center sm:gap-6",
190
+ children: [/* @__PURE__ */ E(m, {
191
+ value: X,
192
+ total: Y,
193
+ caption: "approved",
194
+ size: 140,
195
+ ariaLabel: `${X} of ${Y} discussions approved`
196
+ }), /* @__PURE__ */ D("dl", {
197
+ className: "flex w-full flex-col gap-3",
198
+ children: [
199
+ /* @__PURE__ */ D("div", {
200
+ className: "flex items-center justify-between gap-3",
201
+ children: [/* @__PURE__ */ D("dt", {
202
+ className: "flex items-center gap-2 text-sm text-text-secondary",
203
+ children: [/* @__PURE__ */ E("span", {
204
+ className: "h-2.5 w-2.5 rounded-full bg-accent",
205
+ "aria-hidden": "true"
206
+ }), "Approved"]
207
+ }), /* @__PURE__ */ E("dd", {
208
+ className: "font-display text-sm font-semibold tabular-nums text-text-primary",
209
+ children: X
210
+ })]
211
+ }),
212
+ /* @__PURE__ */ D("div", {
213
+ className: "flex items-center justify-between gap-3",
214
+ children: [/* @__PURE__ */ D("dt", {
215
+ className: "flex items-center gap-2 text-sm text-text-secondary",
216
+ children: [/* @__PURE__ */ E("span", {
217
+ className: "h-2.5 w-2.5 rounded-full bg-bg-sunken",
218
+ "aria-hidden": "true"
219
+ }), "Needs attention"]
220
+ }), /* @__PURE__ */ E("dd", {
221
+ className: "font-display text-sm font-semibold tabular-nums text-text-primary",
222
+ children: Z
223
+ })]
224
+ }),
225
+ /* @__PURE__ */ D("div", {
226
+ className: "mt-1 flex items-start gap-2 rounded-lg bg-bg-sunken/60 p-3 text-xs text-text-secondary",
227
+ children: [/* @__PURE__ */ E(w, {
228
+ className: "mt-0.5 h-4 w-4 shrink-0 text-accent",
229
+ "aria-hidden": "true"
230
+ }), /* @__PURE__ */ E("span", { children: "Approved replies are published; pending, flagged and removed discussions need a moderator’s attention." })]
231
+ })
232
+ ]
233
+ })]
234
+ })]
235
+ })]
236
+ }),
237
+ /* @__PURE__ */ E(i, {
238
+ title: "Recent activity",
239
+ description: "The latest stories and discussions across the workspace.",
240
+ children: $.length === 0 ? /* @__PURE__ */ E(s, {
241
+ icon: /* @__PURE__ */ E(g, { className: "h-9 w-9" }),
242
+ title: "Nothing yet",
243
+ description: "Draft a story or open a discussion — it will show up here."
244
+ }) : /* @__PURE__ */ E(O, {
245
+ treatment: "plain",
246
+ className: "divide-y divide-border-seam",
247
+ children: $.map((e) => {
248
+ let t = A[e.kind];
249
+ return /* @__PURE__ */ D("button", {
250
+ type: "button",
251
+ onClick: e.onOpen,
252
+ className: "flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-bg-sunken/60 focus-visible:bg-bg-sunken/60 focus-visible:outline-none",
253
+ children: [
254
+ /* @__PURE__ */ E("span", {
255
+ className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
256
+ "aria-hidden": "true",
257
+ children: /* @__PURE__ */ E(t, { className: "h-4 w-4" })
258
+ }),
259
+ /* @__PURE__ */ D("div", {
260
+ className: "min-w-0 flex-1",
261
+ children: [/* @__PURE__ */ E("p", {
262
+ className: "truncate text-sm font-medium text-text-primary",
263
+ children: e.title
264
+ }), /* @__PURE__ */ D("p", {
265
+ className: "mt-0.5 text-xs capitalize text-text-tertiary",
266
+ children: [
267
+ e.kind,
268
+ " · ",
269
+ a(e.createdAt)
270
+ ]
271
+ })]
272
+ }),
273
+ /* @__PURE__ */ E(o, {
274
+ status: e.status,
275
+ className: "hidden shrink-0 sm:inline-flex"
276
+ }),
277
+ /* @__PURE__ */ E(_, {
278
+ className: "h-4 w-4 shrink-0 text-text-tertiary",
279
+ "aria-hidden": "true"
280
+ })
281
+ ]
282
+ }, e.id);
283
+ })
284
+ })
285
+ })
286
+ ] })]
287
+ })
288
+ });
289
+ }
290
+ //#endregion
291
+ export { j as GovernancePage };
292
+
293
+ //# sourceMappingURL=GovernancePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GovernancePage.js","names":[],"sources":["../../../../src/theculturepost/pages/governance/GovernancePage.tsx"],"sourcesContent":["/**\n * GovernancePage — the editorial / governance overview for TheCulturePost.\n * @module theculturepost/pages/governance\n *\n * A data-honest dashboard computed entirely from real counts in the current\n * workspace. There is NO historical series in the contract, so this surface\n * carries NO trend deltas and NO time-series line chart (unlike the concept\n * mockup). It also DROPS the \"Contributors & Roles\" table — actors are id-only\n * federation stubs and roles are platform RBAC, neither of which this subgraph\n * exposes.\n *\n * What it renders, all from live `tcp*` reads:\n * - Status StatTiles: Published / In Review / Draft / Idea stories, plus a\n * Discussions total and an Archive (artifacts) count.\n * - \"Content pipeline\" {@link BarChart} across the lifecycle stages.\n * - \"Discussion health\" {@link Donut} = share of discussions that are Approved.\n * - \"Recent activity\" — the latest stories + discussions, newest first.\n */\n\nimport { useMemo } from 'react';\nimport {\n BookOpen,\n Clock,\n PencilLine,\n Lightbulb,\n MessagesSquare,\n Library,\n Activity,\n ShieldCheck,\n ArrowRight,\n} from 'lucide-react';\nimport type { LucideIcon } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageHero,\n Section,\n LoadingView,\n ErrorView,\n EmptyState,\n StatTile,\n BarChart,\n Donut,\n StatusBadge,\n type BarDatum,\n} from '../../components';\nimport {\n useTcpStories,\n useTcpDiscussions,\n useTcpCollections,\n useTcpArchiveItems,\n} from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport { formatRelativeTime } from '../../utils/formatters';\n\nconst COUNT_LIMIT = 100;\n\ntype ActivityKind = 'story' | 'discussion';\n\ninterface ActivityRow {\n id: string;\n kind: ActivityKind;\n title: string;\n status: string;\n createdAt: string;\n onOpen: () => void;\n}\n\nconst ACTIVITY_ICON: Record<ActivityKind, LucideIcon> = {\n story: BookOpen,\n discussion: MessagesSquare,\n};\n\nexport function GovernancePage() {\n const { navigateTo } = useTheCulturePost();\n\n // One query per lifecycle stage — each returns that stage's stories, so the\n // count is `items.length` (up to COUNT_LIMIT). No cross-stage query needed.\n const ideaQuery = useTcpStories({ status: 'IDEA', pagination: { limit: COUNT_LIMIT } });\n const draftQuery = useTcpStories({ status: 'DRAFT', pagination: { limit: COUNT_LIMIT } });\n const reviewQuery = useTcpStories({ status: 'IN_REVIEW', pagination: { limit: COUNT_LIMIT } });\n const publishedQuery = useTcpStories({\n status: 'PUBLISHED',\n pagination: { limit: COUNT_LIMIT },\n });\n const discussionsQuery = useTcpDiscussions({ pagination: { limit: COUNT_LIMIT } });\n const collectionsQuery = useTcpCollections({ limit: COUNT_LIMIT });\n const artifactsQuery = useTcpArchiveItems({ pagination: { limit: COUNT_LIMIT } });\n\n const storyQueries = [ideaQuery, draftQuery, reviewQuery, publishedQuery];\n const allQueries = [...storyQueries, discussionsQuery, collectionsQuery, artifactsQuery];\n\n const ideaStories = useMemo(() => ideaQuery.data?.items ?? [], [ideaQuery.data]);\n const draftStories = useMemo(() => draftQuery.data?.items ?? [], [draftQuery.data]);\n const reviewStories = useMemo(() => reviewQuery.data?.items ?? [], [reviewQuery.data]);\n const publishedStories = useMemo(() => publishedQuery.data?.items ?? [], [publishedQuery.data]);\n const discussions = useMemo(() => discussionsQuery.data?.items ?? [], [discussionsQuery.data]);\n\n const ideaCount = ideaStories.length;\n const draftCount = draftStories.length;\n const reviewCount = reviewStories.length;\n const publishedCount = publishedStories.length;\n const discussionTotal = discussions.length;\n const approvedCount = discussions.filter((d) => d.status === 'APPROVED').length;\n const needsAttentionCount = discussionTotal - approvedCount;\n const collectionsCount = collectionsQuery.data?.length ?? 0;\n const artifactsCount = artifactsQuery.data?.items.length ?? 0;\n\n const pipelineData: BarDatum[] = [\n { label: 'Idea', value: ideaCount },\n { label: 'Draft', value: draftCount },\n { label: 'In Review', value: reviewCount },\n { label: 'Published', value: publishedCount },\n ];\n\n const recentActivity = useMemo<ActivityRow[]>(() => {\n const storyRows: ActivityRow[] = [\n ...ideaStories,\n ...draftStories,\n ...reviewStories,\n ...publishedStories,\n ].map((story) => ({\n id: `story:${story.id}`,\n kind: 'story' as const,\n title: story.title,\n status: story.status,\n createdAt: story.createdAt,\n onOpen: () => navigateTo(`stories/${story.id}`),\n }));\n\n const discussionRows: ActivityRow[] = discussions.map((discussion) => ({\n id: `discussion:${discussion.id}`,\n kind: 'discussion' as const,\n title: discussion.title,\n status: discussion.status,\n createdAt: discussion.createdAt,\n onOpen: () => navigateTo(`discussions/${discussion.id}`),\n }));\n\n return [...storyRows, ...discussionRows]\n .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())\n .slice(0, 8);\n }, [ideaStories, draftStories, reviewStories, publishedStories, discussions, navigateTo]);\n\n const isLoading = allQueries.some((q) => q.isLoading);\n const isError = allQueries.some((q) => q.isError);\n\n const retryAll = () => {\n allQueries.forEach((q) => {\n void q.refetch();\n });\n };\n\n return (\n <div className=\"h-full overflow-auto\">\n <div className=\"mx-auto flex max-w-6xl flex-col gap-6 p-4 sm:p-6\">\n <PageHero\n eyebrow=\"Editorial\"\n title=\"Governance & Editorial\"\n description=\"Oversee content quality, discussions and community participation — at a glance.\"\n />\n\n {isLoading ? (\n <LoadingView label=\"Computing editorial metrics…\" />\n ) : isError ? (\n <ErrorView\n title=\"Couldn't load the overview\"\n message=\"We couldn't compute the editorial metrics for this workspace.\"\n onRetry={retryAll}\n />\n ) : (\n <>\n <p className=\"-mt-2 text-xs text-text-tertiary\">\n Counts are computed live from the current workspace.\n </p>\n\n {/* Status metrics */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <StatTile\n icon={<BookOpen className=\"h-5 w-5\" />}\n label=\"Published\"\n value={publishedCount}\n caption=\"stories live\"\n />\n <StatTile\n icon={<Clock className=\"h-5 w-5\" />}\n label=\"In Review\"\n value={reviewCount}\n caption=\"awaiting editorial sign-off\"\n />\n <StatTile\n icon={<PencilLine className=\"h-5 w-5\" />}\n label=\"Draft\"\n value={draftCount}\n caption=\"in progress\"\n />\n <StatTile\n icon={<Lightbulb className=\"h-5 w-5\" />}\n label=\"Idea\"\n value={ideaCount}\n caption=\"pitched, not yet drafted\"\n />\n <StatTile\n icon={<MessagesSquare className=\"h-5 w-5\" />}\n label=\"Discussions\"\n value={discussionTotal}\n caption=\"community conversations\"\n />\n <StatTile\n icon={<Library className=\"h-5 w-5\" />}\n label=\"Archive\"\n value={artifactsCount}\n caption={`artifacts in ${collectionsCount} collection${\n collectionsCount === 1 ? '' : 's'\n }`}\n />\n </div>\n\n {/* Pipeline + discussion health */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-4 p-5 sm:p-6\">\n <div>\n <h2 className=\"font-display text-lg font-semibold text-text-primary\">\n Content pipeline\n </h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n Stories across the editorial lifecycle.\n </p>\n </div>\n {publishedCount + reviewCount + draftCount + ideaCount === 0 ? (\n <EmptyState\n icon={<BookOpen className=\"h-9 w-9\" />}\n title=\"No stories yet\"\n description=\"Draft the first story to populate the pipeline.\"\n />\n ) : (\n <BarChart\n ariaLabel=\"Stories by lifecycle stage\"\n data={pipelineData}\n height={180}\n />\n )}\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-4 p-5 sm:p-6\">\n <div>\n <h2 className=\"font-display text-lg font-semibold text-text-primary\">\n Discussion health\n </h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n Share of community discussions that are approved.\n </p>\n </div>\n {discussionTotal === 0 ? (\n <EmptyState\n icon={<MessagesSquare className=\"h-9 w-9\" />}\n title=\"No discussions yet\"\n description=\"Open a discussion to start tracking conversation quality.\"\n />\n ) : (\n <div className=\"flex flex-col items-center gap-5 sm:flex-row sm:items-center sm:gap-6\">\n <Donut\n value={approvedCount}\n total={discussionTotal}\n caption=\"approved\"\n size={140}\n ariaLabel={`${approvedCount} of ${discussionTotal} discussions approved`}\n />\n <dl className=\"flex w-full flex-col gap-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <dt className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span className=\"h-2.5 w-2.5 rounded-full bg-accent\" aria-hidden=\"true\" />\n Approved\n </dt>\n <dd className=\"font-display text-sm font-semibold tabular-nums text-text-primary\">\n {approvedCount}\n </dd>\n </div>\n <div className=\"flex items-center justify-between gap-3\">\n <dt className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span\n className=\"h-2.5 w-2.5 rounded-full bg-bg-sunken\"\n aria-hidden=\"true\"\n />\n Needs attention\n </dt>\n <dd className=\"font-display text-sm font-semibold tabular-nums text-text-primary\">\n {needsAttentionCount}\n </dd>\n </div>\n <div className=\"mt-1 flex items-start gap-2 rounded-lg bg-bg-sunken/60 p-3 text-xs text-text-secondary\">\n <ShieldCheck\n className=\"mt-0.5 h-4 w-4 shrink-0 text-accent\"\n aria-hidden=\"true\"\n />\n <span>\n Approved replies are published; pending, flagged and removed discussions\n need a moderator&rsquo;s attention.\n </span>\n </div>\n </dl>\n </div>\n )}\n </GlassCard>\n </div>\n\n {/* Recent activity */}\n <Section\n title=\"Recent activity\"\n description=\"The latest stories and discussions across the workspace.\"\n >\n {recentActivity.length === 0 ? (\n <EmptyState\n icon={<Activity className=\"h-9 w-9\" />}\n title=\"Nothing yet\"\n description=\"Draft a story or open a discussion — it will show up here.\"\n />\n ) : (\n <GlassCard treatment=\"plain\" className=\"divide-y divide-border-seam\">\n {recentActivity.map((row) => {\n const Icon = ACTIVITY_ICON[row.kind];\n return (\n <button\n key={row.id}\n type=\"button\"\n onClick={row.onOpen}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-bg-sunken/60 focus-visible:bg-bg-sunken/60 focus-visible:outline-none\"\n >\n <span\n className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\"\n aria-hidden=\"true\"\n >\n <Icon className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {row.title}\n </p>\n <p className=\"mt-0.5 text-xs capitalize text-text-tertiary\">\n {row.kind} · {formatRelativeTime(row.createdAt)}\n </p>\n </div>\n <StatusBadge\n status={row.status}\n className=\"hidden shrink-0 sm:inline-flex\"\n />\n <ArrowRight\n className=\"h-4 w-4 shrink-0 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n </button>\n );\n })}\n </GlassCard>\n )}\n </Section>\n </>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,14 +1,14 @@
1
1
  import { useTheCulturePost as e } from "../../context/TheCulturePostContext.js";
2
- import { PageLayout as t } from "../../components/PageLayout.js";
3
- import { ErrorView as n, LoadingView as r } from "../../components/StateViews.js";
4
2
  import "../../context/index.js";
5
- import { StoryCard as i } from "../../components/StoryCard.js";
6
- import { EmptyState as a } from "../../components/EmptyState.js";
7
- import { useTcpStories as o, useTcpTopics as s } from "../../hooks/useTcpApi.js";
8
- import { useCursorPager as c } from "../../hooks/useCursorPager.js";
3
+ import { ErrorView as t, LoadingView as n } from "../../components/StateViews.js";
4
+ import { StoryCard as r } from "../../components/StoryCard.js";
5
+ import { EmptyState as i } from "../../components/EmptyState.js";
6
+ import { useTcpStories as a, useTcpTopics as o } from "../../hooks/useTcpApi.js";
7
+ import { useCursorPager as s } from "../../hooks/useCursorPager.js";
9
8
  import "../../hooks/index.js";
10
- import { FilterTabs as l } from "../../components/FilterTabs.js";
11
- import { Pagination as u } from "../../components/Pagination.js";
9
+ import { Pagination as c } from "../../components/Pagination.js";
10
+ import { PageLayout as l } from "../../components/PageLayout.js";
11
+ import { FilterTabs as u } from "../../components/FilterTabs.js";
12
12
  import { CreateStoryDialog as d } from "../../components/CreateStoryDialog.js";
13
13
  import { useState as f } from "react";
14
14
  import { BookOpen as p, Plus as m } from "lucide-react";
@@ -42,7 +42,7 @@ var w = 12, T = [
42
42
  }
43
43
  ];
44
44
  function E() {
45
- let { navigateTo: E } = e(), D = c(), [O, k] = f("ALL"), [A, j] = f("ALL"), [M, N] = f(!1), P = s({ pagination: { limit: 100 } }), { data: F, isLoading: I, isError: L, refetch: R } = o({
45
+ let { navigateTo: E } = e(), D = s(), [O, k] = f("ALL"), [A, j] = f("ALL"), [M, N] = f(!1), P = o({ pagination: { limit: 100 } }), { data: F, isLoading: I, isError: L, refetch: R } = a({
46
46
  status: O === "ALL" ? void 0 : O,
47
47
  topicId: A === "ALL" ? void 0 : A,
48
48
  pagination: {
@@ -50,7 +50,7 @@ function E() {
50
50
  cursor: D.cursor
51
51
  }
52
52
  }), z = F?.items ?? [];
53
- return /* @__PURE__ */ _(t, {
53
+ return /* @__PURE__ */ _(l, {
54
54
  title: "Stories",
55
55
  description: "Articles, essays, explainers, photo stories and timelines — across the full editorial lifecycle.",
56
56
  actions: /* @__PURE__ */ _(v, {
@@ -59,8 +59,8 @@ function E() {
59
59
  }),
60
60
  children: [
61
61
  /* @__PURE__ */ _("div", {
62
- className: "mb-5 flex flex-wrap items-center gap-3",
63
- children: [/* @__PURE__ */ g(l, {
62
+ className: "mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
63
+ children: [/* @__PURE__ */ g(u, {
64
64
  options: T,
65
65
  value: O,
66
66
  onChange: (e) => {
@@ -72,7 +72,7 @@ function E() {
72
72
  j(e), D.reset();
73
73
  },
74
74
  children: [/* @__PURE__ */ g(S, {
75
- className: "w-[12rem]",
75
+ className: "w-full sm:w-[13rem]",
76
76
  "aria-label": "Filter by topic",
77
77
  children: /* @__PURE__ */ g(C, { placeholder: "All topics" })
78
78
  }), /* @__PURE__ */ _(b, { children: [/* @__PURE__ */ g(x, {
@@ -84,10 +84,10 @@ function E() {
84
84
  }, e.id))] })]
85
85
  })]
86
86
  }),
87
- I ? /* @__PURE__ */ g(r, { label: "Loading stories…" }) : L ? /* @__PURE__ */ g(n, {
87
+ I ? /* @__PURE__ */ g(n, { label: "Loading stories…" }) : L ? /* @__PURE__ */ g(t, {
88
88
  message: "We couldn't load stories.",
89
89
  onRetry: () => R()
90
- }) : z.length === 0 && D.page === 0 ? /* @__PURE__ */ g(a, {
90
+ }) : z.length === 0 && D.page === 0 ? /* @__PURE__ */ g(i, {
91
91
  illustration: "empty-data",
92
92
  icon: /* @__PURE__ */ g(p, { className: "h-10 w-10" }),
93
93
  title: O === "ALL" && A === "ALL" ? "No stories yet" : "No stories match these filters",
@@ -97,12 +97,12 @@ function E() {
97
97
  children: [/* @__PURE__ */ g(m, { className: "mr-1.5 h-4 w-4" }), " New story"]
98
98
  })
99
99
  }) : /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("div", {
100
- className: "grid gap-5 sm:grid-cols-2 xl:grid-cols-3",
101
- children: z.map((e) => /* @__PURE__ */ g(i, {
100
+ className: "grid gap-5 sm:grid-cols-2 lg:grid-cols-3",
101
+ children: z.map((e) => /* @__PURE__ */ g(r, {
102
102
  story: e,
103
103
  onOpen: () => E(`stories/${e.id}`)
104
104
  }, e.id))
105
- }), /* @__PURE__ */ g(u, {
105
+ }), /* @__PURE__ */ g(c, {
106
106
  page: D.page,
107
107
  pageSize: w,
108
108
  itemCount: z.length,
@@ -1 +1 @@
1
- {"version":3,"file":"StoriesPage.js","names":[],"sources":["../../../../src/theculturepost/pages/stories/StoriesPage.tsx"],"sourcesContent":["/**\n * StoriesPage — browse the newsroom.\n * @module theculturepost/pages/stories\n *\n * Reads `tcpListStories` (filtered by status + optional topic). \"New Story\"\n * opens {@link CreateStoryDialog}. Cursor-paginated.\n */\n\nimport { useState } from 'react';\nimport { BookOpen, Plus } from 'lucide-react';\nimport {\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { FilterTabs, type FilterOption } from '../../components/FilterTabs';\nimport { StoryCard } from '../../components/StoryCard';\nimport { CreateStoryDialog } from '../../components/CreateStoryDialog';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport { useTcpStories, useTcpTopics, useCursorPager } from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport type { TcpStoryStatus } from '../../types';\n\nconst PAGE_SIZE = 12;\n\ntype StatusFilter = 'ALL' | TcpStoryStatus;\n\nconst STATUS_FILTERS: FilterOption<StatusFilter>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'IN_REVIEW', label: 'In Review' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'IDEA', label: 'Idea' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nexport function StoriesPage() {\n const { navigateTo } = useTheCulturePost();\n const pager = useCursorPager();\n const [status, setStatus] = useState<StatusFilter>('ALL');\n const [topicId, setTopicId] = useState<string>('ALL');\n const [createOpen, setCreateOpen] = useState(false);\n\n const topicsQuery = useTcpTopics({ pagination: { limit: 100 } });\n const { data, isLoading, isError, refetch } = useTcpStories({\n status: status === 'ALL' ? undefined : status,\n topicId: topicId === 'ALL' ? undefined : topicId,\n pagination: { limit: PAGE_SIZE, cursor: pager.cursor },\n });\n\n const stories = data?.items ?? [];\n\n const changeStatus = (next: StatusFilter) => {\n setStatus(next);\n pager.reset();\n };\n\n const changeTopic = (next: string) => {\n setTopicId(next);\n pager.reset();\n };\n\n return (\n <PageLayout\n title=\"Stories\"\n description=\"Articles, essays, explainers, photo stories and timelines — across the full editorial lifecycle.\"\n actions={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New story\n </Button>\n }\n >\n <div className=\"mb-5 flex flex-wrap items-center gap-3\">\n <FilterTabs options={STATUS_FILTERS} value={status} onChange={changeStatus} />\n <Select value={topicId} onValueChange={changeTopic}>\n <SelectTrigger className=\"w-[12rem]\" aria-label=\"Filter by topic\">\n <SelectValue placeholder=\"All topics\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All topics</SelectItem>\n {(topicsQuery.data?.items ?? []).map((topic) => (\n <SelectItem key={topic.id} value={topic.id}>\n {topic.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading stories…\" />\n ) : isError ? (\n <ErrorView message=\"We couldn't load stories.\" onRetry={() => refetch()} />\n ) : stories.length === 0 && pager.page === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<BookOpen className=\"h-10 w-10\" />}\n title={\n status === 'ALL' && topicId === 'ALL'\n ? 'No stories yet'\n : 'No stories match these filters'\n }\n description={\n status === 'ALL' && topicId === 'ALL'\n ? 'Draft the first story — it enters the editorial lifecycle you can publish from.'\n : 'Try widening the filters, or draft a new story.'\n }\n action={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New story\n </Button>\n }\n />\n ) : (\n <>\n <div className=\"grid gap-5 sm:grid-cols-2 xl:grid-cols-3\">\n {stories.map((story) => (\n <StoryCard\n key={story.id}\n story={story}\n onOpen={() => navigateTo(`stories/${story.id}`)}\n />\n ))}\n </div>\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={stories.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target, data?.pageInfo.cursor)}\n />\n </>\n )}\n\n <CreateStoryDialog\n open={createOpen}\n onOpenChange={setCreateOpen}\n topicId={topicId === 'ALL' ? undefined : topicId}\n onCreated={(story) => navigateTo(`stories/${story.id}`)}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"StoriesPage.js","names":[],"sources":["../../../../src/theculturepost/pages/stories/StoriesPage.tsx"],"sourcesContent":["/**\n * StoriesPage — browse the newsroom.\n * @module theculturepost/pages/stories\n *\n * Reads `tcpListStories` (filtered by status + optional topic). \"New Story\"\n * opens {@link CreateStoryDialog}. Cursor-paginated.\n */\n\nimport { useState } from 'react';\nimport { BookOpen, Plus } from 'lucide-react';\nimport {\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { FilterTabs, type FilterOption } from '../../components/FilterTabs';\nimport { StoryCard } from '../../components/StoryCard';\nimport { CreateStoryDialog } from '../../components/CreateStoryDialog';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport { useTcpStories, useTcpTopics, useCursorPager } from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport type { TcpStoryStatus } from '../../types';\n\nconst PAGE_SIZE = 12;\n\ntype StatusFilter = 'ALL' | TcpStoryStatus;\n\nconst STATUS_FILTERS: FilterOption<StatusFilter>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'IN_REVIEW', label: 'In Review' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'IDEA', label: 'Idea' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nexport function StoriesPage() {\n const { navigateTo } = useTheCulturePost();\n const pager = useCursorPager();\n const [status, setStatus] = useState<StatusFilter>('ALL');\n const [topicId, setTopicId] = useState<string>('ALL');\n const [createOpen, setCreateOpen] = useState(false);\n\n const topicsQuery = useTcpTopics({ pagination: { limit: 100 } });\n const { data, isLoading, isError, refetch } = useTcpStories({\n status: status === 'ALL' ? undefined : status,\n topicId: topicId === 'ALL' ? undefined : topicId,\n pagination: { limit: PAGE_SIZE, cursor: pager.cursor },\n });\n\n const stories = data?.items ?? [];\n\n const changeStatus = (next: StatusFilter) => {\n setStatus(next);\n pager.reset();\n };\n\n const changeTopic = (next: string) => {\n setTopicId(next);\n pager.reset();\n };\n\n return (\n <PageLayout\n title=\"Stories\"\n description=\"Articles, essays, explainers, photo stories and timelines — across the full editorial lifecycle.\"\n actions={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New story\n </Button>\n }\n >\n <div className=\"mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <FilterTabs options={STATUS_FILTERS} value={status} onChange={changeStatus} />\n <Select value={topicId} onValueChange={changeTopic}>\n <SelectTrigger className=\"w-full sm:w-[13rem]\" aria-label=\"Filter by topic\">\n <SelectValue placeholder=\"All topics\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All topics</SelectItem>\n {(topicsQuery.data?.items ?? []).map((topic) => (\n <SelectItem key={topic.id} value={topic.id}>\n {topic.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading stories…\" />\n ) : isError ? (\n <ErrorView message=\"We couldn't load stories.\" onRetry={() => refetch()} />\n ) : stories.length === 0 && pager.page === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<BookOpen className=\"h-10 w-10\" />}\n title={\n status === 'ALL' && topicId === 'ALL'\n ? 'No stories yet'\n : 'No stories match these filters'\n }\n description={\n status === 'ALL' && topicId === 'ALL'\n ? 'Draft the first story — it enters the editorial lifecycle you can publish from.'\n : 'Try widening the filters, or draft a new story.'\n }\n action={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New story\n </Button>\n }\n />\n ) : (\n <>\n <div className=\"grid gap-5 sm:grid-cols-2 lg:grid-cols-3\">\n {stories.map((story) => (\n <StoryCard\n key={story.id}\n story={story}\n onOpen={() => navigateTo(`stories/${story.id}`)}\n />\n ))}\n </div>\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={stories.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target, data?.pageInfo.cursor)}\n />\n </>\n )}\n\n <CreateStoryDialog\n open={createOpen}\n onOpenChange={setCreateOpen}\n topicId={topicId === 'ALL' ? undefined : topicId}\n onCreated={(story) => navigateTo(`stories/${story.id}`)}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}