@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
@@ -1,22 +1,24 @@
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, Section as i } from "../../components/StateViews.js";
4
2
  import "../../context/index.js";
5
- import { formatEnumLabel as a } from "../../utils/formatters.js";
6
- import { StatusBadge as o } from "../../components/StatusBadge.js";
3
+ import { PageHero as t } from "../../components/PageHero.js";
4
+ import { ErrorView as n, LoadingView as r } from "../../components/StateViews.js";
5
+ import { formatEnumLabel as i } from "../../utils/formatters.js";
6
+ import { StatusBadge as a } from "../../components/StatusBadge.js";
7
+ import { categoryIcon as o } from "../../components/categoryMeta.js";
7
8
  import { StoryCard as s } from "../../components/StoryCard.js";
8
9
  import { EmptyState as c } from "../../components/EmptyState.js";
9
10
  import { useTcpArchiveTopic as l, useTcpTopic as u, useTcpUpdateTopic as d } from "../../hooks/useTcpApi.js";
10
11
  import "../../hooks/index.js";
11
12
  import { describeError as f, notify as p } from "../../lib/toast.js";
12
13
  import { CreateStoryDialog as m } from "../../components/CreateStoryDialog.js";
13
- import { useMemo as h, useState as g } from "react";
14
- import { Archive as _, ArrowLeft as v, BookOpen as y, Compass as b, Pencil as x, Plus as S, Sparkles as C } from "lucide-react";
15
- import { useParams as w } from "react-router-dom";
16
- import { jsx as T, jsxs as E } from "react/jsx-runtime";
17
- import { Button as D, GlassCard as O, Input as k, Label as A, ResponsiveDialog as j, ResponsiveDialogContent as M, ResponsiveDialogDescription as N, ResponsiveDialogFooter as P, ResponsiveDialogHeader as F, ResponsiveDialogTitle as I, Select as L, SelectContent as R, SelectItem as z, SelectTrigger as B, SelectValue as V, Textarea as H } from "@burdenoff/fe-libs/ui";
14
+ import "../../components/index.js";
15
+ import { createElement as h, useMemo as g, useState as _ } from "react";
16
+ import { Archive as v, ArrowLeft as y, BookOpen as b, Globe as x, Pencil as S, Plus as C, Sparkles as w } from "lucide-react";
17
+ import { useParams as T } from "react-router-dom";
18
+ import { Fragment as E, jsx as D, jsxs as O } from "react/jsx-runtime";
19
+ import { Button as k, Input as A, Label as j, ResponsiveDialog as M, ResponsiveDialogContent as N, ResponsiveDialogDescription as P, ResponsiveDialogFooter as F, ResponsiveDialogHeader as I, ResponsiveDialogTitle as L, Select as R, SelectContent as z, SelectItem as B, SelectTrigger as V, SelectValue as H, Textarea as U } from "@burdenoff/fe-libs/ui";
18
20
  //#region src/theculturepost/pages/topics/TopicDetailPage.tsx
19
- var U = [
21
+ var W = [
20
22
  "CULTURE_AND_IDENTITY",
21
23
  "RELIGION_AND_FAITH",
22
24
  "HISTORY",
@@ -26,210 +28,284 @@ var U = [
26
28
  "ORAL_HISTORIES",
27
29
  "PHILOSOPHY_AND_THOUGHT",
28
30
  "VALUES_AND_ETHICS"
29
- ];
30
- function W() {
31
- let { id: a } = w(), { navigateTo: h } = e(), { data: k, isLoading: A, isError: j, refetch: M } = u(a), N = d(), P = l(), [F, I] = g(!1), [L, R] = g(!1), z = /* @__PURE__ */ E(D, {
32
- variant: "ghost",
33
- size: "sm",
34
- onClick: () => h("topics"),
35
- children: [/* @__PURE__ */ T(v, { className: "mr-1.5 h-4 w-4" }), " Back"]
36
- });
37
- if (A) return /* @__PURE__ */ T(t, {
38
- title: "Topic",
39
- actions: z,
40
- children: /* @__PURE__ */ T(r, { label: "Loading topic…" })
41
- });
42
- if (j || !k) return /* @__PURE__ */ T(t, {
43
- title: "Topic",
44
- actions: z,
45
- children: /* @__PURE__ */ T(n, {
46
- title: "Topic not found",
47
- message: "This topic may have been archived, or you may not have access.",
48
- onRetry: () => M()
31
+ ], G = [
32
+ {
33
+ value: "newest",
34
+ label: "Newest"
35
+ },
36
+ {
37
+ value: "oldest",
38
+ label: "Oldest"
39
+ },
40
+ {
41
+ value: "updated",
42
+ label: "Recently updated"
43
+ },
44
+ {
45
+ value: "title",
46
+ label: "Title (A–Z)"
47
+ }
48
+ ], K = (e) => {
49
+ let t = new Date(e.publishedAt ?? e.createdAt).getTime();
50
+ return Number.isNaN(t) ? 0 : t;
51
+ }, q = (e) => {
52
+ let t = new Date(e.updatedAt).getTime();
53
+ return Number.isNaN(t) ? 0 : t;
54
+ };
55
+ function J(e, t) {
56
+ let n = [...e];
57
+ switch (t) {
58
+ case "newest": return n.sort((e, t) => K(t) - K(e));
59
+ case "oldest": return n.sort((e, t) => K(e) - K(t));
60
+ case "updated": return n.sort((e, t) => q(t) - q(e));
61
+ case "title": return n.sort((e, t) => e.title.localeCompare(t.title));
62
+ default: return n;
63
+ }
64
+ }
65
+ function Y({ children: e }) {
66
+ return /* @__PURE__ */ D("div", {
67
+ className: "h-full overflow-y-auto",
68
+ children: /* @__PURE__ */ D("div", {
69
+ className: "mx-auto flex max-w-5xl flex-col gap-6 p-4 sm:p-6",
70
+ children: e
49
71
  })
50
72
  });
51
- let B = k.stories ?? [];
52
- return /* @__PURE__ */ E(t, {
53
- title: k.name,
54
- actions: /* @__PURE__ */ E("div", {
55
- className: "flex items-center gap-2",
73
+ }
74
+ function X() {
75
+ let { id: i } = T(), { navigateTo: A } = e(), { data: M, isLoading: N, isError: P, refetch: F } = u(i), I = d(), L = l(), [U, W] = _(!1), [K, q] = _(!1), [X, ee] = _("newest"), Q = g(() => M?.stories ?? [], [M]), te = g(() => J(Q, X), [Q, X]), $ = () => A("topics");
76
+ if (N) return /* @__PURE__ */ O(Y, { children: [/* @__PURE__ */ O("button", {
77
+ type: "button",
78
+ onClick: $,
79
+ className: "inline-flex w-fit items-center gap-1.5 text-sm font-medium text-text-secondary hover:text-text-primary",
80
+ children: [/* @__PURE__ */ D(y, {
81
+ className: "h-4 w-4",
82
+ "aria-hidden": "true"
83
+ }), " Topics"]
84
+ }), /* @__PURE__ */ D(r, { label: "Loading topic…" })] });
85
+ if (P || !M) return /* @__PURE__ */ O(Y, { children: [/* @__PURE__ */ O("button", {
86
+ type: "button",
87
+ onClick: $,
88
+ className: "inline-flex w-fit items-center gap-1.5 text-sm font-medium text-text-secondary hover:text-text-primary",
89
+ children: [/* @__PURE__ */ D(y, {
90
+ className: "h-4 w-4",
91
+ "aria-hidden": "true"
92
+ }), " Topics"]
93
+ }), /* @__PURE__ */ D(n, {
94
+ title: "Topic not found",
95
+ message: "This topic may have been archived, or you may not have access.",
96
+ onRetry: () => F()
97
+ })] });
98
+ let ne = async () => {
99
+ try {
100
+ await I.mutateAsync({
101
+ id: M.id,
102
+ input: { isFeatured: !M.isFeatured }
103
+ }), p.success(M.isFeatured ? "Removed from featured" : "Topic featured");
104
+ } catch (e) {
105
+ p.error("Could not update topic", f(e));
106
+ }
107
+ }, re = async () => {
108
+ try {
109
+ await L.mutateAsync(M.id), p.success("Topic archived"), A("topics");
110
+ } catch (e) {
111
+ p.error("Could not archive topic", f(e));
112
+ }
113
+ }, ie = /* @__PURE__ */ O(E, { children: [
114
+ M.isFeatured && /* @__PURE__ */ O("span", {
115
+ className: "inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2.5 py-0.5 text-xs font-medium text-status-warning-text",
116
+ children: [/* @__PURE__ */ D(w, {
117
+ className: "h-3 w-3",
118
+ "aria-hidden": "true"
119
+ }), " Featured"]
120
+ }),
121
+ /* @__PURE__ */ D(a, {
122
+ status: M.category,
123
+ icon: h(o(M.category), { className: "h-3 w-3" })
124
+ }),
125
+ M.tradition && /* @__PURE__ */ O("span", {
126
+ className: "inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary",
56
127
  children: [
57
- z,
58
- /* @__PURE__ */ E(D, {
59
- variant: "outline",
60
- size: "sm",
61
- onClick: async () => {
62
- try {
63
- await N.mutateAsync({
64
- id: k.id,
65
- input: { isFeatured: !k.isFeatured }
66
- }), p.success(k.isFeatured ? "Removed from featured" : "Topic featured");
67
- } catch (e) {
68
- p.error("Could not update topic", f(e));
69
- }
70
- },
71
- disabled: N.isPending,
72
- children: [/* @__PURE__ */ T(C, { className: "mr-1.5 h-4 w-4" }), k.isFeatured ? "Unfeature" : "Feature"]
73
- }),
74
- /* @__PURE__ */ E(D, {
75
- variant: "outline",
76
- size: "sm",
77
- onClick: () => I(!0),
78
- children: [/* @__PURE__ */ T(x, { className: "mr-1.5 h-4 w-4" }), " Edit"]
128
+ /* @__PURE__ */ D(x, {
129
+ className: "h-3 w-3",
130
+ "aria-hidden": "true"
79
131
  }),
80
- /* @__PURE__ */ E(D, {
81
- variant: "ghost",
82
- size: "sm",
83
- onClick: async () => {
84
- try {
85
- await P.mutateAsync(k.id), p.success("Topic archived"), h("topics");
86
- } catch (e) {
87
- p.error("Could not archive topic", f(e));
88
- }
89
- },
90
- disabled: P.isPending,
91
- className: "text-status-error-text",
92
- children: [/* @__PURE__ */ T(_, { className: "mr-1.5 h-4 w-4" }), " Archive"]
93
- })
132
+ " ",
133
+ M.tradition
94
134
  ]
135
+ })
136
+ ] }), ae = /* @__PURE__ */ O(E, { children: [
137
+ /* @__PURE__ */ O(k, {
138
+ variant: "outline",
139
+ size: "sm",
140
+ onClick: ne,
141
+ disabled: I.isPending,
142
+ children: [/* @__PURE__ */ D(w, { className: "mr-1.5 h-4 w-4" }), M.isFeatured ? "Unfeature" : "Feature"]
95
143
  }),
96
- children: [
97
- /* @__PURE__ */ E("div", {
98
- className: "mx-auto flex max-w-5xl flex-col gap-6",
99
- children: [/* @__PURE__ */ E(O, {
100
- treatment: "plain",
101
- className: "flex flex-col gap-4 p-6",
102
- children: [/* @__PURE__ */ E("div", {
103
- className: "flex items-start gap-4",
104
- children: [/* @__PURE__ */ T("div", {
105
- className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary",
106
- children: /* @__PURE__ */ T(b, { className: "h-6 w-6" })
107
- }), /* @__PURE__ */ E("div", {
108
- className: "min-w-0 flex-1",
109
- children: [/* @__PURE__ */ E("div", {
110
- className: "flex flex-wrap items-center gap-1.5",
111
- children: [/* @__PURE__ */ T(o, { status: k.category }), k.isFeatured && /* @__PURE__ */ E("span", {
112
- className: "inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text",
113
- children: [/* @__PURE__ */ T(C, { className: "h-3 w-3" }), " Featured"]
114
- })]
115
- }), k.tradition && /* @__PURE__ */ T("p", {
116
- className: "mt-1.5 text-sm text-text-secondary",
117
- children: k.tradition
118
- })]
119
- })]
120
- }), k.description && /* @__PURE__ */ T("p", {
121
- className: "whitespace-pre-wrap text-sm leading-relaxed text-text-secondary",
122
- children: k.description
144
+ /* @__PURE__ */ O(k, {
145
+ variant: "outline",
146
+ size: "sm",
147
+ onClick: () => W(!0),
148
+ children: [/* @__PURE__ */ D(S, { className: "mr-1.5 h-4 w-4" }), " Edit"]
149
+ }),
150
+ /* @__PURE__ */ O(k, {
151
+ variant: "outline",
152
+ size: "sm",
153
+ onClick: re,
154
+ disabled: L.isPending,
155
+ className: "text-status-error-text",
156
+ children: [/* @__PURE__ */ D(v, { className: "mr-1.5 h-4 w-4" }), " Archive"]
157
+ }),
158
+ /* @__PURE__ */ O(k, {
159
+ size: "sm",
160
+ onClick: () => q(!0),
161
+ children: [/* @__PURE__ */ D(C, { className: "mr-1.5 h-4 w-4" }), " New story"]
162
+ })
163
+ ] });
164
+ return /* @__PURE__ */ O(Y, { children: [
165
+ /* @__PURE__ */ D(t, {
166
+ eyebrow: "Topic",
167
+ title: M.name,
168
+ description: M.description ?? void 0,
169
+ backAction: {
170
+ label: "Topics",
171
+ onClick: $
172
+ },
173
+ meta: ie,
174
+ actions: ae
175
+ }),
176
+ /* @__PURE__ */ O("section", {
177
+ "aria-labelledby": "topic-stories-heading",
178
+ children: [/* @__PURE__ */ O("div", {
179
+ className: "mb-4 flex flex-wrap items-center justify-between gap-3",
180
+ children: [/* @__PURE__ */ O("div", {
181
+ className: "flex items-center gap-2",
182
+ children: [/* @__PURE__ */ D("h2", {
183
+ id: "topic-stories-heading",
184
+ className: "font-display text-xl font-semibold text-text-primary",
185
+ children: "Stories in this topic"
186
+ }), /* @__PURE__ */ D("span", {
187
+ className: "rounded-full bg-bg-sunken px-2 py-0.5 text-sm font-medium tabular-nums text-text-secondary",
188
+ children: Q.length
189
+ })]
190
+ }), Q.length > 1 && /* @__PURE__ */ O("div", {
191
+ className: "flex items-center gap-2",
192
+ children: [/* @__PURE__ */ D(j, {
193
+ htmlFor: "topic-story-sort",
194
+ className: "text-sm text-text-secondary",
195
+ children: "Sort by"
196
+ }), /* @__PURE__ */ O(R, {
197
+ value: X,
198
+ onValueChange: (e) => ee(e),
199
+ children: [/* @__PURE__ */ D(V, {
200
+ id: "topic-story-sort",
201
+ className: "w-[12rem]",
202
+ "aria-label": "Sort stories",
203
+ children: /* @__PURE__ */ D(H, {})
204
+ }), /* @__PURE__ */ D(z, { children: G.map((e) => /* @__PURE__ */ D(B, {
205
+ value: e.value,
206
+ children: e.label
207
+ }, e.value)) })]
123
208
  })]
124
- }), /* @__PURE__ */ T(i, {
125
- title: "Stories in this topic",
126
- action: /* @__PURE__ */ E(D, {
127
- size: "sm",
128
- variant: "outline",
129
- onClick: () => R(!0),
130
- children: [/* @__PURE__ */ T(S, { className: "mr-1.5 h-4 w-4" }), " New story"]
131
- }),
132
- children: B.length === 0 ? /* @__PURE__ */ T(c, {
133
- icon: /* @__PURE__ */ T(y, { className: "h-9 w-9" }),
134
- title: "No stories yet",
135
- description: "Draft the first story under this topic.",
136
- action: /* @__PURE__ */ E(D, {
137
- onClick: () => R(!0),
138
- children: [/* @__PURE__ */ T(S, { className: "mr-1.5 h-4 w-4" }), " New story"]
139
- })
140
- }) : /* @__PURE__ */ T("div", {
141
- className: "grid gap-5 sm:grid-cols-2 xl:grid-cols-3",
142
- children: B.map((e) => /* @__PURE__ */ T(s, {
143
- story: e,
144
- onOpen: () => h(`stories/${e.id}`)
145
- }, e.id))
146
- })
147
209
  })]
148
- }),
149
- /* @__PURE__ */ T(m, {
150
- open: L,
151
- onOpenChange: R,
152
- topicId: k.id,
153
- onCreated: (e) => h(`stories/${e.id}`)
154
- }),
155
- /* @__PURE__ */ T(G, {
156
- open: F,
157
- onOpenChange: I,
158
- topic: k
159
- })
160
- ]
161
- });
210
+ }), Q.length === 0 ? /* @__PURE__ */ D(c, {
211
+ icon: /* @__PURE__ */ D(b, { className: "h-9 w-9" }),
212
+ title: "No stories yet",
213
+ description: "Draft the first story under this topic.",
214
+ action: /* @__PURE__ */ O(k, {
215
+ onClick: () => q(!0),
216
+ children: [/* @__PURE__ */ D(C, { className: "mr-1.5 h-4 w-4" }), " New story"]
217
+ })
218
+ }) : /* @__PURE__ */ D("div", {
219
+ className: "grid gap-5 sm:grid-cols-2 xl:grid-cols-3",
220
+ children: te.map((e) => /* @__PURE__ */ D(s, {
221
+ story: e,
222
+ onOpen: () => A(`stories/${e.id}`)
223
+ }, e.id))
224
+ })]
225
+ }),
226
+ /* @__PURE__ */ D(m, {
227
+ open: K,
228
+ onOpenChange: q,
229
+ topicId: M.id,
230
+ onCreated: (e) => A(`stories/${e.id}`)
231
+ }),
232
+ /* @__PURE__ */ D(Z, {
233
+ open: U,
234
+ onOpenChange: W,
235
+ topic: M
236
+ })
237
+ ] });
162
238
  }
163
- function G({ open: e, onOpenChange: t, topic: n }) {
164
- let r = d(), [i, o] = g(n.name), [s, c] = g(n.tradition ?? ""), [l, u] = g(n.description ?? ""), [m, _] = g(n.category), [v, y] = g(null), b = h(() => `${n.id}:${n.updatedAt}`, [n.id, n.updatedAt]), [x, S] = g(b);
165
- return x !== b && (S(b), o(n.name), c(n.tradition ?? ""), u(n.description ?? ""), _(n.category), y(null)), /* @__PURE__ */ T(j, {
239
+ function Z({ open: e, onOpenChange: t, topic: n }) {
240
+ let r = d(), [a, o] = _(n.name), [s, c] = _(n.tradition ?? ""), [l, u] = _(n.description ?? ""), [m, h] = _(n.category), [v, y] = _(null), b = g(() => `${n.id}:${n.updatedAt}`, [n.id, n.updatedAt]), [x, S] = _(b);
241
+ return x !== b && (S(b), o(n.name), c(n.tradition ?? ""), u(n.description ?? ""), h(n.category), y(null)), /* @__PURE__ */ D(M, {
166
242
  open: e,
167
243
  onOpenChange: t,
168
- children: /* @__PURE__ */ E(M, {
244
+ children: /* @__PURE__ */ O(N, {
169
245
  className: "sm:max-w-lg",
170
246
  children: [
171
- /* @__PURE__ */ E(F, { children: [/* @__PURE__ */ T(I, { children: "Edit topic" }), /* @__PURE__ */ T(N, { children: "The URL slug is fixed once created." })] }),
172
- /* @__PURE__ */ E("div", {
247
+ /* @__PURE__ */ O(I, { children: [/* @__PURE__ */ D(L, { children: "Edit topic" }), /* @__PURE__ */ D(P, { children: "The URL slug is fixed once created." })] }),
248
+ /* @__PURE__ */ O("div", {
173
249
  className: "flex flex-col gap-4 py-2",
174
250
  children: [
175
- /* @__PURE__ */ E("div", {
251
+ /* @__PURE__ */ O("div", {
176
252
  className: "grid gap-1.5",
177
- children: [/* @__PURE__ */ T(A, {
253
+ children: [/* @__PURE__ */ D(j, {
178
254
  htmlFor: "edit-topic-name",
179
255
  children: "Name"
180
- }), /* @__PURE__ */ T(k, {
256
+ }), /* @__PURE__ */ D(A, {
181
257
  id: "edit-topic-name",
182
- value: i,
258
+ value: a,
183
259
  onChange: (e) => o(e.target.value)
184
260
  })]
185
261
  }),
186
- /* @__PURE__ */ E("div", {
262
+ /* @__PURE__ */ O("div", {
187
263
  className: "grid gap-1.5",
188
- children: [/* @__PURE__ */ T(A, { children: "Category" }), /* @__PURE__ */ E(L, {
264
+ children: [/* @__PURE__ */ D(j, { children: "Category" }), /* @__PURE__ */ O(R, {
189
265
  value: m,
190
- onValueChange: (e) => _(e),
191
- children: [/* @__PURE__ */ T(B, { children: /* @__PURE__ */ T(V, {}) }), /* @__PURE__ */ T(R, { children: U.map((e) => /* @__PURE__ */ T(z, {
266
+ onValueChange: (e) => h(e),
267
+ children: [/* @__PURE__ */ D(V, { children: /* @__PURE__ */ D(H, {}) }), /* @__PURE__ */ D(z, { children: W.map((e) => /* @__PURE__ */ D(B, {
192
268
  value: e,
193
- children: a(e)
269
+ children: i(e)
194
270
  }, e)) })]
195
271
  })]
196
272
  }),
197
- /* @__PURE__ */ E("div", {
273
+ /* @__PURE__ */ O("div", {
198
274
  className: "grid gap-1.5",
199
- children: [/* @__PURE__ */ T(A, {
275
+ children: [/* @__PURE__ */ D(j, {
200
276
  htmlFor: "edit-topic-tradition",
201
277
  children: "Tradition"
202
- }), /* @__PURE__ */ T(k, {
278
+ }), /* @__PURE__ */ D(A, {
203
279
  id: "edit-topic-tradition",
204
280
  value: s,
205
281
  onChange: (e) => c(e.target.value)
206
282
  })]
207
283
  }),
208
- /* @__PURE__ */ E("div", {
284
+ /* @__PURE__ */ O("div", {
209
285
  className: "grid gap-1.5",
210
- children: [/* @__PURE__ */ T(A, {
286
+ children: [/* @__PURE__ */ D(j, {
211
287
  htmlFor: "edit-topic-desc",
212
288
  children: "Description"
213
- }), /* @__PURE__ */ T(H, {
289
+ }), /* @__PURE__ */ D(U, {
214
290
  id: "edit-topic-desc",
215
291
  value: l,
216
292
  onChange: (e) => u(e.target.value),
217
293
  rows: 3
218
294
  })]
219
295
  }),
220
- v && /* @__PURE__ */ T("p", {
296
+ v && /* @__PURE__ */ D("p", {
221
297
  className: "text-sm text-status-error-text",
222
298
  children: v
223
299
  })
224
300
  ]
225
301
  }),
226
- /* @__PURE__ */ E(P, { children: [/* @__PURE__ */ T(D, {
302
+ /* @__PURE__ */ O(F, { children: [/* @__PURE__ */ D(k, {
227
303
  variant: "outline",
228
304
  onClick: () => t(!1),
229
305
  children: "Cancel"
230
- }), /* @__PURE__ */ T(D, {
306
+ }), /* @__PURE__ */ D(k, {
231
307
  onClick: async () => {
232
- if (!i.trim()) {
308
+ if (!a.trim()) {
233
309
  y("A topic name is required.");
234
310
  return;
235
311
  }
@@ -237,12 +313,12 @@ function G({ open: e, onOpenChange: t, topic: n }) {
237
313
  await r.mutateAsync({
238
314
  id: n.id,
239
315
  input: {
240
- name: i.trim(),
316
+ name: a.trim(),
241
317
  tradition: s.trim() || null,
242
318
  description: l.trim() || null,
243
319
  category: m
244
320
  }
245
- }), p.success("Topic updated", i.trim()), t(!1);
321
+ }), p.success("Topic updated", a.trim()), t(!1);
246
322
  } catch (e) {
247
323
  y(f(e));
248
324
  }
@@ -255,6 +331,6 @@ function G({ open: e, onOpenChange: t, topic: n }) {
255
331
  });
256
332
  }
257
333
  //#endregion
258
- export { W as TopicDetailPage };
334
+ export { X as TopicDetailPage };
259
335
 
260
336
  //# sourceMappingURL=TopicDetailPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TopicDetailPage.js","names":[],"sources":["../../../../src/theculturepost/pages/topics/TopicDetailPage.tsx"],"sourcesContent":["/**\n * TopicDetailPage — a single topic and the stories curated under it.\n * @module theculturepost/pages/topics\n *\n * Reads `tcpTopic` (with its stories). Writes `tcpUpdateTopic` (edit + feature\n * toggle) and `tcpArchiveTopic`. Stories are drawn from the topic's own\n * `stories` field.\n */\n\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Compass, Sparkles, Pencil, Archive, Plus, BookOpen } from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { StoryCard } from '../../components/StoryCard';\nimport { CreateStoryDialog } from '../../components/CreateStoryDialog';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { useTcpTopic, useTcpUpdateTopic, useTcpArchiveTopic } from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport { formatEnumLabel } from '../../utils/formatters';\nimport { notify, describeError } from '../../lib/toast';\nimport type { TcpTopic, TcpTopicCategory } from '../../types';\n\nconst CATEGORIES: TcpTopicCategory[] = [\n 'CULTURE_AND_IDENTITY',\n 'RELIGION_AND_FAITH',\n 'HISTORY',\n 'HERITAGE_AND_ARCHIVES',\n 'FESTIVALS_AND_RITUALS',\n 'MIGRATION_AND_DIASPORA',\n 'ORAL_HISTORIES',\n 'PHILOSOPHY_AND_THOUGHT',\n 'VALUES_AND_ETHICS',\n];\n\nexport function TopicDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useTheCulturePost();\n const { data: topic, isLoading, isError, refetch } = useTcpTopic(id);\n const updateTopic = useTcpUpdateTopic();\n const archiveTopic = useTcpArchiveTopic();\n\n const [editOpen, setEditOpen] = useState(false);\n const [createStoryOpen, setCreateStoryOpen] = useState(false);\n\n const backButton = (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('topics')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Back\n </Button>\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"Topic\" actions={backButton}>\n <LoadingView label=\"Loading topic…\" />\n </PageLayout>\n );\n }\n\n if (isError || !topic) {\n return (\n <PageLayout title=\"Topic\" actions={backButton}>\n <ErrorView\n title=\"Topic not found\"\n message=\"This topic may have been archived, or you may not have access.\"\n onRetry={() => refetch()}\n />\n </PageLayout>\n );\n }\n\n const stories = topic.stories ?? [];\n\n const toggleFeatured = async () => {\n try {\n await updateTopic.mutateAsync({ id: topic.id, input: { isFeatured: !topic.isFeatured } });\n notify.success(topic.isFeatured ? 'Removed from featured' : 'Topic featured');\n } catch (err) {\n notify.error('Could not update topic', describeError(err));\n }\n };\n\n const doArchive = async () => {\n try {\n await archiveTopic.mutateAsync(topic.id);\n notify.success('Topic archived');\n navigateTo('topics');\n } catch (err) {\n notify.error('Could not archive topic', describeError(err));\n }\n };\n\n return (\n <PageLayout\n title={topic.name}\n actions={\n <div className=\"flex items-center gap-2\">\n {backButton}\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={toggleFeatured}\n disabled={updateTopic.isPending}\n >\n <Sparkles className=\"mr-1.5 h-4 w-4\" />\n {topic.isFeatured ? 'Unfeature' : 'Feature'}\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditOpen(true)}>\n <Pencil className=\"mr-1.5 h-4 w-4\" /> Edit\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={doArchive}\n disabled={archiveTopic.isPending}\n className=\"text-status-error-text\"\n >\n <Archive className=\"mr-1.5 h-4 w-4\" /> Archive\n </Button>\n </div>\n }\n >\n <div className=\"mx-auto flex max-w-5xl flex-col gap-6\">\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-4 p-6\">\n <div className=\"flex items-start gap-4\">\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary\">\n <Compass className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={topic.category} />\n {topic.isFeatured && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text\">\n <Sparkles className=\"h-3 w-3\" /> Featured\n </span>\n )}\n </div>\n {topic.tradition && (\n <p className=\"mt-1.5 text-sm text-text-secondary\">{topic.tradition}</p>\n )}\n </div>\n </div>\n {topic.description && (\n <p className=\"whitespace-pre-wrap text-sm leading-relaxed text-text-secondary\">\n {topic.description}\n </p>\n )}\n </GlassCard>\n\n <Section\n title=\"Stories in this topic\"\n action={\n <Button size=\"sm\" variant=\"outline\" onClick={() => setCreateStoryOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New story\n </Button>\n }\n >\n {stories.length === 0 ? (\n <EmptyState\n icon={<BookOpen className=\"h-9 w-9\" />}\n title=\"No stories yet\"\n description=\"Draft the first story under this topic.\"\n action={\n <Button onClick={() => setCreateStoryOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New story\n </Button>\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 )}\n </Section>\n </div>\n\n <CreateStoryDialog\n open={createStoryOpen}\n onOpenChange={setCreateStoryOpen}\n topicId={topic.id}\n onCreated={(story) => navigateTo(`stories/${story.id}`)}\n />\n <EditTopicDialog open={editOpen} onOpenChange={setEditOpen} topic={topic} />\n </PageLayout>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Edit topic dialog (local — only used here)\n// ---------------------------------------------------------------------------\n\nfunction EditTopicDialog({\n open,\n onOpenChange,\n topic,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n topic: TcpTopic;\n}) {\n const updateTopic = useTcpUpdateTopic();\n const [name, setName] = useState(topic.name);\n const [tradition, setTradition] = useState(topic.tradition ?? '');\n const [description, setDescription] = useState(topic.description ?? '');\n const [category, setCategory] = useState<TcpTopicCategory>(topic.category);\n const [error, setError] = useState<string | null>(null);\n\n const topicKey = useMemo(() => `${topic.id}:${topic.updatedAt}`, [topic.id, topic.updatedAt]);\n const [seededKey, setSeededKey] = useState(topicKey);\n if (seededKey !== topicKey) {\n setSeededKey(topicKey);\n setName(topic.name);\n setTradition(topic.tradition ?? '');\n setDescription(topic.description ?? '');\n setCategory(topic.category);\n setError(null);\n }\n\n const submit = async () => {\n if (!name.trim()) {\n setError('A topic name is required.');\n return;\n }\n try {\n await updateTopic.mutateAsync({\n id: topic.id,\n input: {\n name: name.trim(),\n tradition: tradition.trim() || null,\n description: description.trim() || null,\n category,\n },\n });\n notify.success('Topic updated', name.trim());\n onOpenChange(false);\n } catch (err) {\n setError(describeError(err));\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit topic</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n The URL slug is fixed once created.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"flex flex-col gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-topic-name\">Name</Label>\n <Input id=\"edit-topic-name\" value={name} onChange={(e) => setName(e.target.value)} />\n </div>\n <div className=\"grid gap-1.5\">\n <Label>Category</Label>\n <Select value={category} onValueChange={(v) => setCategory(v as TcpTopicCategory)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CATEGORIES.map((c) => (\n <SelectItem key={c} value={c}>\n {formatEnumLabel(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-topic-tradition\">Tradition</Label>\n <Input\n id=\"edit-topic-tradition\"\n value={tradition}\n onChange={(e) => setTradition(e.target.value)}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-topic-desc\">Description</Label>\n <Textarea\n id=\"edit-topic-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n />\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={updateTopic.isPending}>\n Save changes\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TopicDetailPage.js","names":[],"sources":["../../../../src/theculturepost/pages/topics/TopicDetailPage.tsx"],"sourcesContent":["/**\n * TopicDetailPage — a single topic and the stories curated under it.\n * @module theculturepost/pages/topics\n *\n * An editorial {@link PageHero} banner carries the title, description and the\n * Featured / category / tradition badges, plus the feature-toggle, edit and\n * archive actions (`tcpUpdateTopic` / `tcpArchiveTopic`). Below, the topic's own\n * `stories` are shown in a richer {@link StoryCard} grid with a real count and a\n * client-side sort. Reads `tcpTopic` (with its stories) — no fabricated fields.\n */\n\nimport { createElement, useMemo, useState, type ReactNode } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Globe, Sparkles, Pencil, Archive, Plus, BookOpen } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageHero,\n StatusBadge,\n StoryCard,\n CreateStoryDialog,\n EmptyState,\n LoadingView,\n ErrorView,\n categoryIcon,\n} from '../../components';\nimport { useTcpTopic, useTcpUpdateTopic, useTcpArchiveTopic } from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport { formatEnumLabel } from '../../utils/formatters';\nimport { notify, describeError } from '../../lib/toast';\nimport type { TcpStory, TcpTopic, TcpTopicCategory } from '../../types';\n\nconst CATEGORIES: TcpTopicCategory[] = [\n 'CULTURE_AND_IDENTITY',\n 'RELIGION_AND_FAITH',\n 'HISTORY',\n 'HERITAGE_AND_ARCHIVES',\n 'FESTIVALS_AND_RITUALS',\n 'MIGRATION_AND_DIASPORA',\n 'ORAL_HISTORIES',\n 'PHILOSOPHY_AND_THOUGHT',\n 'VALUES_AND_ETHICS',\n];\n\ntype SortKey = 'newest' | 'oldest' | 'updated' | 'title';\n\nconst SORT_OPTIONS: { value: SortKey; label: string }[] = [\n { value: 'newest', label: 'Newest' },\n { value: 'oldest', label: 'Oldest' },\n { value: 'updated', label: 'Recently updated' },\n { value: 'title', label: 'Title (A–Z)' },\n];\n\n/** ms timestamp of a story's effective publish date (published, else created). */\nconst publishedTs = (story: TcpStory): number => {\n const t = new Date(story.publishedAt ?? story.createdAt).getTime();\n return Number.isNaN(t) ? 0 : t;\n};\n\nconst updatedTs = (story: TcpStory): number => {\n const t = new Date(story.updatedAt).getTime();\n return Number.isNaN(t) ? 0 : t;\n};\n\nfunction sortStories(stories: TcpStory[], key: SortKey): TcpStory[] {\n const copy = [...stories];\n switch (key) {\n case 'newest':\n return copy.sort((a, b) => publishedTs(b) - publishedTs(a));\n case 'oldest':\n return copy.sort((a, b) => publishedTs(a) - publishedTs(b));\n case 'updated':\n return copy.sort((a, b) => updatedTs(b) - updatedTs(a));\n case 'title':\n return copy.sort((a, b) => a.title.localeCompare(b.title));\n default:\n return copy;\n }\n}\n\n/** Minimal scroll shell so hero/loading/error surfaces share one container. */\nfunction DetailShell({ children }: { children: ReactNode }) {\n return (\n <div className=\"h-full overflow-y-auto\">\n <div className=\"mx-auto flex max-w-5xl flex-col gap-6 p-4 sm:p-6\">{children}</div>\n </div>\n );\n}\n\nexport function TopicDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useTheCulturePost();\n const { data: topic, isLoading, isError, refetch } = useTcpTopic(id);\n const updateTopic = useTcpUpdateTopic();\n const archiveTopic = useTcpArchiveTopic();\n\n const [editOpen, setEditOpen] = useState(false);\n const [createStoryOpen, setCreateStoryOpen] = useState(false);\n const [sort, setSort] = useState<SortKey>('newest');\n\n const stories = useMemo(() => topic?.stories ?? [], [topic]);\n const sortedStories = useMemo(() => sortStories(stories, sort), [stories, sort]);\n\n const backToTopics = () => navigateTo('topics');\n\n if (isLoading) {\n return (\n <DetailShell>\n <button\n type=\"button\"\n onClick={backToTopics}\n className=\"inline-flex w-fit items-center gap-1.5 text-sm font-medium text-text-secondary hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" aria-hidden=\"true\" /> Topics\n </button>\n <LoadingView label=\"Loading topic…\" />\n </DetailShell>\n );\n }\n\n if (isError || !topic) {\n return (\n <DetailShell>\n <button\n type=\"button\"\n onClick={backToTopics}\n className=\"inline-flex w-fit items-center gap-1.5 text-sm font-medium text-text-secondary hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" aria-hidden=\"true\" /> Topics\n </button>\n <ErrorView\n title=\"Topic not found\"\n message=\"This topic may have been archived, or you may not have access.\"\n onRetry={() => refetch()}\n />\n </DetailShell>\n );\n }\n\n const toggleFeatured = async () => {\n try {\n await updateTopic.mutateAsync({ id: topic.id, input: { isFeatured: !topic.isFeatured } });\n notify.success(topic.isFeatured ? 'Removed from featured' : 'Topic featured');\n } catch (err) {\n notify.error('Could not update topic', describeError(err));\n }\n };\n\n const doArchive = async () => {\n try {\n await archiveTopic.mutateAsync(topic.id);\n notify.success('Topic archived');\n navigateTo('topics');\n } catch (err) {\n notify.error('Could not archive topic', describeError(err));\n }\n };\n\n const heroMeta = (\n <>\n {topic.isFeatured && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2.5 py-0.5 text-xs font-medium text-status-warning-text\">\n <Sparkles className=\"h-3 w-3\" aria-hidden=\"true\" /> Featured\n </span>\n )}\n <StatusBadge\n status={topic.category}\n icon={createElement(categoryIcon(topic.category), { className: 'h-3 w-3' })}\n />\n {topic.tradition && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n <Globe className=\"h-3 w-3\" aria-hidden=\"true\" /> {topic.tradition}\n </span>\n )}\n </>\n );\n\n const heroActions = (\n <>\n <Button variant=\"outline\" size=\"sm\" onClick={toggleFeatured} disabled={updateTopic.isPending}>\n <Sparkles className=\"mr-1.5 h-4 w-4\" />\n {topic.isFeatured ? 'Unfeature' : 'Feature'}\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditOpen(true)}>\n <Pencil className=\"mr-1.5 h-4 w-4\" /> Edit\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={doArchive}\n disabled={archiveTopic.isPending}\n className=\"text-status-error-text\"\n >\n <Archive className=\"mr-1.5 h-4 w-4\" /> Archive\n </Button>\n <Button size=\"sm\" onClick={() => setCreateStoryOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New story\n </Button>\n </>\n );\n\n return (\n <DetailShell>\n <PageHero\n eyebrow=\"Topic\"\n title={topic.name}\n description={topic.description ?? undefined}\n backAction={{ label: 'Topics', onClick: backToTopics }}\n meta={heroMeta}\n actions={heroActions}\n />\n\n <section aria-labelledby=\"topic-stories-heading\">\n <div className=\"mb-4 flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <h2\n id=\"topic-stories-heading\"\n className=\"font-display text-xl font-semibold text-text-primary\"\n >\n Stories in this topic\n </h2>\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-sm font-medium tabular-nums text-text-secondary\">\n {stories.length}\n </span>\n </div>\n {stories.length > 1 && (\n <div className=\"flex items-center gap-2\">\n <Label htmlFor=\"topic-story-sort\" className=\"text-sm text-text-secondary\">\n Sort by\n </Label>\n <Select value={sort} onValueChange={(v) => setSort(v as SortKey)}>\n <SelectTrigger\n id=\"topic-story-sort\"\n className=\"w-[12rem]\"\n aria-label=\"Sort stories\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SORT_OPTIONS.map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n {opt.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n </div>\n\n {stories.length === 0 ? (\n <EmptyState\n icon={<BookOpen className=\"h-9 w-9\" />}\n title=\"No stories yet\"\n description=\"Draft the first story under this topic.\"\n action={\n <Button onClick={() => setCreateStoryOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New story\n </Button>\n }\n />\n ) : (\n <div className=\"grid gap-5 sm:grid-cols-2 xl:grid-cols-3\">\n {sortedStories.map((story) => (\n <StoryCard\n key={story.id}\n story={story}\n onOpen={() => navigateTo(`stories/${story.id}`)}\n />\n ))}\n </div>\n )}\n </section>\n\n <CreateStoryDialog\n open={createStoryOpen}\n onOpenChange={setCreateStoryOpen}\n topicId={topic.id}\n onCreated={(story) => navigateTo(`stories/${story.id}`)}\n />\n <EditTopicDialog open={editOpen} onOpenChange={setEditOpen} topic={topic} />\n </DetailShell>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Edit topic dialog (local — only used here)\n// ---------------------------------------------------------------------------\n\nfunction EditTopicDialog({\n open,\n onOpenChange,\n topic,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n topic: TcpTopic;\n}) {\n const updateTopic = useTcpUpdateTopic();\n const [name, setName] = useState(topic.name);\n const [tradition, setTradition] = useState(topic.tradition ?? '');\n const [description, setDescription] = useState(topic.description ?? '');\n const [category, setCategory] = useState<TcpTopicCategory>(topic.category);\n const [error, setError] = useState<string | null>(null);\n\n const topicKey = useMemo(() => `${topic.id}:${topic.updatedAt}`, [topic.id, topic.updatedAt]);\n const [seededKey, setSeededKey] = useState(topicKey);\n if (seededKey !== topicKey) {\n setSeededKey(topicKey);\n setName(topic.name);\n setTradition(topic.tradition ?? '');\n setDescription(topic.description ?? '');\n setCategory(topic.category);\n setError(null);\n }\n\n const submit = async () => {\n if (!name.trim()) {\n setError('A topic name is required.');\n return;\n }\n try {\n await updateTopic.mutateAsync({\n id: topic.id,\n input: {\n name: name.trim(),\n tradition: tradition.trim() || null,\n description: description.trim() || null,\n category,\n },\n });\n notify.success('Topic updated', name.trim());\n onOpenChange(false);\n } catch (err) {\n setError(describeError(err));\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit topic</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n The URL slug is fixed once created.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"flex flex-col gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-topic-name\">Name</Label>\n <Input id=\"edit-topic-name\" value={name} onChange={(e) => setName(e.target.value)} />\n </div>\n <div className=\"grid gap-1.5\">\n <Label>Category</Label>\n <Select value={category} onValueChange={(v) => setCategory(v as TcpTopicCategory)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CATEGORIES.map((c) => (\n <SelectItem key={c} value={c}>\n {formatEnumLabel(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-topic-tradition\">Tradition</Label>\n <Input\n id=\"edit-topic-tradition\"\n value={tradition}\n onChange={(e) => setTradition(e.target.value)}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-topic-desc\">Description</Label>\n <Textarea\n id=\"edit-topic-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n />\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={updateTopic.isPending}>\n Save changes\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}