@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,129 +1,191 @@
|
|
|
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 {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
3
|
+
import { ErrorView as t, LoadingView as n } from "../../components/StateViews.js";
|
|
4
|
+
import { formatNumber as r, formatRelativeTime as i } from "../../utils/formatters.js";
|
|
5
|
+
import { StatusBadge as a } from "../../components/StatusBadge.js";
|
|
6
|
+
import { AuthorLabel as o } from "../../components/AuthorLabel.js";
|
|
7
|
+
import { useTcpDiscussion as s, useTcpLockDiscussion as c } from "../../hooks/useTcpApi.js";
|
|
9
8
|
import "../../hooks/index.js";
|
|
10
|
-
import { describeError as
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
9
|
+
import { describeError as l, notify as u } from "../../lib/toast.js";
|
|
10
|
+
import { PageLayout as d } from "../../components/PageLayout.js";
|
|
11
|
+
import { ReplyThread as f } from "../../components/ReplyThread.js";
|
|
12
|
+
import { ArrowLeft as p, BookOpen as m, CheckCircle2 as h, Clock as g, ExternalLink as _, Flag as v, HeartHandshake as y, Lock as b, MessagesSquare as x, Quote as S, Trash2 as C, Unlock as w } from "lucide-react";
|
|
13
|
+
import { useParams as T } from "react-router-dom";
|
|
14
|
+
import { Fragment as E, jsx as D, jsxs as O } from "react/jsx-runtime";
|
|
15
|
+
import { Button as k, GlassCard as A } from "@burdenoff/fe-libs/ui";
|
|
16
16
|
//#region src/theculturepost/pages/discussions/DiscussionDetailPage.tsx
|
|
17
|
-
function
|
|
18
|
-
let
|
|
17
|
+
function j(e) {
|
|
18
|
+
let t = "h-3 w-3";
|
|
19
|
+
switch (e) {
|
|
20
|
+
case "APPROVED": return /* @__PURE__ */ D(h, {
|
|
21
|
+
className: t,
|
|
22
|
+
"aria-hidden": "true"
|
|
23
|
+
});
|
|
24
|
+
case "PENDING": return /* @__PURE__ */ D(g, {
|
|
25
|
+
className: t,
|
|
26
|
+
"aria-hidden": "true"
|
|
27
|
+
});
|
|
28
|
+
case "FLAGGED": return /* @__PURE__ */ D(v, {
|
|
29
|
+
className: t,
|
|
30
|
+
"aria-hidden": "true"
|
|
31
|
+
});
|
|
32
|
+
case "REMOVED": return /* @__PURE__ */ D(C, {
|
|
33
|
+
className: t,
|
|
34
|
+
"aria-hidden": "true"
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function M() {
|
|
39
|
+
let { id: h } = T(), { navigateTo: g } = e(), { data: v, isLoading: C, isError: M, refetch: N } = s(h), P = c(), F = /* @__PURE__ */ O(k, {
|
|
19
40
|
variant: "ghost",
|
|
20
41
|
size: "sm",
|
|
21
|
-
onClick: () =>
|
|
22
|
-
children: [/* @__PURE__ */
|
|
42
|
+
onClick: () => g("discussions"),
|
|
43
|
+
children: [/* @__PURE__ */ D(p, { className: "mr-1.5 h-4 w-4" }), " Back"]
|
|
23
44
|
});
|
|
24
|
-
return
|
|
45
|
+
return C ? /* @__PURE__ */ D(d, {
|
|
25
46
|
title: "Discussion",
|
|
26
|
-
actions:
|
|
27
|
-
children: /* @__PURE__ */
|
|
28
|
-
}) :
|
|
47
|
+
actions: F,
|
|
48
|
+
children: /* @__PURE__ */ D(n, { label: "Loading discussion…" })
|
|
49
|
+
}) : M || !v ? /* @__PURE__ */ D(d, {
|
|
29
50
|
title: "Discussion",
|
|
30
|
-
actions:
|
|
31
|
-
children: /* @__PURE__ */
|
|
51
|
+
actions: F,
|
|
52
|
+
children: /* @__PURE__ */ D(t, {
|
|
32
53
|
title: "Discussion not found",
|
|
33
54
|
message: "This discussion may have been removed, or you may not have access.",
|
|
34
|
-
onRetry: () =>
|
|
55
|
+
onRetry: () => N()
|
|
35
56
|
})
|
|
36
|
-
}) : /* @__PURE__ */
|
|
37
|
-
title:
|
|
38
|
-
actions:
|
|
39
|
-
|
|
40
|
-
children: [P, /* @__PURE__ */ C(T, {
|
|
41
|
-
variant: "outline",
|
|
42
|
-
size: "sm",
|
|
43
|
-
onClick: async () => {
|
|
44
|
-
try {
|
|
45
|
-
await N.mutateAsync(k.id), f.success(k.isLocked ? "Discussion unlocked" : "Discussion locked");
|
|
46
|
-
} catch (e) {
|
|
47
|
-
f.error("Could not update the discussion", d(e));
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
disabled: N.isPending,
|
|
51
|
-
children: k.isLocked ? /* @__PURE__ */ w(S, { children: [/* @__PURE__ */ C(b, { className: "mr-1.5 h-4 w-4" }), " Unlock"] }) : /* @__PURE__ */ w(S, { children: [/* @__PURE__ */ C(_, { className: "mr-1.5 h-4 w-4" }), " Lock"] })
|
|
52
|
-
})]
|
|
53
|
-
}),
|
|
54
|
-
children: /* @__PURE__ */ w("div", {
|
|
57
|
+
}) : /* @__PURE__ */ D(d, {
|
|
58
|
+
title: "Discussion",
|
|
59
|
+
actions: F,
|
|
60
|
+
children: /* @__PURE__ */ O("div", {
|
|
55
61
|
className: "mx-auto flex max-w-3xl flex-col gap-6",
|
|
56
|
-
children: [/* @__PURE__ */
|
|
62
|
+
children: [/* @__PURE__ */ O(A, {
|
|
57
63
|
treatment: "plain",
|
|
58
64
|
className: "flex flex-col gap-4 p-5 sm:p-6",
|
|
59
65
|
children: [
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
-
className: "flex
|
|
62
|
-
children: [
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
/* @__PURE__ */ O("div", {
|
|
67
|
+
className: "flex items-start justify-between gap-3",
|
|
68
|
+
children: [/* @__PURE__ */ D("h1", {
|
|
69
|
+
className: "font-display text-2xl font-semibold leading-tight text-text-primary sm:text-3xl",
|
|
70
|
+
children: v.title
|
|
71
|
+
}), /* @__PURE__ */ D(k, {
|
|
72
|
+
variant: "outline",
|
|
73
|
+
size: "sm",
|
|
74
|
+
onClick: async () => {
|
|
75
|
+
try {
|
|
76
|
+
await P.mutateAsync(v.id), u.success(v.isLocked ? "Discussion unlocked" : "Discussion locked");
|
|
77
|
+
} catch (e) {
|
|
78
|
+
u.error("Could not update the discussion", l(e));
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
disabled: P.isPending,
|
|
82
|
+
className: "shrink-0",
|
|
83
|
+
children: v.isLocked ? /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D(w, { className: "mr-1.5 h-4 w-4" }), " Unlock"] }) : /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D(b, { className: "mr-1.5 h-4 w-4" }), " Lock"] })
|
|
84
|
+
})]
|
|
73
85
|
}),
|
|
74
|
-
|
|
75
|
-
className: "
|
|
76
|
-
children:
|
|
86
|
+
/* @__PURE__ */ O("div", {
|
|
87
|
+
className: "flex flex-wrap items-center gap-x-4 gap-y-2",
|
|
88
|
+
children: [/* @__PURE__ */ O("div", {
|
|
89
|
+
className: "flex flex-wrap items-center gap-1.5",
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ D(a, {
|
|
92
|
+
status: v.status,
|
|
93
|
+
icon: j(v.status)
|
|
94
|
+
}),
|
|
95
|
+
v.isInterfaith && /* @__PURE__ */ O("span", {
|
|
96
|
+
className: "inline-flex items-center gap-1 rounded-full bg-[var(--color-accent-soft)] px-2.5 py-0.5 text-xs font-medium text-text-primary",
|
|
97
|
+
children: [/* @__PURE__ */ D(y, {
|
|
98
|
+
className: "h-3 w-3",
|
|
99
|
+
"aria-hidden": "true"
|
|
100
|
+
}), " Interfaith"]
|
|
101
|
+
}),
|
|
102
|
+
v.isLocked && /* @__PURE__ */ O("span", {
|
|
103
|
+
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",
|
|
104
|
+
children: [/* @__PURE__ */ D(b, {
|
|
105
|
+
className: "h-3 w-3",
|
|
106
|
+
"aria-hidden": "true"
|
|
107
|
+
}), " Locked"]
|
|
108
|
+
})
|
|
109
|
+
]
|
|
110
|
+
}), v.tags.length > 0 && /* @__PURE__ */ D("div", {
|
|
111
|
+
className: "flex flex-wrap items-center gap-1.5 sm:ml-auto",
|
|
112
|
+
children: v.tags.map((e) => /* @__PURE__ */ O("span", {
|
|
113
|
+
className: "rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary",
|
|
114
|
+
children: ["#", e]
|
|
115
|
+
}, e))
|
|
116
|
+
})]
|
|
77
117
|
}),
|
|
78
|
-
|
|
79
|
-
className: "flex flex-wrap items-center gap-1
|
|
80
|
-
children: [/* @__PURE__ */ C(y, {
|
|
81
|
-
className: "h-3.5 w-3.5 text-text-tertiary",
|
|
82
|
-
"aria-hidden": "true"
|
|
83
|
-
}), k.tags.map((e) => /* @__PURE__ */ w("span", {
|
|
84
|
-
className: "rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary",
|
|
85
|
-
children: ["#", e]
|
|
86
|
-
}, e))]
|
|
87
|
-
}),
|
|
88
|
-
/* @__PURE__ */ w("div", {
|
|
89
|
-
className: "flex flex-wrap items-center gap-x-4 gap-y-1 border-t border-border-seam pt-3 text-xs text-text-tertiary",
|
|
118
|
+
/* @__PURE__ */ O("div", {
|
|
119
|
+
className: "flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-text-tertiary",
|
|
90
120
|
children: [
|
|
91
|
-
/* @__PURE__ */
|
|
92
|
-
actorId:
|
|
121
|
+
/* @__PURE__ */ D(o, {
|
|
122
|
+
actorId: v.openedById,
|
|
93
123
|
prefix: "Opened by"
|
|
94
124
|
}),
|
|
95
|
-
/* @__PURE__ */
|
|
125
|
+
/* @__PURE__ */ D("span", {
|
|
126
|
+
"aria-hidden": "true",
|
|
127
|
+
children: "·"
|
|
128
|
+
}),
|
|
129
|
+
/* @__PURE__ */ O("span", {
|
|
96
130
|
className: "inline-flex items-center gap-1",
|
|
97
131
|
children: [
|
|
98
|
-
/* @__PURE__ */
|
|
99
|
-
|
|
100
|
-
|
|
132
|
+
/* @__PURE__ */ D(x, {
|
|
133
|
+
className: "h-3.5 w-3.5",
|
|
134
|
+
"aria-hidden": "true"
|
|
135
|
+
}),
|
|
136
|
+
r(v.replyCount),
|
|
101
137
|
" ",
|
|
102
|
-
"replies"
|
|
138
|
+
v.replyCount === 1 ? "reply" : "replies"
|
|
103
139
|
]
|
|
104
140
|
}),
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
children: [/* @__PURE__ */ C(h, { className: "h-3.5 w-3.5" }), " View linked story"]
|
|
111
|
-
})
|
|
141
|
+
/* @__PURE__ */ D("span", {
|
|
142
|
+
"aria-hidden": "true",
|
|
143
|
+
children: "·"
|
|
144
|
+
}),
|
|
145
|
+
/* @__PURE__ */ O("span", { children: ["Started ", i(v.createdAt)] })
|
|
112
146
|
]
|
|
147
|
+
}),
|
|
148
|
+
(v.prompt || v.storyId) && /* @__PURE__ */ O("div", {
|
|
149
|
+
className: "flex flex-col gap-3 rounded-xl border border-border-seam bg-[var(--color-accent-soft)] p-4 sm:flex-row sm:items-start sm:justify-between",
|
|
150
|
+
children: [v.prompt ? /* @__PURE__ */ O("div", {
|
|
151
|
+
className: "flex min-w-0 items-start gap-3",
|
|
152
|
+
children: [/* @__PURE__ */ D("span", {
|
|
153
|
+
className: "mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-canvas/70 text-accent",
|
|
154
|
+
"aria-hidden": "true",
|
|
155
|
+
children: /* @__PURE__ */ D(S, { className: "h-4 w-4" })
|
|
156
|
+
}), /* @__PURE__ */ D("p", {
|
|
157
|
+
className: "min-w-0 whitespace-pre-wrap text-sm leading-relaxed text-text-primary",
|
|
158
|
+
children: v.prompt
|
|
159
|
+
})]
|
|
160
|
+
}) : /* @__PURE__ */ D("span", {
|
|
161
|
+
className: "text-sm text-text-secondary",
|
|
162
|
+
children: "Anchored to a story."
|
|
163
|
+
}), v.storyId && /* @__PURE__ */ O("button", {
|
|
164
|
+
type: "button",
|
|
165
|
+
onClick: () => g(`stories/${v.storyId}`),
|
|
166
|
+
className: "inline-flex shrink-0 items-center gap-1.5 self-start rounded-lg px-1 text-sm font-medium text-accent hover:underline",
|
|
167
|
+
children: [
|
|
168
|
+
/* @__PURE__ */ D(m, {
|
|
169
|
+
className: "h-4 w-4",
|
|
170
|
+
"aria-hidden": "true"
|
|
171
|
+
}),
|
|
172
|
+
" View linked story",
|
|
173
|
+
/* @__PURE__ */ D(_, {
|
|
174
|
+
className: "h-3.5 w-3.5",
|
|
175
|
+
"aria-hidden": "true"
|
|
176
|
+
})
|
|
177
|
+
]
|
|
178
|
+
})]
|
|
113
179
|
})
|
|
114
180
|
]
|
|
115
|
-
}), /* @__PURE__ */
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
children: /* @__PURE__ */ C(p, {
|
|
119
|
-
discussionId: k.id,
|
|
120
|
-
isLocked: k.isLocked
|
|
121
|
-
})
|
|
181
|
+
}), /* @__PURE__ */ D(f, {
|
|
182
|
+
discussionId: v.id,
|
|
183
|
+
isLocked: v.isLocked
|
|
122
184
|
})]
|
|
123
185
|
})
|
|
124
186
|
});
|
|
125
187
|
}
|
|
126
188
|
//#endregion
|
|
127
|
-
export {
|
|
189
|
+
export { M as DiscussionDetailPage };
|
|
128
190
|
|
|
129
191
|
//# sourceMappingURL=DiscussionDetailPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DiscussionDetailPage.js","names":[],"sources":["../../../../src/theculturepost/pages/discussions/DiscussionDetailPage.tsx"],"sourcesContent":["/**\n * DiscussionDetailPage — a single discussion and its moderated replies.\n * @module theculturepost/pages/discussions\n *\n * Reads `tcpDiscussion`. Writes `tcpLockDiscussion` and, via {@link ReplyThread},\n * `tcpPostReply` / `tcpModerateReply`. When anchored to a story, a link back to\n * the story is shown.\n */\n\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Lock,\n Unlock,\n HeartHandshake,\n Tag,\n BookOpen,\n MessagesSquare,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { AuthorLabel } from '../../components/AuthorLabel';\nimport { ReplyThread } from '../../components/ReplyThread';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { useTcpDiscussion, useTcpLockDiscussion } from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport { formatNumber, formatRelativeTime } from '../../utils/formatters';\nimport { notify, describeError } from '../../lib/toast';\n\nexport function DiscussionDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useTheCulturePost();\n const { data: discussion, isLoading, isError, refetch } = useTcpDiscussion(id);\n const lock = useTcpLockDiscussion();\n\n const backButton = (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('discussions')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Back\n </Button>\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"Discussion\" actions={backButton}>\n <LoadingView label=\"Loading discussion…\" />\n </PageLayout>\n );\n }\n\n if (isError || !discussion) {\n return (\n <PageLayout title=\"Discussion\" actions={backButton}>\n <ErrorView\n title=\"Discussion not found\"\n message=\"This discussion may have been removed, or you may not have access.\"\n onRetry={() => refetch()}\n />\n </PageLayout>\n );\n }\n\n const doLock = async () => {\n try {\n await lock.mutateAsync(discussion.id);\n notify.success(discussion.isLocked ? 'Discussion unlocked' : 'Discussion locked');\n } catch (err) {\n notify.error('Could not update the discussion', describeError(err));\n }\n };\n\n return (\n <PageLayout\n title={discussion.title}\n actions={\n <div className=\"flex items-center gap-2\">\n {backButton}\n <Button variant=\"outline\" size=\"sm\" onClick={doLock} disabled={lock.isPending}>\n {discussion.isLocked ? (\n <>\n <Unlock className=\"mr-1.5 h-4 w-4\" /> Unlock\n </>\n ) : (\n <>\n <Lock className=\"mr-1.5 h-4 w-4\" /> Lock\n </>\n )}\n </Button>\n </div>\n }\n >\n <div className=\"mx-auto flex max-w-3xl flex-col gap-6\">\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-4 p-5 sm:p-6\">\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={discussion.status} />\n {discussion.isInterfaith && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs font-medium text-text-primary\">\n <HeartHandshake className=\"h-3 w-3\" /> Interfaith\n </span>\n )}\n {discussion.isLocked && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n <Lock className=\"h-3 w-3\" /> Locked\n </span>\n )}\n </div>\n\n {discussion.prompt && (\n <p className=\"whitespace-pre-wrap text-base leading-relaxed text-text-secondary\">\n {discussion.prompt}\n </p>\n )}\n\n {discussion.tags.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <Tag className=\"h-3.5 w-3.5 text-text-tertiary\" aria-hidden=\"true\" />\n {discussion.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 border-t border-border-seam pt-3 text-xs text-text-tertiary\">\n <AuthorLabel actorId={discussion.openedById} prefix=\"Opened by\" />\n <span className=\"inline-flex items-center gap-1\">\n <MessagesSquare className=\"h-3.5 w-3.5\" /> {formatNumber(discussion.replyCount)}{' '}\n replies\n </span>\n <span>Started {formatRelativeTime(discussion.createdAt)}</span>\n {discussion.storyId && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`stories/${discussion.storyId}`)}\n className=\"inline-flex items-center gap-1 font-medium text-accent hover:underline\"\n >\n <BookOpen className=\"h-3.5 w-3.5\" /> View linked story\n </button>\n )}\n </div>\n </GlassCard>\n\n <Section\n title=\"Replies\"\n description=\"Perspectives from the community — moderated with care.\"\n >\n <ReplyThread discussionId={discussion.id} isLocked={discussion.isLocked} />\n </Section>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DiscussionDetailPage.js","names":[],"sources":["../../../../src/theculturepost/pages/discussions/DiscussionDetailPage.tsx"],"sourcesContent":["/**\n * DiscussionDetailPage — a single discussion and its moderated replies.\n * @module theculturepost/pages/discussions\n *\n * Reads `tcpDiscussion`. Writes `tcpLockDiscussion` and, via {@link ReplyThread},\n * `tcpPostReply` / `tcpModerateReply`. When anchored to a story, a link back to\n * the story is shown. The header is an editorial card: display-serif title, a\n * moderation-status + interfaith badge row, tag chips, an \"opened by\" byline and\n * a framed prompt callout.\n */\n\nimport type { ReactNode } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Lock,\n Unlock,\n HeartHandshake,\n BookOpen,\n MessagesSquare,\n Quote,\n CheckCircle2,\n Clock,\n Flag,\n Trash2,\n ExternalLink,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { AuthorLabel } from '../../components/AuthorLabel';\nimport { ReplyThread } from '../../components/ReplyThread';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { useTcpDiscussion, useTcpLockDiscussion } from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport { formatNumber, formatRelativeTime } from '../../utils/formatters';\nimport { notify, describeError } from '../../lib/toast';\nimport type { TcpModerationStatus } from '../../types';\n\n/** Leading icon for a moderation-status badge (token colours come from StatusBadge). */\nfunction statusIcon(status: TcpModerationStatus): ReactNode {\n const className = 'h-3 w-3';\n switch (status) {\n case 'APPROVED':\n return <CheckCircle2 className={className} aria-hidden=\"true\" />;\n case 'PENDING':\n return <Clock className={className} aria-hidden=\"true\" />;\n case 'FLAGGED':\n return <Flag className={className} aria-hidden=\"true\" />;\n case 'REMOVED':\n return <Trash2 className={className} aria-hidden=\"true\" />;\n }\n}\n\nexport function DiscussionDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useTheCulturePost();\n const { data: discussion, isLoading, isError, refetch } = useTcpDiscussion(id);\n const lock = useTcpLockDiscussion();\n\n const backButton = (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('discussions')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Back\n </Button>\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"Discussion\" actions={backButton}>\n <LoadingView label=\"Loading discussion…\" />\n </PageLayout>\n );\n }\n\n if (isError || !discussion) {\n return (\n <PageLayout title=\"Discussion\" actions={backButton}>\n <ErrorView\n title=\"Discussion not found\"\n message=\"This discussion may have been removed, or you may not have access.\"\n onRetry={() => refetch()}\n />\n </PageLayout>\n );\n }\n\n const doLock = async () => {\n try {\n await lock.mutateAsync(discussion.id);\n notify.success(discussion.isLocked ? 'Discussion unlocked' : 'Discussion locked');\n } catch (err) {\n notify.error('Could not update the discussion', describeError(err));\n }\n };\n\n return (\n <PageLayout title=\"Discussion\" actions={backButton}>\n <div className=\"mx-auto flex max-w-3xl flex-col gap-6\">\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-4 p-5 sm:p-6\">\n {/* Title + lock/unlock */}\n <div className=\"flex items-start justify-between gap-3\">\n <h1 className=\"font-display text-2xl font-semibold leading-tight text-text-primary sm:text-3xl\">\n {discussion.title}\n </h1>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={doLock}\n disabled={lock.isPending}\n className=\"shrink-0\"\n >\n {discussion.isLocked ? (\n <>\n <Unlock className=\"mr-1.5 h-4 w-4\" /> Unlock\n </>\n ) : (\n <>\n <Lock className=\"mr-1.5 h-4 w-4\" /> Lock\n </>\n )}\n </Button>\n </div>\n\n {/* Moderation status + interfaith / locked signals, with tags to the right */}\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-2\">\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={discussion.status} icon={statusIcon(discussion.status)} />\n {discussion.isInterfaith && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-[var(--color-accent-soft)] px-2.5 py-0.5 text-xs font-medium text-text-primary\">\n <HeartHandshake className=\"h-3 w-3\" aria-hidden=\"true\" /> Interfaith\n </span>\n )}\n {discussion.isLocked && (\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 <Lock className=\"h-3 w-3\" aria-hidden=\"true\" /> Locked\n </span>\n )}\n </div>\n {discussion.tags.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5 sm:ml-auto\">\n {discussion.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n </div>\n\n {/* Byline */}\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-text-tertiary\">\n <AuthorLabel actorId={discussion.openedById} prefix=\"Opened by\" />\n <span aria-hidden=\"true\">·</span>\n <span className=\"inline-flex items-center gap-1\">\n <MessagesSquare className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {formatNumber(discussion.replyCount)}{' '}\n {discussion.replyCount === 1 ? 'reply' : 'replies'}\n </span>\n <span aria-hidden=\"true\">·</span>\n <span>Started {formatRelativeTime(discussion.createdAt)}</span>\n </div>\n\n {/* Prompt callout + linked story */}\n {(discussion.prompt || discussion.storyId) && (\n <div className=\"flex flex-col gap-3 rounded-xl border border-border-seam bg-[var(--color-accent-soft)] p-4 sm:flex-row sm:items-start sm:justify-between\">\n {discussion.prompt ? (\n <div className=\"flex min-w-0 items-start gap-3\">\n <span\n className=\"mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-canvas/70 text-accent\"\n aria-hidden=\"true\"\n >\n <Quote className=\"h-4 w-4\" />\n </span>\n <p className=\"min-w-0 whitespace-pre-wrap text-sm leading-relaxed text-text-primary\">\n {discussion.prompt}\n </p>\n </div>\n ) : (\n <span className=\"text-sm text-text-secondary\">Anchored to a story.</span>\n )}\n {discussion.storyId && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`stories/${discussion.storyId}`)}\n className=\"inline-flex shrink-0 items-center gap-1.5 self-start rounded-lg px-1 text-sm font-medium text-accent hover:underline\"\n >\n <BookOpen className=\"h-4 w-4\" aria-hidden=\"true\" /> View linked story\n <ExternalLink className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n )}\n </GlassCard>\n\n <ReplyThread discussionId={discussion.id} isLocked={discussion.isLocked} />\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { useTheCulturePost as e } from "../../context/TheCulturePostContext.js";
|
|
2
2
|
import { cn as t } from "../../utils/cn.js";
|
|
3
|
-
import { PageLayout as n } from "../../components/PageLayout.js";
|
|
4
|
-
import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
|
|
5
3
|
import "../../context/index.js";
|
|
6
|
-
import {
|
|
7
|
-
import { EmptyState as
|
|
8
|
-
import { useTcpDiscussions as
|
|
9
|
-
import { useCursorPager as
|
|
4
|
+
import { ErrorView as n, LoadingView as r } from "../../components/StateViews.js";
|
|
5
|
+
import { EmptyState as i } from "../../components/EmptyState.js";
|
|
6
|
+
import { useTcpDiscussions as a } from "../../hooks/useTcpApi.js";
|
|
7
|
+
import { useCursorPager as o } from "../../hooks/useCursorPager.js";
|
|
10
8
|
import "../../hooks/index.js";
|
|
9
|
+
import { Pagination as s } from "../../components/Pagination.js";
|
|
10
|
+
import { PageLayout as c } from "../../components/PageLayout.js";
|
|
11
11
|
import { FilterTabs as l } from "../../components/FilterTabs.js";
|
|
12
|
-
import {
|
|
12
|
+
import { DiscussionCard as u } from "../../components/DiscussionCard.js";
|
|
13
13
|
import { OpenDiscussionDialog as d } from "../../components/OpenDiscussionDialog.js";
|
|
14
14
|
import { useState as f } from "react";
|
|
15
15
|
import { HeartHandshake as p, MessagesSquare as m, Plus as h } from "lucide-react";
|
|
@@ -39,14 +39,14 @@ var b = 12, x = [
|
|
|
39
39
|
}
|
|
40
40
|
];
|
|
41
41
|
function S() {
|
|
42
|
-
let { navigateTo: S } = e(), C =
|
|
42
|
+
let { navigateTo: S } = e(), C = o(), [w, T] = f("ALL"), [E, D] = f(!1), [O, k] = f(!1), { data: A, isLoading: j, isError: M, refetch: N } = a({
|
|
43
43
|
status: w === "ALL" ? void 0 : w,
|
|
44
44
|
pagination: {
|
|
45
45
|
limit: b,
|
|
46
46
|
cursor: C.cursor
|
|
47
47
|
}
|
|
48
48
|
}), P = A?.items ?? [], F = E ? P.filter((e) => e.isInterfaith) : P;
|
|
49
|
-
return /* @__PURE__ */ v(
|
|
49
|
+
return /* @__PURE__ */ v(c, {
|
|
50
50
|
title: "Discussions",
|
|
51
51
|
description: "Respectful, moderated conversations — including interfaith spaces that welcome many traditions.",
|
|
52
52
|
actions: /* @__PURE__ */ v(y, {
|
|
@@ -70,10 +70,10 @@ function S() {
|
|
|
70
70
|
children: [/* @__PURE__ */ _(p, { className: "h-4 w-4" }), " Interfaith"]
|
|
71
71
|
})]
|
|
72
72
|
}),
|
|
73
|
-
j ? /* @__PURE__ */ _(
|
|
73
|
+
j ? /* @__PURE__ */ _(r, { label: "Loading discussions…" }) : M ? /* @__PURE__ */ _(n, {
|
|
74
74
|
message: "We couldn't load discussions.",
|
|
75
75
|
onRetry: () => N()
|
|
76
|
-
}) : F.length === 0 && C.page === 0 ? /* @__PURE__ */ _(
|
|
76
|
+
}) : F.length === 0 && C.page === 0 ? /* @__PURE__ */ _(i, {
|
|
77
77
|
illustration: "empty-notifications",
|
|
78
78
|
icon: /* @__PURE__ */ _(m, { className: "h-10 w-10" }),
|
|
79
79
|
title: E ? "No interfaith discussions here" : w === "ALL" ? "No discussions yet" : "No discussions match this filter",
|
|
@@ -82,13 +82,13 @@ function S() {
|
|
|
82
82
|
onClick: () => k(!0),
|
|
83
83
|
children: [/* @__PURE__ */ _(h, { className: "mr-1.5 h-4 w-4" }), " Open discussion"]
|
|
84
84
|
})
|
|
85
|
-
}) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("
|
|
86
|
-
className: "
|
|
87
|
-
children: F.map((e) => /* @__PURE__ */ _(
|
|
85
|
+
}) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("ul", {
|
|
86
|
+
className: "flex flex-col gap-3",
|
|
87
|
+
children: F.map((e) => /* @__PURE__ */ _("li", { children: /* @__PURE__ */ _(u, {
|
|
88
88
|
discussion: e,
|
|
89
89
|
onOpen: () => S(`discussions/${e.id}`)
|
|
90
|
-
}, e.id))
|
|
91
|
-
}), /* @__PURE__ */ _(
|
|
90
|
+
}) }, e.id))
|
|
91
|
+
}), /* @__PURE__ */ _(s, {
|
|
92
92
|
page: C.page,
|
|
93
93
|
pageSize: b,
|
|
94
94
|
itemCount: P.length,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DiscussionsPage.js","names":[],"sources":["../../../../src/theculturepost/pages/discussions/DiscussionsPage.tsx"],"sourcesContent":["/**\n * DiscussionsPage — browse moderated community discussions.\n * @module theculturepost/pages/discussions\n *\n * Reads `tcpListDiscussions` (filtered by moderation status). The interfaith\n * toggle is a client-side filter over the loaded page (the svc list field does\n * not filter by interfaith). \"Open Discussion\" opens {@link OpenDiscussionDialog}.\n */\n\nimport { useState } from 'react';\nimport { MessagesSquare, Plus, HeartHandshake } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { FilterTabs, type FilterOption } from '../../components/FilterTabs';\nimport { DiscussionCard } from '../../components/DiscussionCard';\nimport { OpenDiscussionDialog } from '../../components/OpenDiscussionDialog';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport { useTcpDiscussions, useCursorPager } from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport { cn } from '../../utils/cn';\nimport type { TcpModerationStatus } from '../../types';\n\nconst PAGE_SIZE = 12;\n\ntype StatusFilter = 'ALL' | TcpModerationStatus;\n\nconst STATUS_FILTERS: FilterOption<StatusFilter>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'APPROVED', label: 'Approved' },\n { value: 'PENDING', label: 'Pending' },\n { value: 'FLAGGED', label: 'Flagged' },\n { value: 'REMOVED', label: 'Removed' },\n];\n\nexport function DiscussionsPage() {\n const { navigateTo } = useTheCulturePost();\n const pager = useCursorPager();\n const [status, setStatus] = useState<StatusFilter>('ALL');\n const [interfaithOnly, setInterfaithOnly] = useState(false);\n const [createOpen, setCreateOpen] = useState(false);\n\n const { data, isLoading, isError, refetch } = useTcpDiscussions({\n status: status === 'ALL' ? undefined : status,\n pagination: { limit: PAGE_SIZE, cursor: pager.cursor },\n });\n\n const allItems = data?.items ?? [];\n const discussions = interfaithOnly ? allItems.filter((d) => d.isInterfaith) : allItems;\n\n const changeStatus = (next: StatusFilter) => {\n setStatus(next);\n pager.reset();\n };\n\n return (\n <PageLayout\n title=\"Discussions\"\n description=\"Respectful, moderated conversations — including interfaith spaces that welcome many traditions.\"\n actions={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Open discussion\n </Button>\n }\n >\n <div className=\"mb-5 flex flex-wrap items-center gap-3\">\n <FilterTabs options={STATUS_FILTERS} value={status} onChange={changeStatus} />\n <button\n type=\"button\"\n aria-pressed={interfaithOnly}\n onClick={() => setInterfaithOnly((v) => !v)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-xl border px-3 py-1.5 text-sm font-medium transition-colors',\n interfaithOnly\n ? 'border-transparent bg-action-primary-bg text-action-primary-text'\n : 'border-border-seam bg-bg-surface text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n <HeartHandshake className=\"h-4 w-4\" /> Interfaith\n </button>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading discussions…\" />\n ) : isError ? (\n <ErrorView message=\"We couldn't load discussions.\" onRetry={() => refetch()} />\n ) : discussions.length === 0 && pager.page === 0 ? (\n <EmptyState\n illustration=\"empty-notifications\"\n icon={<MessagesSquare className=\"h-10 w-10\" />}\n title={\n interfaithOnly\n ? 'No interfaith discussions here'\n : status === 'ALL'\n ? 'No discussions yet'\n : 'No discussions match this filter'\n }\n description={\n status === 'ALL' && !interfaithOnly\n ? 'Open a discussion to invite thoughtful, respectful perspectives.'\n : 'Try a different filter, or open a new discussion.'\n }\n action={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Open discussion\n </Button>\n }\n />\n ) : (\n <>\n <
|
|
1
|
+
{"version":3,"file":"DiscussionsPage.js","names":[],"sources":["../../../../src/theculturepost/pages/discussions/DiscussionsPage.tsx"],"sourcesContent":["/**\n * DiscussionsPage — browse moderated community discussions.\n * @module theculturepost/pages/discussions\n *\n * Reads `tcpListDiscussions` (filtered by moderation status). The interfaith\n * toggle is a client-side filter over the loaded page (the svc list field does\n * not filter by interfaith). \"Open Discussion\" opens {@link OpenDiscussionDialog}.\n */\n\nimport { useState } from 'react';\nimport { MessagesSquare, Plus, HeartHandshake } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { FilterTabs, type FilterOption } from '../../components/FilterTabs';\nimport { DiscussionCard } from '../../components/DiscussionCard';\nimport { OpenDiscussionDialog } from '../../components/OpenDiscussionDialog';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport { useTcpDiscussions, useCursorPager } from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport { cn } from '../../utils/cn';\nimport type { TcpModerationStatus } from '../../types';\n\nconst PAGE_SIZE = 12;\n\ntype StatusFilter = 'ALL' | TcpModerationStatus;\n\nconst STATUS_FILTERS: FilterOption<StatusFilter>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'APPROVED', label: 'Approved' },\n { value: 'PENDING', label: 'Pending' },\n { value: 'FLAGGED', label: 'Flagged' },\n { value: 'REMOVED', label: 'Removed' },\n];\n\nexport function DiscussionsPage() {\n const { navigateTo } = useTheCulturePost();\n const pager = useCursorPager();\n const [status, setStatus] = useState<StatusFilter>('ALL');\n const [interfaithOnly, setInterfaithOnly] = useState(false);\n const [createOpen, setCreateOpen] = useState(false);\n\n const { data, isLoading, isError, refetch } = useTcpDiscussions({\n status: status === 'ALL' ? undefined : status,\n pagination: { limit: PAGE_SIZE, cursor: pager.cursor },\n });\n\n const allItems = data?.items ?? [];\n const discussions = interfaithOnly ? allItems.filter((d) => d.isInterfaith) : allItems;\n\n const changeStatus = (next: StatusFilter) => {\n setStatus(next);\n pager.reset();\n };\n\n return (\n <PageLayout\n title=\"Discussions\"\n description=\"Respectful, moderated conversations — including interfaith spaces that welcome many traditions.\"\n actions={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Open discussion\n </Button>\n }\n >\n <div className=\"mb-5 flex flex-wrap items-center gap-3\">\n <FilterTabs options={STATUS_FILTERS} value={status} onChange={changeStatus} />\n <button\n type=\"button\"\n aria-pressed={interfaithOnly}\n onClick={() => setInterfaithOnly((v) => !v)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-xl border px-3 py-1.5 text-sm font-medium transition-colors',\n interfaithOnly\n ? 'border-transparent bg-action-primary-bg text-action-primary-text'\n : 'border-border-seam bg-bg-surface text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n <HeartHandshake className=\"h-4 w-4\" /> Interfaith\n </button>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading discussions…\" />\n ) : isError ? (\n <ErrorView message=\"We couldn't load discussions.\" onRetry={() => refetch()} />\n ) : discussions.length === 0 && pager.page === 0 ? (\n <EmptyState\n illustration=\"empty-notifications\"\n icon={<MessagesSquare className=\"h-10 w-10\" />}\n title={\n interfaithOnly\n ? 'No interfaith discussions here'\n : status === 'ALL'\n ? 'No discussions yet'\n : 'No discussions match this filter'\n }\n description={\n status === 'ALL' && !interfaithOnly\n ? 'Open a discussion to invite thoughtful, respectful perspectives.'\n : 'Try a different filter, or open a new discussion.'\n }\n action={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Open discussion\n </Button>\n }\n />\n ) : (\n <>\n <ul className=\"flex flex-col gap-3\">\n {discussions.map((discussion) => (\n <li key={discussion.id}>\n <DiscussionCard\n discussion={discussion}\n onOpen={() => navigateTo(`discussions/${discussion.id}`)}\n />\n </li>\n ))}\n </ul>\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={allItems.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target, data?.pageInfo.cursor)}\n />\n </>\n )}\n\n <OpenDiscussionDialog\n open={createOpen}\n onOpenChange={setCreateOpen}\n onCreated={(discussion) => navigateTo(`discussions/${discussion.id}`)}\n />\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwBA,IAAM,IAAY,IAIZ,IAA+C;CACnD;EAAE,OAAO;EAAO,OAAO;EAAO;CAC9B;EAAE,OAAO;EAAY,OAAO;EAAY;CACxC;EAAE,OAAO;EAAW,OAAO;EAAW;CACtC;EAAE,OAAO;EAAW,OAAO;EAAW;CACtC;EAAE,OAAO;EAAW,OAAO;EAAW;CACvC;AAED,SAAgB,IAAkB;CAChC,IAAM,EAAE,kBAAe,GAAmB,EACpC,IAAQ,GAAgB,EACxB,CAAC,GAAQ,KAAa,EAAuB,MAAM,EACnD,CAAC,GAAgB,KAAqB,EAAS,GAAM,EACrD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAE7C,EAAE,SAAM,cAAW,YAAS,eAAY,EAAkB;EAC9D,QAAQ,MAAW,QAAQ,KAAA,IAAY;EACvC,YAAY;GAAE,OAAO;GAAW,QAAQ,EAAM;GAAQ;EACvD,CAAC,EAEI,IAAW,GAAM,SAAS,EAAE,EAC5B,IAAc,IAAiB,EAAS,QAAQ,MAAM,EAAE,aAAa,GAAG;AAO9E,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,GAAD;GAAQ,eAAe,EAAc,GAAK;aAA1C,CACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,mBAC5B;;YANb;GASE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAY,SAAS;KAAgB,OAAO;KAAQ,WAhBpC,MAAuB;AAE3C,MADA,EAAU,EAAK,EACf,EAAM,OAAO;;KAcqE,CAAA,EAC9E,kBAAC,UAAD;KACE,MAAK;KACL,gBAAc;KACd,eAAe,GAAmB,MAAM,CAAC,EAAE;KAC3C,WAAW,EACT,wGACA,IACI,qEACA,kGACL;eATH,CAWE,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA,EAAA,cAC/B;OACL;;GAEL,IACC,kBAAC,GAAD,EAAa,OAAM,wBAAyB,CAAA,GAC1C,IACF,kBAAC,GAAD;IAAW,SAAQ;IAAgC,eAAe,GAAS;IAAI,CAAA,GAC7E,EAAY,WAAW,KAAK,EAAM,SAAS,IAC7C,kBAAC,GAAD;IACE,cAAa;IACb,MAAM,kBAAC,GAAD,EAAgB,WAAU,aAAc,CAAA;IAC9C,OACE,IACI,mCACA,MAAW,QACT,uBACA;IAER,aACE,MAAW,SAAS,CAAC,IACjB,qEACA;IAEN,QACE,kBAAC,GAAD;KAAQ,eAAe,EAAc,GAAK;eAA1C,CACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,mBAC5B;;IAEX,CAAA,GAEF,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAY,KAAK,MAChB,kBAAC,MAAD,EAAA,UACE,kBAAC,GAAD;KACc;KACZ,cAAc,EAAW,eAAe,EAAW,KAAK;KACxD,CAAA,EACC,EALI,EAAW,GAKf,CACL;IACC,CAAA,EACL,kBAAC,GAAD;IACE,MAAM,EAAM;IACZ,UAAU;IACV,WAAW,EAAS;IACpB,SAAS,GAAM,SAAS,WAAW;IACnC,eAAe,MAAW,EAAM,aAAa,GAAQ,GAAM,SAAS,OAAO;IAC3E,CAAA,CACD,EAAA,CAAA;GAGL,kBAAC,GAAD;IACE,MAAM;IACN,cAAc;IACd,YAAY,MAAe,EAAW,eAAe,EAAW,KAAK;IACrE,CAAA;GACS"}
|