@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.
- package/dist/theculturepost/TheCulturePostRoot.js +63 -45
- package/dist/theculturepost/TheCulturePostRoot.js.map +1 -1
- package/dist/theculturepost/TheCulturePostRoutes.js +17 -9
- package/dist/theculturepost/TheCulturePostRoutes.js.map +1 -1
- package/dist/theculturepost/components/AddCitationDialog.js +131 -102
- package/dist/theculturepost/components/AddCitationDialog.js.map +1 -1
- package/dist/theculturepost/components/ArchiveItemCard.js +56 -43
- package/dist/theculturepost/components/ArchiveItemCard.js.map +1 -1
- package/dist/theculturepost/components/AudioPlayer.js +74 -0
- package/dist/theculturepost/components/AudioPlayer.js.map +1 -0
- package/dist/theculturepost/components/CitationList.js +215 -105
- package/dist/theculturepost/components/CitationList.js.map +1 -1
- package/dist/theculturepost/components/CollectionCard.js +32 -32
- package/dist/theculturepost/components/CollectionCard.js.map +1 -1
- package/dist/theculturepost/components/CreateArchiveItemDialog.js +133 -93
- package/dist/theculturepost/components/CreateArchiveItemDialog.js.map +1 -1
- package/dist/theculturepost/components/CreateCollectionDialog.js +91 -70
- package/dist/theculturepost/components/CreateCollectionDialog.js.map +1 -1
- package/dist/theculturepost/components/CreateStoryDialog.js +311 -132
- package/dist/theculturepost/components/CreateStoryDialog.js.map +1 -1
- package/dist/theculturepost/components/DecorativeCover.js +78 -0
- package/dist/theculturepost/components/DecorativeCover.js.map +1 -0
- package/dist/theculturepost/components/DecorativeDivider.js +45 -0
- package/dist/theculturepost/components/DecorativeDivider.js.map +1 -0
- package/dist/theculturepost/components/DiscussionCard.js +75 -60
- package/dist/theculturepost/components/DiscussionCard.js.map +1 -1
- package/dist/theculturepost/components/HistoryTimeline.js +115 -0
- package/dist/theculturepost/components/HistoryTimeline.js.map +1 -0
- package/dist/theculturepost/components/LifecycleStepper.js +33 -0
- package/dist/theculturepost/components/LifecycleStepper.js.map +1 -0
- package/dist/theculturepost/components/OpenDiscussionDialog.js +66 -56
- package/dist/theculturepost/components/OpenDiscussionDialog.js.map +1 -1
- package/dist/theculturepost/components/OralHistoryCard.js +6 -73
- package/dist/theculturepost/components/OverlayBadge.js +22 -0
- package/dist/theculturepost/components/OverlayBadge.js.map +1 -0
- package/dist/theculturepost/components/PageHero.js +62 -0
- package/dist/theculturepost/components/PageHero.js.map +1 -0
- package/dist/theculturepost/components/RecordOralHistoryDialog.js +306 -131
- package/dist/theculturepost/components/RecordOralHistoryDialog.js.map +1 -1
- package/dist/theculturepost/components/ReplyThread.js +268 -139
- package/dist/theculturepost/components/ReplyThread.js.map +1 -1
- package/dist/theculturepost/components/StatusBadge.js +9 -9
- package/dist/theculturepost/components/StatusBadge.js.map +1 -1
- package/dist/theculturepost/components/StoryCard.js +54 -49
- package/dist/theculturepost/components/StoryCard.js.map +1 -1
- package/dist/theculturepost/components/TopicCard.js +60 -41
- package/dist/theculturepost/components/TopicCard.js.map +1 -1
- package/dist/theculturepost/components/Waveform.js +31 -0
- package/dist/theculturepost/components/Waveform.js.map +1 -0
- package/dist/theculturepost/components/categoryMeta.js +20 -0
- package/dist/theculturepost/components/categoryMeta.js.map +1 -0
- package/dist/theculturepost/components/index.js +41 -0
- package/dist/theculturepost/components/viz/BarChart.js +53 -0
- package/dist/theculturepost/components/viz/BarChart.js.map +1 -0
- package/dist/theculturepost/components/viz/Donut.js +58 -0
- package/dist/theculturepost/components/viz/Donut.js.map +1 -0
- package/dist/theculturepost/components/viz/StatTile.js +35 -0
- package/dist/theculturepost/components/viz/StatTile.js.map +1 -0
- package/dist/theculturepost/components/viz/index.js +3 -0
- package/dist/theculturepost/hooks/useFiles.js +1 -1
- package/dist/theculturepost/hooks/useFiles.js.map +1 -1
- package/dist/theculturepost/pages/HomePage.js +247 -124
- package/dist/theculturepost/pages/HomePage.js.map +1 -1
- package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js +319 -127
- package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js.map +1 -1
- package/dist/theculturepost/pages/archive/ArchivePage.js +265 -180
- package/dist/theculturepost/pages/archive/ArchivePage.js.map +1 -1
- package/dist/theculturepost/pages/archive/CollectionDetailPage.js +158 -143
- package/dist/theculturepost/pages/archive/CollectionDetailPage.js.map +1 -1
- package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js +155 -93
- package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js.map +1 -1
- package/dist/theculturepost/pages/discussions/DiscussionsPage.js +16 -16
- package/dist/theculturepost/pages/discussions/DiscussionsPage.js.map +1 -1
- package/dist/theculturepost/pages/governance/GovernancePage.js +293 -0
- package/dist/theculturepost/pages/governance/GovernancePage.js.map +1 -0
- package/dist/theculturepost/pages/stories/StoriesPage.js +18 -18
- package/dist/theculturepost/pages/stories/StoriesPage.js.map +1 -1
- package/dist/theculturepost/pages/stories/StoryDetailPage.js +603 -285
- package/dist/theculturepost/pages/stories/StoryDetailPage.js.map +1 -1
- package/dist/theculturepost/pages/timeline/TimelinePage.js +162 -0
- package/dist/theculturepost/pages/timeline/TimelinePage.js.map +1 -0
- package/dist/theculturepost/pages/topics/TopicDetailPage.js +239 -163
- package/dist/theculturepost/pages/topics/TopicDetailPage.js.map +1 -1
- package/dist/theculturepost/pages/topics/TopicsPage.js +76 -74
- package/dist/theculturepost/pages/topics/TopicsPage.js.map +1 -1
- package/dist/theculturepost/styles/injectDisplayFont.js +11 -0
- package/dist/theculturepost/styles/injectDisplayFont.js.map +1 -0
- package/dist/theculturepost/utils/formatters.js +6 -1
- package/dist/theculturepost/utils/formatters.js.map +1 -1
- package/package.json +1 -1
- 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 {
|
|
6
|
-
import {
|
|
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
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
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
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
81
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
|
164
|
-
let r = d(), [
|
|
165
|
-
return x !== b && (S(b), o(n.name), c(n.tradition ?? ""), u(n.description ?? ""),
|
|
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__ */
|
|
244
|
+
children: /* @__PURE__ */ O(N, {
|
|
169
245
|
className: "sm:max-w-lg",
|
|
170
246
|
children: [
|
|
171
|
-
/* @__PURE__ */
|
|
172
|
-
/* @__PURE__ */
|
|
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__ */
|
|
251
|
+
/* @__PURE__ */ O("div", {
|
|
176
252
|
className: "grid gap-1.5",
|
|
177
|
-
children: [/* @__PURE__ */
|
|
253
|
+
children: [/* @__PURE__ */ D(j, {
|
|
178
254
|
htmlFor: "edit-topic-name",
|
|
179
255
|
children: "Name"
|
|
180
|
-
}), /* @__PURE__ */
|
|
256
|
+
}), /* @__PURE__ */ D(A, {
|
|
181
257
|
id: "edit-topic-name",
|
|
182
|
-
value:
|
|
258
|
+
value: a,
|
|
183
259
|
onChange: (e) => o(e.target.value)
|
|
184
260
|
})]
|
|
185
261
|
}),
|
|
186
|
-
/* @__PURE__ */
|
|
262
|
+
/* @__PURE__ */ O("div", {
|
|
187
263
|
className: "grid gap-1.5",
|
|
188
|
-
children: [/* @__PURE__ */
|
|
264
|
+
children: [/* @__PURE__ */ D(j, { children: "Category" }), /* @__PURE__ */ O(R, {
|
|
189
265
|
value: m,
|
|
190
|
-
onValueChange: (e) =>
|
|
191
|
-
children: [/* @__PURE__ */
|
|
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:
|
|
269
|
+
children: i(e)
|
|
194
270
|
}, e)) })]
|
|
195
271
|
})]
|
|
196
272
|
}),
|
|
197
|
-
/* @__PURE__ */
|
|
273
|
+
/* @__PURE__ */ O("div", {
|
|
198
274
|
className: "grid gap-1.5",
|
|
199
|
-
children: [/* @__PURE__ */
|
|
275
|
+
children: [/* @__PURE__ */ D(j, {
|
|
200
276
|
htmlFor: "edit-topic-tradition",
|
|
201
277
|
children: "Tradition"
|
|
202
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
284
|
+
/* @__PURE__ */ O("div", {
|
|
209
285
|
className: "grid gap-1.5",
|
|
210
|
-
children: [/* @__PURE__ */
|
|
286
|
+
children: [/* @__PURE__ */ D(j, {
|
|
211
287
|
htmlFor: "edit-topic-desc",
|
|
212
288
|
children: "Description"
|
|
213
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
302
|
+
/* @__PURE__ */ O(F, { children: [/* @__PURE__ */ D(k, {
|
|
227
303
|
variant: "outline",
|
|
228
304
|
onClick: () => t(!1),
|
|
229
305
|
children: "Cancel"
|
|
230
|
-
}), /* @__PURE__ */
|
|
306
|
+
}), /* @__PURE__ */ D(k, {
|
|
231
307
|
onClick: async () => {
|
|
232
|
-
if (!
|
|
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:
|
|
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",
|
|
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 {
|
|
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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"}
|