@burdenoff/microfe-theculturepost 2026.802.1 → 2026.830.1

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 (49) hide show
  1. package/dist/theculturepost/components/CitationList.d.ts +2 -1
  2. package/dist/theculturepost/components/CitationList.d.ts.map +1 -1
  3. package/dist/theculturepost/components/CitationList.js +114 -95
  4. package/dist/theculturepost/components/CitationList.js.map +1 -1
  5. package/dist/theculturepost/components/SourceScanLink.d.ts +25 -0
  6. package/dist/theculturepost/components/SourceScanLink.d.ts.map +1 -0
  7. package/dist/theculturepost/components/SourceScanLink.js +36 -0
  8. package/dist/theculturepost/components/SourceScanLink.js.map +1 -0
  9. package/dist/theculturepost/components/index.d.ts +1 -0
  10. package/dist/theculturepost/components/index.d.ts.map +1 -1
  11. package/dist/theculturepost/components/index.js +1 -0
  12. package/dist/theculturepost/hooks/useTr.js +2 -2
  13. package/dist/theculturepost/hooks/useTr.js.map +1 -1
  14. package/dist/theculturepost/pages/HomePage.d.ts.map +1 -1
  15. package/dist/theculturepost/pages/HomePage.js +145 -127
  16. package/dist/theculturepost/pages/HomePage.js.map +1 -1
  17. package/dist/theculturepost/pages/governance/GovernancePage.d.ts +3 -1
  18. package/dist/theculturepost/pages/governance/GovernancePage.d.ts.map +1 -1
  19. package/dist/theculturepost/pages/governance/GovernancePage.js +122 -99
  20. package/dist/theculturepost/pages/governance/GovernancePage.js.map +1 -1
  21. package/dist/theculturepost/pages/stories/StoryDetailPage.d.ts +3 -0
  22. package/dist/theculturepost/pages/stories/StoryDetailPage.d.ts.map +1 -1
  23. package/dist/theculturepost/pages/stories/StoryDetailPage.js +202 -132
  24. package/dist/theculturepost/pages/stories/StoryDetailPage.js.map +1 -1
  25. package/dist/theculturepost/translations/ar.d.ts +43 -0
  26. package/dist/theculturepost/translations/ar.d.ts.map +1 -1
  27. package/dist/theculturepost/translations/ar.js +43 -0
  28. package/dist/theculturepost/translations/ar.js.map +1 -1
  29. package/dist/theculturepost/translations/en.d.ts +43 -0
  30. package/dist/theculturepost/translations/en.d.ts.map +1 -1
  31. package/dist/theculturepost/translations/en.js +43 -0
  32. package/dist/theculturepost/translations/en.js.map +1 -1
  33. package/dist/theculturepost/translations/es.d.ts +43 -0
  34. package/dist/theculturepost/translations/es.d.ts.map +1 -1
  35. package/dist/theculturepost/translations/es.js +43 -0
  36. package/dist/theculturepost/translations/es.js.map +1 -1
  37. package/dist/theculturepost/translations/hi.d.ts +43 -0
  38. package/dist/theculturepost/translations/hi.d.ts.map +1 -1
  39. package/dist/theculturepost/translations/hi.js +43 -0
  40. package/dist/theculturepost/translations/hi.js.map +1 -1
  41. package/dist/theculturepost/translations/ta.d.ts +43 -0
  42. package/dist/theculturepost/translations/ta.d.ts.map +1 -1
  43. package/dist/theculturepost/translations/ta.js +43 -0
  44. package/dist/theculturepost/translations/ta.js.map +1 -1
  45. package/dist/theculturepost/translations/zh.d.ts +43 -0
  46. package/dist/theculturepost/translations/zh.d.ts.map +1 -1
  47. package/dist/theculturepost/translations/zh.js +43 -0
  48. package/dist/theculturepost/translations/zh.js.map +1 -1
  49. package/package.json +2 -2
@@ -1,108 +1,144 @@
1
1
  import { useTheCulturePost as e } from "../../context/TheCulturePostContext.js";
2
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";
3
+ import { cn as t } from "../../utils/cn.js";
4
+ import { PageHero as n } from "../../components/PageHero.js";
5
+ import { ErrorView as r, LoadingView as i, Section as a } from "../../components/StateViews.js";
6
+ import { formatRelativeTime as ee } from "../../utils/formatters.js";
6
7
  import { StatusBadge as o } from "../../components/StatusBadge.js";
7
- import { useTcpArchiveItems as s, useTcpCollections as c, useTcpDiscussions as l, useTcpStories as u } from "../../hooks/useTcpApi.js";
8
+ import { useTcpArchiveItems as te, useTcpCollections as s, useTcpDiscussions as c, useTcpStories as l } from "../../hooks/useTcpApi.js";
8
9
  import "../../hooks/index.js";
9
- import { EmptyState as d } from "../../components/EmptyState.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";
10
+ import { EmptyState as u } from "../../components/EmptyState.js";
11
+ import { StatTile as d } from "../../components/viz/StatTile.js";
12
+ import { BarChart as f } from "../../components/viz/BarChart.js";
13
+ import { Donut as p } from "../../components/viz/Donut.js";
13
14
  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";
15
+ import { useMemo as m } from "react";
16
+ import { Activity as h, ArrowRight as g, BookOpen as _, CheckCircle2 as v, CircleSlash as y, Clock as b, Flag as x, Library as ne, Lightbulb as re, MessagesSquare as S, PencilLine as C, ShieldCheck as w } from "lucide-react";
16
17
  import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
17
18
  import { GlassCard as O } from "@burdenoff/fe-libs/ui";
18
19
  //#region src/theculturepost/pages/governance/GovernancePage.tsx
19
20
  var k = 100, A = {
20
- story: v,
21
+ story: _,
21
22
  discussion: S
22
- };
23
- function j() {
24
- let { navigateTo: j } = e(), M = u({
23
+ }, j = {
24
+ APPROVED: {
25
+ label: "Approved",
26
+ icon: v,
27
+ iconClass: "text-accent"
28
+ },
29
+ PENDING: {
30
+ label: "Pending review",
31
+ icon: b,
32
+ iconClass: "text-text-tertiary"
33
+ },
34
+ FLAGGED: {
35
+ label: "Flagged",
36
+ icon: x,
37
+ iconClass: "text-status-warning-text"
38
+ },
39
+ REMOVED: {
40
+ label: "Removed",
41
+ icon: y,
42
+ iconClass: "text-status-error-text"
43
+ }
44
+ }, M = [
45
+ "APPROVED",
46
+ "PENDING",
47
+ "FLAGGED",
48
+ "REMOVED"
49
+ ];
50
+ function N() {
51
+ let { navigateTo: v } = e(), y = l({
25
52
  status: "IDEA",
26
53
  pagination: { limit: k }
27
- }), N = u({
54
+ }), x = l({
28
55
  status: "DRAFT",
29
56
  pagination: { limit: k }
30
- }), P = u({
57
+ }), N = l({
31
58
  status: "IN_REVIEW",
32
59
  pagination: { limit: k }
33
- }), F = u({
60
+ }), P = l({
34
61
  status: "PUBLISHED",
35
62
  pagination: { limit: k }
36
- }), I = l({ pagination: { limit: k } }), L = c({ limit: k }), R = s({ pagination: { limit: k } }), z = [
63
+ }), F = c({ pagination: { limit: k } }), I = s({ limit: k }), L = te({ pagination: { limit: k } }), R = [
37
64
  ...[
38
- M,
65
+ y,
66
+ x,
39
67
  N,
40
- P,
41
- F
68
+ P
42
69
  ],
70
+ F,
43
71
  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 = [
72
+ L
73
+ ], z = m(() => y.data?.items ?? [], [y.data]), B = m(() => x.data?.items ?? [], [x.data]), V = m(() => N.data?.items ?? [], [N.data]), H = m(() => P.data?.items ?? [], [P.data]), U = m(() => F.data?.items ?? [], [F.data]), W = z.length, G = B.length, K = V.length, q = H.length, J = U.length, Y = m(() => {
74
+ let e = {
75
+ APPROVED: 0,
76
+ PENDING: 0,
77
+ FLAGGED: 0,
78
+ REMOVED: 0
79
+ };
80
+ for (let t of U) Object.prototype.hasOwnProperty.call(e, t.status) && (e[t.status] += 1);
81
+ return e;
82
+ }, [U]), X = Y.APPROVED, Z = J - X, Q = I.data?.length ?? 0, ie = L.data?.items.length ?? 0, ae = [
47
83
  {
48
84
  label: "Idea",
49
- value: G
85
+ value: W
50
86
  },
51
87
  {
52
88
  label: "Draft",
53
- value: K
89
+ value: G
54
90
  },
55
91
  {
56
92
  label: "In Review",
57
- value: q
93
+ value: K
58
94
  },
59
95
  {
60
96
  label: "Published",
61
- value: J
97
+ value: q
62
98
  }
63
- ], $ = h(() => {
99
+ ], $ = m(() => {
64
100
  let e = [
101
+ ...z,
65
102
  ...B,
66
103
  ...V,
67
- ...H,
68
- ...U
104
+ ...H
69
105
  ].map((e) => ({
70
106
  id: `story:${e.id}`,
71
107
  kind: "story",
72
108
  title: e.title,
73
109
  status: e.status,
74
110
  createdAt: e.createdAt,
75
- onOpen: () => j(`stories/${e.id}`)
76
- })), t = W.map((e) => ({
111
+ onOpen: () => v(`stories/${e.id}`)
112
+ })), t = U.map((e) => ({
77
113
  id: `discussion:${e.id}`,
78
114
  kind: "discussion",
79
115
  title: e.title,
80
116
  status: e.status,
81
117
  createdAt: e.createdAt,
82
- onOpen: () => j(`discussions/${e.id}`)
118
+ onOpen: () => v(`discussions/${e.id}`)
83
119
  }));
84
120
  return [...e, ...t].sort((e, t) => new Date(t.createdAt).getTime() - new Date(e.createdAt).getTime()).slice(0, 8);
85
121
  }, [
122
+ z,
86
123
  B,
87
124
  V,
88
125
  H,
89
126
  U,
90
- W,
91
- j
92
- ]), ne = z.some((e) => e.isLoading), re = z.some((e) => e.isError);
127
+ v
128
+ ]), oe = R.some((e) => e.isLoading), se = R.some((e) => e.isError);
93
129
  return /* @__PURE__ */ E("div", {
94
130
  className: "h-full overflow-auto",
95
131
  children: /* @__PURE__ */ D("div", {
96
132
  className: "mx-auto flex max-w-6xl flex-col gap-6 p-4 sm:p-6",
97
- children: [/* @__PURE__ */ E(t, {
133
+ children: [/* @__PURE__ */ E(n, {
98
134
  eyebrow: "Editorial",
99
135
  title: "Governance & Editorial",
100
136
  description: "Oversee content quality, discussions and community participation — at a glance."
101
- }), ne ? /* @__PURE__ */ E(r, { label: "Computing editorial metrics…" }) : re ? /* @__PURE__ */ E(n, {
137
+ }), oe ? /* @__PURE__ */ E(i, { label: "Computing editorial metrics…" }) : se ? /* @__PURE__ */ E(r, {
102
138
  title: "Couldn't load the overview",
103
139
  message: "We couldn't compute the editorial metrics for this workspace.",
104
140
  onRetry: () => {
105
- z.forEach((e) => {
141
+ R.forEach((e) => {
106
142
  e.refetch();
107
143
  });
108
144
  }
@@ -114,40 +150,40 @@ function j() {
114
150
  /* @__PURE__ */ D("div", {
115
151
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
116
152
  children: [
117
- /* @__PURE__ */ E(f, {
118
- icon: /* @__PURE__ */ E(v, { className: "h-5 w-5" }),
153
+ /* @__PURE__ */ E(d, {
154
+ icon: /* @__PURE__ */ E(_, { className: "h-5 w-5" }),
119
155
  label: "Published",
120
- value: J,
156
+ value: q,
121
157
  caption: "stories live"
122
158
  }),
123
- /* @__PURE__ */ E(f, {
124
- icon: /* @__PURE__ */ E(y, { className: "h-5 w-5" }),
159
+ /* @__PURE__ */ E(d, {
160
+ icon: /* @__PURE__ */ E(b, { className: "h-5 w-5" }),
125
161
  label: "In Review",
126
- value: q,
162
+ value: K,
127
163
  caption: "awaiting editorial sign-off"
128
164
  }),
129
- /* @__PURE__ */ E(f, {
165
+ /* @__PURE__ */ E(d, {
130
166
  icon: /* @__PURE__ */ E(C, { className: "h-5 w-5" }),
131
167
  label: "Draft",
132
- value: K,
168
+ value: G,
133
169
  caption: "in progress"
134
170
  }),
135
- /* @__PURE__ */ E(f, {
136
- icon: /* @__PURE__ */ E(x, { className: "h-5 w-5" }),
171
+ /* @__PURE__ */ E(d, {
172
+ icon: /* @__PURE__ */ E(re, { className: "h-5 w-5" }),
137
173
  label: "Idea",
138
- value: G,
174
+ value: W,
139
175
  caption: "pitched, not yet drafted"
140
176
  }),
141
- /* @__PURE__ */ E(f, {
177
+ /* @__PURE__ */ E(d, {
142
178
  icon: /* @__PURE__ */ E(S, { className: "h-5 w-5" }),
143
179
  label: "Discussions",
144
- value: Y,
180
+ value: J,
145
181
  caption: "community conversations"
146
182
  }),
147
- /* @__PURE__ */ E(f, {
148
- icon: /* @__PURE__ */ E(b, { className: "h-5 w-5" }),
183
+ /* @__PURE__ */ E(d, {
184
+ icon: /* @__PURE__ */ E(ne, { className: "h-5 w-5" }),
149
185
  label: "Archive",
150
- value: ee,
186
+ value: ie,
151
187
  caption: `artifacts in ${Q} collection${Q === 1 ? "" : "s"}`
152
188
  })
153
189
  ]
@@ -163,13 +199,13 @@ function j() {
163
199
  }), /* @__PURE__ */ E("p", {
164
200
  className: "mt-0.5 text-sm text-text-secondary",
165
201
  children: "Stories across the editorial lifecycle."
166
- })] }), J + q + K + G === 0 ? /* @__PURE__ */ E(d, {
167
- icon: /* @__PURE__ */ E(v, { className: "h-9 w-9" }),
202
+ })] }), q + K + G + W === 0 ? /* @__PURE__ */ E(u, {
203
+ icon: /* @__PURE__ */ E(_, { className: "h-9 w-9" }),
168
204
  title: "No stories yet",
169
205
  description: "Draft the first story to populate the pipeline."
170
- }) : /* @__PURE__ */ E(p, {
206
+ }) : /* @__PURE__ */ E(f, {
171
207
  ariaLabel: "Stories by lifecycle stage",
172
- data: te,
208
+ data: ae,
173
209
  height: 180
174
210
  })]
175
211
  }), /* @__PURE__ */ D(O, {
@@ -181,64 +217,51 @@ function j() {
181
217
  }), /* @__PURE__ */ E("p", {
182
218
  className: "mt-0.5 text-sm text-text-secondary",
183
219
  children: "Share of community discussions that are approved."
184
- })] }), Y === 0 ? /* @__PURE__ */ E(d, {
220
+ })] }), J === 0 ? /* @__PURE__ */ E(u, {
185
221
  icon: /* @__PURE__ */ E(S, { className: "h-9 w-9" }),
186
222
  title: "No discussions yet",
187
223
  description: "Open a discussion to start tracking conversation quality."
188
224
  }) : /* @__PURE__ */ D("div", {
189
225
  className: "flex flex-col items-center gap-5 sm:flex-row sm:items-center sm:gap-6",
190
- children: [/* @__PURE__ */ E(m, {
226
+ children: [/* @__PURE__ */ E(p, {
191
227
  value: X,
192
- total: Y,
228
+ total: J,
193
229
  caption: "approved",
194
230
  size: 140,
195
- ariaLabel: `${X} of ${Y} discussions approved`
231
+ ariaLabel: `${X} of ${J} discussions approved`
196
232
  }), /* @__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", {
233
+ className: "flex w-full flex-col gap-2.5",
234
+ children: [M.map((e) => {
235
+ let n = j[e], r = n.icon;
236
+ return /* @__PURE__ */ D("div", {
213
237
  className: "flex items-center justify-between gap-3",
214
238
  children: [/* @__PURE__ */ D("dt", {
215
239
  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",
240
+ children: [/* @__PURE__ */ E(r, {
241
+ className: t("h-4 w-4 shrink-0", n.iconClass),
218
242
  "aria-hidden": "true"
219
- }), "Needs attention"]
243
+ }), n.label]
220
244
  }), /* @__PURE__ */ E("dd", {
221
245
  className: "font-display text-sm font-semibold tabular-nums text-text-primary",
222
- children: Z
246
+ children: Y[e]
223
247
  })]
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
- ]
248
+ }, e);
249
+ }), /* @__PURE__ */ D("div", {
250
+ className: "mt-1 flex items-start gap-2 rounded-lg bg-bg-sunken/60 p-3 text-xs text-text-secondary",
251
+ children: [/* @__PURE__ */ E(w, {
252
+ className: "mt-0.5 h-4 w-4 shrink-0 text-accent",
253
+ "aria-hidden": "true"
254
+ }), /* @__PURE__ */ E("span", { children: Z === 0 ? "Every discussion in this workspace has been approved — nothing is waiting on a moderator." : Z === 1 ? "Approved discussions are published; one other needs a moderator’s attention." : `Approved discussions are published; ${Z} others need a moderator’s attention.` })]
255
+ })]
233
256
  })]
234
257
  })]
235
258
  })]
236
259
  }),
237
- /* @__PURE__ */ E(i, {
260
+ /* @__PURE__ */ E(a, {
238
261
  title: "Recent activity",
239
262
  description: "The latest stories and discussions across the workspace.",
240
- children: $.length === 0 ? /* @__PURE__ */ E(d, {
241
- icon: /* @__PURE__ */ E(g, { className: "h-9 w-9" }),
263
+ children: $.length === 0 ? /* @__PURE__ */ E(u, {
264
+ icon: /* @__PURE__ */ E(h, { className: "h-9 w-9" }),
242
265
  title: "Nothing yet",
243
266
  description: "Draft a story or open a discussion — it will show up here."
244
267
  }) : /* @__PURE__ */ E(O, {
@@ -266,7 +289,7 @@ function j() {
266
289
  children: [
267
290
  e.kind,
268
291
  " · ",
269
- a(e.createdAt)
292
+ ee(e.createdAt)
270
293
  ]
271
294
  })]
272
295
  }),
@@ -274,7 +297,7 @@ function j() {
274
297
  status: e.status,
275
298
  className: "hidden shrink-0 sm:inline-flex"
276
299
  }),
277
- /* @__PURE__ */ E(_, {
300
+ /* @__PURE__ */ E(g, {
278
301
  className: "h-4 w-4 shrink-0 text-text-tertiary",
279
302
  "aria-hidden": "true"
280
303
  })
@@ -288,6 +311,6 @@ function j() {
288
311
  });
289
312
  }
290
313
  //#endregion
291
- export { j as GovernancePage };
314
+ export { N as GovernancePage };
292
315
 
293
316
  //# sourceMappingURL=GovernancePage.js.map
@@ -1 +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
+ {"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 * beside the real per-status moderation tally (Approved / Pending / Flagged\n * / Removed) read straight off `TcpDiscussion.status`.\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 CheckCircle2,\n Flag,\n CircleSlash,\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';\nimport { cn } from '../../utils/cn';\nimport type { TcpModerationStatus } from '../../types';\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\ninterface ModerationMetaEntry {\n label: string;\n icon: LucideIcon;\n /** Semantic-token colour for the leading icon. */\n iconClass: string;\n}\n\n/**\n * The four real `TcpModerationStatus` values, in reading order. Everything that\n * is not APPROVED needs a moderator, but they need different things — so the\n * panel names each one instead of collapsing them into one opaque number.\n */\nconst MODERATION_META: Record<TcpModerationStatus, ModerationMetaEntry> = {\n APPROVED: { label: 'Approved', icon: CheckCircle2, iconClass: 'text-accent' },\n PENDING: { label: 'Pending review', icon: Clock, iconClass: 'text-text-tertiary' },\n FLAGGED: { label: 'Flagged', icon: Flag, iconClass: 'text-status-warning-text' },\n REMOVED: { label: 'Removed', icon: CircleSlash, iconClass: 'text-status-error-text' },\n};\n\nconst MODERATION_ORDER: TcpModerationStatus[] = ['APPROVED', 'PENDING', 'FLAGGED', 'REMOVED'];\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\n // One pass over the discussions already in hand — no extra query.\n const moderationCounts = useMemo(() => {\n const counts: Record<TcpModerationStatus, number> = {\n APPROVED: 0,\n PENDING: 0,\n FLAGGED: 0,\n REMOVED: 0,\n };\n for (const discussion of discussions) {\n if (Object.prototype.hasOwnProperty.call(counts, discussion.status)) {\n counts[discussion.status] += 1;\n }\n }\n return counts;\n }, [discussions]);\n\n const approvedCount = moderationCounts.APPROVED;\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-2.5\">\n {MODERATION_ORDER.map((status) => {\n const meta = MODERATION_META[status];\n const Icon = meta.icon;\n return (\n <div key={status} className=\"flex items-center justify-between gap-3\">\n <dt className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Icon\n className={cn('h-4 w-4 shrink-0', meta.iconClass)}\n aria-hidden=\"true\"\n />\n {meta.label}\n </dt>\n <dd className=\"font-display text-sm font-semibold tabular-nums text-text-primary\">\n {moderationCounts[status]}\n </dd>\n </div>\n );\n })}\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 {needsAttentionCount === 0\n ? 'Every discussion in this workspace has been approved — nothing is waiting on a moderator.'\n : needsAttentionCount === 1\n ? 'Approved discussions are published; one other needs a moderator’s attention.'\n : `Approved discussions are published; ${needsAttentionCount} others need a moderator’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":";;;;;;;;;;;;;;;;;;;AA6DA,IAAM,IAAc,KAad,IAAkD;CACtD,OAAO;CACP,YAAY;CACb,EAcK,IAAoE;CACxE,UAAU;EAAE,OAAO;EAAY,MAAM;EAAc,WAAW;EAAe;CAC7E,SAAS;EAAE,OAAO;EAAkB,MAAM;EAAO,WAAW;EAAsB;CAClF,SAAS;EAAE,OAAO;EAAW,MAAM;EAAM,WAAW;EAA4B;CAChF,SAAS;EAAE,OAAO;EAAW,MAAM;EAAa,WAAW;EAA0B;CACtF,EAEK,IAA0C;CAAC;CAAY;CAAW;CAAW;CAAU;AAE7F,SAAgB,IAAiB;CAC/B,IAAM,EAAE,kBAAe,GAAmB,EAIpC,IAAY,EAAc;EAAE,QAAQ;EAAQ,YAAY,EAAE,OAAO,GAAa;EAAE,CAAC,EACjF,IAAa,EAAc;EAAE,QAAQ;EAAS,YAAY,EAAE,OAAO,GAAa;EAAE,CAAC,EACnF,IAAc,EAAc;EAAE,QAAQ;EAAa,YAAY,EAAE,OAAO,GAAa;EAAE,CAAC,EACxF,IAAiB,EAAc;EACnC,QAAQ;EACR,YAAY,EAAE,OAAO,GAAa;EACnC,CAAC,EACI,IAAmB,EAAkB,EAAE,YAAY,EAAE,OAAO,GAAa,EAAE,CAAC,EAC5E,IAAmB,EAAkB,EAAE,OAAO,GAAa,CAAC,EAC5D,IAAiB,GAAmB,EAAE,YAAY,EAAE,OAAO,GAAa,EAAE,CAAC,EAG3E,IAAa;EAAC,GADC;GAAC;GAAW;GAAY;GAAa;GAAe;EACpC;EAAkB;EAAkB;EAAe,EAElF,IAAc,QAAc,EAAU,MAAM,SAAS,EAAE,EAAE,CAAC,EAAU,KAAK,CAAC,EAC1E,IAAe,QAAc,EAAW,MAAM,SAAS,EAAE,EAAE,CAAC,EAAW,KAAK,CAAC,EAC7E,IAAgB,QAAc,EAAY,MAAM,SAAS,EAAE,EAAE,CAAC,EAAY,KAAK,CAAC,EAChF,IAAmB,QAAc,EAAe,MAAM,SAAS,EAAE,EAAE,CAAC,EAAe,KAAK,CAAC,EACzF,IAAc,QAAc,EAAiB,MAAM,SAAS,EAAE,EAAE,CAAC,EAAiB,KAAK,CAAC,EAExF,IAAY,EAAY,QACxB,IAAa,EAAa,QAC1B,IAAc,EAAc,QAC5B,IAAiB,EAAiB,QAClC,IAAkB,EAAY,QAG9B,IAAmB,QAAc;EACrC,IAAM,IAA8C;GAClD,UAAU;GACV,SAAS;GACT,SAAS;GACT,SAAS;GACV;AACD,OAAK,IAAM,KAAc,EACvB,CAAI,OAAO,UAAU,eAAe,KAAK,GAAQ,EAAW,OAAO,KACjE,EAAO,EAAW,WAAW;AAGjC,SAAO;IACN,CAAC,EAAY,CAAC,EAEX,IAAgB,EAAiB,UACjC,IAAsB,IAAkB,GACxC,IAAmB,EAAiB,MAAM,UAAU,GACpD,KAAiB,EAAe,MAAM,MAAM,UAAU,GAEtD,KAA2B;EAC/B;GAAE,OAAO;GAAQ,OAAO;GAAW;EACnC;GAAE,OAAO;GAAS,OAAO;GAAY;EACrC;GAAE,OAAO;GAAa,OAAO;GAAa;EAC1C;GAAE,OAAO;GAAa,OAAO;GAAgB;EAC9C,EAEK,IAAiB,QAA6B;EAClD,IAAM,IAA2B;GAC/B,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG;GACJ,CAAC,KAAK,OAAW;GAChB,IAAI,SAAS,EAAM;GACnB,MAAM;GACN,OAAO,EAAM;GACb,QAAQ,EAAM;GACd,WAAW,EAAM;GACjB,cAAc,EAAW,WAAW,EAAM,KAAK;GAChD,EAAE,EAEG,IAAgC,EAAY,KAAK,OAAgB;GACrE,IAAI,cAAc,EAAW;GAC7B,MAAM;GACN,OAAO,EAAW;GAClB,QAAQ,EAAW;GACnB,WAAW,EAAW;GACtB,cAAc,EAAW,eAAe,EAAW,KAAK;GACzD,EAAE;AAEH,SAAO,CAAC,GAAG,GAAW,GAAG,EAAe,CACrC,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,CACjF,MAAM,GAAG,EAAE;IACb;EAAC;EAAa;EAAc;EAAe;EAAkB;EAAa;EAAW,CAAC,EAEnF,KAAY,EAAW,MAAM,MAAM,EAAE,UAAU,EAC/C,KAAU,EAAW,MAAM,MAAM,EAAE,QAAQ;AAQjD,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IACE,SAAQ;IACR,OAAM;IACN,aAAY;IACZ,CAAA,EAED,KACC,kBAAC,GAAD,EAAa,OAAM,gCAAiC,CAAA,GAClD,KACF,kBAAC,GAAD;IACE,OAAM;IACN,SAAQ;IACR,eArBa;AACrB,OAAW,SAAS,MAAM;AACnB,QAAE,SAAS;OAChB;;IAmBM,CAAA,GAEF,kBAAA,GAAA,EAAA,UAAA;IACE,kBAAC,KAAD;KAAG,WAAU;eAAmC;KAE5C,CAAA;IAGJ,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;OACtC,OAAM;OACN,OAAO;OACP,SAAQ;OACR,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OACnC,OAAM;OACN,OAAO;OACP,SAAQ;OACR,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;OACxC,OAAM;OACN,OAAO;OACP,SAAQ;OACR,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,IAAD,EAAW,WAAU,WAAY,CAAA;OACvC,OAAM;OACN,OAAO;OACP,SAAQ;OACR,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;OAC5C,OAAM;OACN,OAAO;OACP,SAAQ;OACR,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,IAAD,EAAS,WAAU,WAAY,CAAA;OACrC,OAAM;OACN,OAAO;OACP,SAAS,gBAAgB,EAAiB,aACxC,MAAqB,IAAI,KAAK;OAEhC,CAAA;MACE;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAuD;OAEhE,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBAAqC;OAE9C,CAAA,CACA,EAAA,CAAA,EACL,IAAiB,IAAc,IAAa,MAAc,IACzD,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;OACtC,OAAM;OACN,aAAY;OACZ,CAAA,GAEF,kBAAC,GAAD;OACE,WAAU;OACV,MAAM;OACN,QAAQ;OACR,CAAA,CAEM;SAEZ,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAuD;OAEhE,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBAAqC;OAE9C,CAAA,CACA,EAAA,CAAA,EACL,MAAoB,IACnB,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;OAC5C,OAAM;OACN,aAAY;OACZ,CAAA,GAEF,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,OAAO;QACP,OAAO;QACP,SAAQ;QACR,MAAM;QACN,WAAW,GAAG,EAAc,MAAM,EAAgB;QAClD,CAAA,EACF,kBAAC,MAAD;QAAI,WAAU;kBAAd,CACG,EAAiB,KAAK,MAAW;SAChC,IAAM,IAAO,EAAgB,IACvB,IAAO,EAAK;AAClB,gBACE,kBAAC,OAAD;UAAkB,WAAU;oBAA5B,CACE,kBAAC,MAAD;WAAI,WAAU;qBAAd,CACE,kBAAC,GAAD;YACE,WAAW,EAAG,oBAAoB,EAAK,UAAU;YACjD,eAAY;YACZ,CAAA,EACD,EAAK,MACH;cACL,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAiB;WACf,CAAA,CACD;YAXI,EAWJ;UAER,EACF,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UACE,WAAU;UACV,eAAY;UACZ,CAAA,EACF,kBAAC,QAAD,EAAA,UACG,MAAwB,IACrB,8FACA,MAAwB,IACtB,iFACA,uCAAuC,EAAoB,wCAC5D,CAAA,CACH;WACH;UACD;SAEE;QACR;;IAGN,kBAAC,GAAD;KACE,OAAM;KACN,aAAY;eAEX,EAAe,WAAW,IACzB,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;MACtC,OAAM;MACN,aAAY;MACZ,CAAA,GAEF,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBACpC,EAAe,KAAK,MAAQ;OAC3B,IAAM,IAAO,EAAc,EAAI;AAC/B,cACE,kBAAC,UAAD;QAEE,MAAK;QACL,SAAS,EAAI;QACb,WAAU;kBAJZ;SAME,kBAAC,QAAD;UACE,WAAU;UACV,eAAY;oBAEZ,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;UACvB,CAAA;SACP,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAI;WACH,CAAA,EACJ,kBAAC,KAAD;WAAG,WAAU;qBAAb;YACG,EAAI;YAAK;YAAI,GAAmB,EAAI,UAAU;YAC7C;aACA;;SACN,kBAAC,GAAD;UACE,QAAQ,EAAI;UACZ,WAAU;UACV,CAAA;SACF,kBAAC,GAAD;UACE,WAAU;UACV,eAAY;UACZ,CAAA;SACK;UA3BF,EAAI,GA2BF;QAEX;MACQ,CAAA;KAEN,CAAA;IACT,EAAA,CAAA,CAED;;EACF,CAAA"}
@@ -6,6 +6,9 @@
6
6
  * - READING (tcp-03): a cover-banner hero, serif display title, a drop-cap
7
7
  * article body with ornamental section breaks, and a right rail of citations
8
8
  * (compact) + linked discussions.
9
+ * - CITATIONS (tcp-05): the story's full evidence base — roomy source cards
10
+ * carrying excerpt, provenance, source link and scan, a labelled
11
+ * verification control per source, and the methodology legend.
9
12
  * - EDITORIAL (tcp-04): a visual lifecycle stepper with validated transitions
10
13
  * (Publish / Send back), plus citations as a verifiable-source table with an
11
14
  * inline verify control.
@@ -1 +1 @@
1
- {"version":3,"file":"StoryDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/theculturepost/pages/stories/StoryDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA6IH,wBAAgB,eAAe,4CAuL9B"}
1
+ {"version":3,"file":"StoryDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/theculturepost/pages/stories/StoryDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAkJH,wBAAgB,eAAe,4CA2L9B"}