@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,177 +1,353 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { FILE_FOLDERS as t, uploadImageToFiles as n } from "../hooks/useFiles.js";
|
|
3
|
+
import { formatEnumLabel as r } from "../utils/formatters.js";
|
|
4
|
+
import { useTcpCreateStory as ee, useTcpTopics as te } from "../hooks/useTcpApi.js";
|
|
4
5
|
import "../hooks/index.js";
|
|
5
6
|
import { slugify as i } from "../utils/slug.js";
|
|
6
|
-
import { describeError as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { jsx as
|
|
11
|
-
import { Button as
|
|
7
|
+
import { describeError as a, notify as o } from "../lib/toast.js";
|
|
8
|
+
import { TagInput as s } from "./TagInput.js";
|
|
9
|
+
import { useRef as c, useState as l } from "react";
|
|
10
|
+
import { CheckCircle2 as u, FileCheck2 as d, Loader2 as f, UploadCloud as p, X as m } from "lucide-react";
|
|
11
|
+
import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
12
|
+
import { Button as v, Input as y, Label as b, ResponsiveDialog as x, ResponsiveDialogContent as S, ResponsiveDialogDescription as C, ResponsiveDialogFooter as w, ResponsiveDialogHeader as T, ResponsiveDialogTitle as E, Select as D, SelectContent as O, SelectItem as ne, SelectTrigger as k, SelectValue as A, Textarea as j } from "@burdenoff/fe-libs/ui";
|
|
12
13
|
//#region src/theculturepost/components/CreateStoryDialog.tsx
|
|
13
|
-
var
|
|
14
|
+
var re = [
|
|
14
15
|
"ARTICLE",
|
|
15
16
|
"ESSAY",
|
|
16
17
|
"EXPLAINER",
|
|
17
18
|
"ORAL_HISTORY",
|
|
18
19
|
"PHOTO_STORY",
|
|
19
20
|
"TIMELINE"
|
|
20
|
-
];
|
|
21
|
-
function
|
|
22
|
-
let
|
|
23
|
-
|
|
21
|
+
], M = 200, N = 25;
|
|
22
|
+
function ie({ coverFileId: r, onUploaded: ee, onCleared: te }) {
|
|
23
|
+
let i = c(null), [s, u] = l(!1), [h, y] = l(!1), [b, x] = l(null), [S, C] = l(null), w = async (e) => {
|
|
24
|
+
if (!e.type.startsWith("image/")) {
|
|
25
|
+
o.error("Unsupported file", "Please choose an image (JPG or PNG).");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (e.size > N * 1024 * 1024) {
|
|
29
|
+
o.error("File too large", `Please choose an image under ${N} MB.`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
u(!0);
|
|
33
|
+
try {
|
|
34
|
+
let r = await n(e, t.covers);
|
|
35
|
+
x(r.url), C(r.name), ee(r.id), o.success("Cover uploaded", r.name);
|
|
36
|
+
} catch (e) {
|
|
37
|
+
o.error("Upload failed", a(e));
|
|
38
|
+
} finally {
|
|
39
|
+
u(!1);
|
|
40
|
+
}
|
|
41
|
+
}, T = (e) => {
|
|
42
|
+
let t = e.target.files?.[0];
|
|
43
|
+
e.target.value = "", t && w(t);
|
|
44
|
+
}, E = (e) => {
|
|
45
|
+
if (e.preventDefault(), y(!1), s) return;
|
|
46
|
+
let t = e.dataTransfer.files?.[0];
|
|
47
|
+
t && w(t);
|
|
48
|
+
}, D = () => {
|
|
49
|
+
x(null), C(null), te();
|
|
50
|
+
}, O = /* @__PURE__ */ g("input", {
|
|
51
|
+
ref: i,
|
|
52
|
+
type: "file",
|
|
53
|
+
accept: "image/*",
|
|
54
|
+
className: "sr-only",
|
|
55
|
+
onChange: T,
|
|
56
|
+
disabled: s,
|
|
57
|
+
"aria-label": "Upload cover image"
|
|
58
|
+
});
|
|
59
|
+
return b ? /* @__PURE__ */ _("div", {
|
|
60
|
+
className: "relative overflow-hidden rounded-2xl border border-border-seam",
|
|
61
|
+
children: [
|
|
62
|
+
O,
|
|
63
|
+
/* @__PURE__ */ g("img", {
|
|
64
|
+
src: b,
|
|
65
|
+
alt: "Cover preview",
|
|
66
|
+
className: "max-h-52 w-full object-cover"
|
|
67
|
+
}),
|
|
68
|
+
/* @__PURE__ */ _("div", {
|
|
69
|
+
className: "absolute right-2 top-2 flex gap-1.5",
|
|
70
|
+
children: [/* @__PURE__ */ g("button", {
|
|
71
|
+
type: "button",
|
|
72
|
+
onClick: () => i.current?.click(),
|
|
73
|
+
className: "rounded-full bg-bg-canvas/85 px-2.5 py-1 text-xs font-medium text-text-primary backdrop-blur-sm hover:bg-bg-canvas",
|
|
74
|
+
children: "Replace"
|
|
75
|
+
}), /* @__PURE__ */ g("button", {
|
|
76
|
+
type: "button",
|
|
77
|
+
onClick: D,
|
|
78
|
+
"aria-label": "Remove cover",
|
|
79
|
+
className: "rounded-full bg-bg-canvas/85 p-1.5 text-text-primary backdrop-blur-sm hover:bg-bg-canvas",
|
|
80
|
+
children: /* @__PURE__ */ g(m, {
|
|
81
|
+
className: "h-4 w-4",
|
|
82
|
+
"aria-hidden": "true"
|
|
83
|
+
})
|
|
84
|
+
})]
|
|
85
|
+
})
|
|
86
|
+
]
|
|
87
|
+
}) : r ? /* @__PURE__ */ _("div", {
|
|
88
|
+
className: "flex items-center justify-between gap-2 rounded-2xl border border-border-seam bg-bg-surface px-3 py-2.5 text-sm",
|
|
89
|
+
children: [
|
|
90
|
+
O,
|
|
91
|
+
/* @__PURE__ */ _("span", {
|
|
92
|
+
className: "inline-flex min-w-0 items-center gap-2 text-text-primary",
|
|
93
|
+
children: [/* @__PURE__ */ g(d, {
|
|
94
|
+
className: "h-4 w-4 shrink-0 text-status-success-text",
|
|
95
|
+
"aria-hidden": "true"
|
|
96
|
+
}), /* @__PURE__ */ g("span", {
|
|
97
|
+
className: "truncate",
|
|
98
|
+
children: S ?? "Cover selected"
|
|
99
|
+
})]
|
|
100
|
+
}),
|
|
101
|
+
/* @__PURE__ */ g("button", {
|
|
102
|
+
type: "button",
|
|
103
|
+
onClick: D,
|
|
104
|
+
"aria-label": "Remove cover",
|
|
105
|
+
className: "text-text-tertiary hover:text-text-primary",
|
|
106
|
+
children: /* @__PURE__ */ g(m, {
|
|
107
|
+
className: "h-4 w-4",
|
|
108
|
+
"aria-hidden": "true"
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
]
|
|
112
|
+
}) : /* @__PURE__ */ _("div", {
|
|
113
|
+
onDragOver: (e) => {
|
|
114
|
+
e.preventDefault(), s || y(!0);
|
|
115
|
+
},
|
|
116
|
+
onDragLeave: (e) => {
|
|
117
|
+
e.preventDefault(), y(!1);
|
|
118
|
+
},
|
|
119
|
+
onDrop: E,
|
|
120
|
+
className: e("rounded-2xl border-2 border-dashed px-4 py-6 text-center transition-colors", h ? "border-accent bg-[var(--color-accent-soft)]" : "border-border-seam bg-bg-sunken/40"),
|
|
121
|
+
children: [
|
|
122
|
+
O,
|
|
123
|
+
s ? /* @__PURE__ */ _("div", {
|
|
124
|
+
className: "flex items-center justify-center gap-2 text-sm text-text-secondary",
|
|
125
|
+
children: [/* @__PURE__ */ g(f, {
|
|
126
|
+
className: "h-5 w-5 animate-spin",
|
|
127
|
+
"aria-hidden": "true"
|
|
128
|
+
}), " Uploading cover…"]
|
|
129
|
+
}) : /* @__PURE__ */ _("div", {
|
|
130
|
+
className: "flex flex-col items-center justify-center gap-3 sm:flex-row",
|
|
131
|
+
children: [
|
|
132
|
+
/* @__PURE__ */ g(p, {
|
|
133
|
+
className: "h-8 w-8 text-accent",
|
|
134
|
+
"aria-hidden": "true"
|
|
135
|
+
}),
|
|
136
|
+
/* @__PURE__ */ g("p", {
|
|
137
|
+
className: "text-sm text-text-secondary",
|
|
138
|
+
children: "Drag and drop an image here, or"
|
|
139
|
+
}),
|
|
140
|
+
/* @__PURE__ */ g(v, {
|
|
141
|
+
type: "button",
|
|
142
|
+
size: "sm",
|
|
143
|
+
onClick: () => i.current?.click(),
|
|
144
|
+
children: "Upload cover"
|
|
145
|
+
})
|
|
146
|
+
]
|
|
147
|
+
}),
|
|
148
|
+
/* @__PURE__ */ _("p", {
|
|
149
|
+
className: "mt-3 text-xs text-text-tertiary",
|
|
150
|
+
children: [
|
|
151
|
+
"Recommended: 16:9 ratio, min 1200×675px. JPG or PNG, up to ",
|
|
152
|
+
N,
|
|
153
|
+
" MB."
|
|
154
|
+
]
|
|
155
|
+
})
|
|
156
|
+
]
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function P({ open: e, onOpenChange: t, topicId: n, onCreated: c }) {
|
|
160
|
+
let d = ee(), p = te({ pagination: { limit: 100 } }), [m, N] = l(""), [P, F] = l(""), [ae, I] = l(!1), [L, R] = l(""), [z, B] = l(""), [V, oe] = l("ARTICLE"), [H, U] = l(n ?? ""), [W, G] = l([]), [K, q] = l([]), [J, Y] = l(""), [X, Z] = l(null), [se, Q] = l(null), [ce, le] = l(0), $ = ae ? P : i(m), ue = () => {
|
|
161
|
+
N(""), F(""), I(!1), R(""), B(""), oe("ARTICLE"), U(n ?? ""), G([]), q([]), Y(""), Z(null), Q(null), le((e) => e + 1);
|
|
24
162
|
};
|
|
25
|
-
return /* @__PURE__ */
|
|
26
|
-
open:
|
|
27
|
-
onOpenChange:
|
|
28
|
-
children: /* @__PURE__ */
|
|
29
|
-
className: "sm:max-w-
|
|
163
|
+
return /* @__PURE__ */ g(x, {
|
|
164
|
+
open: e,
|
|
165
|
+
onOpenChange: t,
|
|
166
|
+
children: /* @__PURE__ */ _(S, {
|
|
167
|
+
className: "sm:max-w-3xl",
|
|
30
168
|
children: [
|
|
31
|
-
/* @__PURE__ */
|
|
32
|
-
|
|
33
|
-
|
|
169
|
+
/* @__PURE__ */ _(T, { children: [/* @__PURE__ */ g(E, {
|
|
170
|
+
className: "font-display",
|
|
171
|
+
children: "Draft a story"
|
|
172
|
+
}), /* @__PURE__ */ g(C, { children: "Begin a new piece — it enters the editorial lifecycle as a draft you can refine and publish later." })] }),
|
|
173
|
+
/* @__PURE__ */ _("div", {
|
|
174
|
+
className: "flex max-h-[64vh] flex-col gap-5 overflow-y-auto py-2",
|
|
34
175
|
children: [
|
|
35
|
-
/* @__PURE__ */
|
|
36
|
-
className: "grid gap-1.5",
|
|
37
|
-
children: [/* @__PURE__ */ c(f, { children: "Cover image" }), /* @__PURE__ */ c(a, {
|
|
38
|
-
folder: e.covers,
|
|
39
|
-
accept: "image/*",
|
|
40
|
-
label: "Upload cover",
|
|
41
|
-
hint: "A hero image for the story. PNG or JPG, up to 25 MB.",
|
|
42
|
-
fileId: Y,
|
|
43
|
-
onUploaded: X,
|
|
44
|
-
onCleared: () => X(null)
|
|
45
|
-
})]
|
|
46
|
-
}),
|
|
47
|
-
/* @__PURE__ */ l("div", {
|
|
176
|
+
/* @__PURE__ */ _("div", {
|
|
48
177
|
className: "grid gap-1.5",
|
|
49
|
-
children: [/* @__PURE__ */
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
value: k,
|
|
55
|
-
onChange: (e) => A(e.target.value),
|
|
56
|
-
placeholder: "e.g. The Weavers of the Coromandel Coast"
|
|
57
|
-
})]
|
|
178
|
+
children: [/* @__PURE__ */ g(b, { children: "Cover image" }), /* @__PURE__ */ g(ie, {
|
|
179
|
+
coverFileId: X,
|
|
180
|
+
onUploaded: Z,
|
|
181
|
+
onCleared: () => Z(null)
|
|
182
|
+
}, ce)]
|
|
58
183
|
}),
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
-
className: "grid gap-
|
|
61
|
-
children: [/* @__PURE__ */
|
|
62
|
-
|
|
63
|
-
children:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
184
|
+
/* @__PURE__ */ _("div", {
|
|
185
|
+
className: "grid gap-4 sm:grid-cols-2",
|
|
186
|
+
children: [/* @__PURE__ */ _("div", {
|
|
187
|
+
className: "grid gap-1.5",
|
|
188
|
+
children: [/* @__PURE__ */ g(b, {
|
|
189
|
+
htmlFor: "story-title",
|
|
190
|
+
children: "Title"
|
|
191
|
+
}), /* @__PURE__ */ g(y, {
|
|
192
|
+
id: "story-title",
|
|
193
|
+
value: m,
|
|
194
|
+
onChange: (e) => N(e.target.value),
|
|
195
|
+
placeholder: "e.g. The Weavers of the Coromandel Coast"
|
|
196
|
+
})]
|
|
197
|
+
}), /* @__PURE__ */ _("div", {
|
|
198
|
+
className: "grid gap-1.5",
|
|
199
|
+
children: [
|
|
200
|
+
/* @__PURE__ */ g(b, {
|
|
201
|
+
htmlFor: "story-slug",
|
|
202
|
+
children: "URL slug"
|
|
203
|
+
}),
|
|
204
|
+
/* @__PURE__ */ _("div", {
|
|
205
|
+
className: "relative",
|
|
206
|
+
children: [/* @__PURE__ */ g(y, {
|
|
207
|
+
id: "story-slug",
|
|
208
|
+
value: $,
|
|
209
|
+
onChange: (e) => {
|
|
210
|
+
I(!0), F(i(e.target.value));
|
|
211
|
+
},
|
|
212
|
+
placeholder: "the-weavers-of-the-coromandel-coast",
|
|
213
|
+
className: $ ? "pr-9" : void 0
|
|
214
|
+
}), $ && /* @__PURE__ */ g(u, {
|
|
215
|
+
className: "pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-status-success-text",
|
|
216
|
+
"aria-hidden": "true"
|
|
217
|
+
})]
|
|
218
|
+
}),
|
|
219
|
+
/* @__PURE__ */ g("p", {
|
|
220
|
+
className: "text-xs text-text-tertiary",
|
|
221
|
+
children: "Auto-derived from the title — used in the story’s URL, so keep it unique."
|
|
222
|
+
})
|
|
223
|
+
]
|
|
71
224
|
})]
|
|
72
225
|
}),
|
|
73
|
-
/* @__PURE__ */
|
|
74
|
-
className: "grid grid-cols-2
|
|
75
|
-
children: [/* @__PURE__ */
|
|
226
|
+
/* @__PURE__ */ _("div", {
|
|
227
|
+
className: "grid gap-4 sm:grid-cols-2",
|
|
228
|
+
children: [/* @__PURE__ */ _("div", {
|
|
76
229
|
className: "grid gap-1.5",
|
|
77
|
-
children: [/* @__PURE__ */
|
|
78
|
-
value:
|
|
79
|
-
onValueChange: (e) =>
|
|
80
|
-
children: [/* @__PURE__ */
|
|
230
|
+
children: [/* @__PURE__ */ g(b, { children: "Format" }), /* @__PURE__ */ _(D, {
|
|
231
|
+
value: V,
|
|
232
|
+
onValueChange: (e) => oe(e),
|
|
233
|
+
children: [/* @__PURE__ */ g(k, { children: /* @__PURE__ */ g(A, {}) }), /* @__PURE__ */ g(O, { children: re.map((e) => /* @__PURE__ */ g(ne, {
|
|
81
234
|
value: e,
|
|
82
|
-
children:
|
|
235
|
+
children: r(e)
|
|
83
236
|
}, e)) })]
|
|
84
237
|
})]
|
|
85
|
-
}), /* @__PURE__ */
|
|
238
|
+
}), /* @__PURE__ */ _("div", {
|
|
86
239
|
className: "grid gap-1.5",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
240
|
+
children: [/* @__PURE__ */ g(b, {
|
|
88
241
|
htmlFor: "story-reading",
|
|
89
242
|
children: "Reading time (min)"
|
|
90
|
-
}), /* @__PURE__ */
|
|
243
|
+
}), /* @__PURE__ */ g(y, {
|
|
91
244
|
id: "story-reading",
|
|
92
245
|
type: "number",
|
|
93
246
|
min: 0,
|
|
94
|
-
value:
|
|
95
|
-
onChange: (e) =>
|
|
247
|
+
value: J,
|
|
248
|
+
onChange: (e) => Y(e.target.value),
|
|
96
249
|
placeholder: "Optional"
|
|
97
250
|
})]
|
|
98
251
|
})]
|
|
99
252
|
}),
|
|
100
|
-
/* @__PURE__ */
|
|
253
|
+
/* @__PURE__ */ _("div", {
|
|
101
254
|
className: "grid gap-1.5",
|
|
102
|
-
children: [/* @__PURE__ */
|
|
103
|
-
value:
|
|
104
|
-
onValueChange:
|
|
105
|
-
children: [/* @__PURE__ */
|
|
255
|
+
children: [/* @__PURE__ */ g(b, { children: "Topic" }), /* @__PURE__ */ _(D, {
|
|
256
|
+
value: H,
|
|
257
|
+
onValueChange: U,
|
|
258
|
+
children: [/* @__PURE__ */ g(k, { children: /* @__PURE__ */ g(A, { placeholder: "Choose a topic (optional)" }) }), /* @__PURE__ */ g(O, { children: (p.data?.items ?? []).map((e) => /* @__PURE__ */ g(ne, {
|
|
106
259
|
value: e.id,
|
|
107
260
|
children: e.name
|
|
108
261
|
}, e.id)) })]
|
|
109
262
|
})]
|
|
110
263
|
}),
|
|
111
|
-
/* @__PURE__ */
|
|
264
|
+
/* @__PURE__ */ _("div", {
|
|
112
265
|
className: "grid gap-1.5",
|
|
113
|
-
children: [/* @__PURE__ */
|
|
114
|
-
|
|
115
|
-
children:
|
|
116
|
-
|
|
266
|
+
children: [/* @__PURE__ */ _("div", {
|
|
267
|
+
className: "flex items-center justify-between gap-2",
|
|
268
|
+
children: [/* @__PURE__ */ g(b, {
|
|
269
|
+
htmlFor: "story-summary",
|
|
270
|
+
children: "Summary"
|
|
271
|
+
}), /* @__PURE__ */ _("span", {
|
|
272
|
+
className: "text-xs tabular-nums text-text-tertiary",
|
|
273
|
+
children: [
|
|
274
|
+
L.length,
|
|
275
|
+
"/",
|
|
276
|
+
M
|
|
277
|
+
]
|
|
278
|
+
})]
|
|
279
|
+
}), /* @__PURE__ */ g(j, {
|
|
117
280
|
id: "story-summary",
|
|
118
|
-
value:
|
|
119
|
-
onChange: (e) =>
|
|
281
|
+
value: L,
|
|
282
|
+
onChange: (e) => R(e.target.value.slice(0, M)),
|
|
283
|
+
maxLength: M,
|
|
120
284
|
placeholder: "A short standfirst that introduces the story.",
|
|
121
285
|
rows: 2
|
|
122
286
|
})]
|
|
123
287
|
}),
|
|
124
|
-
/* @__PURE__ */
|
|
288
|
+
/* @__PURE__ */ _("div", {
|
|
125
289
|
className: "grid gap-1.5",
|
|
126
|
-
children: [/* @__PURE__ */
|
|
127
|
-
|
|
128
|
-
children:
|
|
129
|
-
|
|
290
|
+
children: [/* @__PURE__ */ _("div", {
|
|
291
|
+
className: "flex items-center justify-between gap-2",
|
|
292
|
+
children: [/* @__PURE__ */ g(b, {
|
|
293
|
+
htmlFor: "story-body",
|
|
294
|
+
children: "Body"
|
|
295
|
+
}), /* @__PURE__ */ _("span", {
|
|
296
|
+
className: "text-xs tabular-nums text-text-tertiary",
|
|
297
|
+
children: [
|
|
298
|
+
z.length,
|
|
299
|
+
" ",
|
|
300
|
+
z.length === 1 ? "character" : "characters"
|
|
301
|
+
]
|
|
302
|
+
})]
|
|
303
|
+
}), /* @__PURE__ */ g(j, {
|
|
130
304
|
id: "story-body",
|
|
131
|
-
value:
|
|
132
|
-
onChange: (e) =>
|
|
305
|
+
value: z,
|
|
306
|
+
onChange: (e) => B(e.target.value),
|
|
133
307
|
placeholder: "Write the story… (line breaks are preserved)",
|
|
134
308
|
rows: 5
|
|
135
309
|
})]
|
|
136
310
|
}),
|
|
137
|
-
/* @__PURE__ */
|
|
138
|
-
className: "grid gap-
|
|
139
|
-
children: [/* @__PURE__ */
|
|
140
|
-
|
|
141
|
-
children:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
311
|
+
/* @__PURE__ */ _("div", {
|
|
312
|
+
className: "grid gap-4 sm:grid-cols-2",
|
|
313
|
+
children: [/* @__PURE__ */ _("div", {
|
|
314
|
+
className: "grid gap-1.5",
|
|
315
|
+
children: [/* @__PURE__ */ g(b, {
|
|
316
|
+
htmlFor: "story-langs",
|
|
317
|
+
children: "Languages"
|
|
318
|
+
}), /* @__PURE__ */ g(s, {
|
|
319
|
+
id: "story-langs",
|
|
320
|
+
value: W,
|
|
321
|
+
onChange: G,
|
|
322
|
+
placeholder: "e.g. English, Tamil…",
|
|
323
|
+
preserveCase: !0
|
|
324
|
+
})]
|
|
325
|
+
}), /* @__PURE__ */ _("div", {
|
|
326
|
+
className: "grid gap-1.5",
|
|
327
|
+
children: [/* @__PURE__ */ g(b, {
|
|
328
|
+
htmlFor: "story-tags",
|
|
329
|
+
children: "Tags"
|
|
330
|
+
}), /* @__PURE__ */ g(s, {
|
|
331
|
+
id: "story-tags",
|
|
332
|
+
value: K,
|
|
333
|
+
onChange: q,
|
|
334
|
+
placeholder: "Add tags…"
|
|
335
|
+
})]
|
|
160
336
|
})]
|
|
161
337
|
}),
|
|
162
|
-
|
|
338
|
+
se && /* @__PURE__ */ g("p", {
|
|
163
339
|
className: "text-sm text-status-error-text",
|
|
164
|
-
children:
|
|
340
|
+
children: se
|
|
165
341
|
})
|
|
166
342
|
]
|
|
167
343
|
}),
|
|
168
|
-
/* @__PURE__ */
|
|
344
|
+
/* @__PURE__ */ _(w, { children: [/* @__PURE__ */ g(v, {
|
|
169
345
|
variant: "outline",
|
|
170
|
-
onClick: () =>
|
|
346
|
+
onClick: () => t(!1),
|
|
171
347
|
children: "Cancel"
|
|
172
|
-
}), /* @__PURE__ */
|
|
348
|
+
}), /* @__PURE__ */ g(v, {
|
|
173
349
|
onClick: async () => {
|
|
174
|
-
if (!
|
|
350
|
+
if (!m.trim()) {
|
|
175
351
|
Q("A title is required.");
|
|
176
352
|
return;
|
|
177
353
|
}
|
|
@@ -180,31 +356,34 @@ function w({ open: w, onOpenChange: T, topicId: E, onCreated: ie }) {
|
|
|
180
356
|
return;
|
|
181
357
|
}
|
|
182
358
|
try {
|
|
183
|
-
let e = await
|
|
184
|
-
title:
|
|
359
|
+
let e = await d.mutateAsync({
|
|
360
|
+
title: m.trim(),
|
|
185
361
|
slug: $,
|
|
186
|
-
summary:
|
|
187
|
-
body:
|
|
188
|
-
format:
|
|
189
|
-
topicId:
|
|
190
|
-
languages:
|
|
191
|
-
tags:
|
|
192
|
-
readingMins:
|
|
193
|
-
coverFileId:
|
|
362
|
+
summary: L.trim() || null,
|
|
363
|
+
body: z.trim() || null,
|
|
364
|
+
format: V,
|
|
365
|
+
topicId: H || null,
|
|
366
|
+
languages: W.length ? W : null,
|
|
367
|
+
tags: K.length ? K : null,
|
|
368
|
+
readingMins: J ? Number(J) : null,
|
|
369
|
+
coverFileId: X
|
|
194
370
|
});
|
|
195
|
-
|
|
371
|
+
o.success("Story created", e.title), t(!1), ue(), c?.(e);
|
|
196
372
|
} catch (e) {
|
|
197
|
-
Q(
|
|
373
|
+
Q(a(e));
|
|
198
374
|
}
|
|
199
375
|
},
|
|
200
|
-
disabled:
|
|
201
|
-
children:
|
|
376
|
+
disabled: d.isPending,
|
|
377
|
+
children: d.isPending ? /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(f, {
|
|
378
|
+
className: "mr-1.5 h-4 w-4 animate-spin",
|
|
379
|
+
"aria-hidden": "true"
|
|
380
|
+
}), " Creating…"] }) : "Create story"
|
|
202
381
|
})] })
|
|
203
382
|
]
|
|
204
383
|
})
|
|
205
384
|
});
|
|
206
385
|
}
|
|
207
386
|
//#endregion
|
|
208
|
-
export {
|
|
387
|
+
export { P as CreateStoryDialog };
|
|
209
388
|
|
|
210
389
|
//# sourceMappingURL=CreateStoryDialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateStoryDialog.js","names":[],"sources":["../../../src/theculturepost/components/CreateStoryDialog.tsx"],"sourcesContent":["/**\n * CreateStoryDialog — draft a new story.\n * @module theculturepost/components\n *\n * Writes `tcpCreateStory`. Slug auto-derives from the title (editable). New\n * stories begin at the start of the editorial lifecycle (IDEA/DRAFT on the svc).\n */\n\nimport { useState } from '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 { TagInput } from './TagInput';\nimport { FileUploadField } from './FileUploadField';\nimport { useTcpCreateStory, useTcpTopics, FILE_FOLDERS } from '../hooks';\nimport { slugify } from '../utils/slug';\nimport { formatEnumLabel } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { TcpStory, TcpStoryFormat } from '../types';\n\nconst FORMATS: TcpStoryFormat[] = [\n 'ARTICLE',\n 'ESSAY',\n 'EXPLAINER',\n 'ORAL_HISTORY',\n 'PHOTO_STORY',\n 'TIMELINE',\n];\n\ninterface CreateStoryDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Pre-select a topic (e.g. from a topic detail page). */\n topicId?: string;\n onCreated?: (story: TcpStory) => void;\n}\n\nexport function CreateStoryDialog({\n open,\n onOpenChange,\n topicId,\n onCreated,\n}: CreateStoryDialogProps) {\n const createStory = useTcpCreateStory();\n const topicsQuery = useTcpTopics({ pagination: { limit: 100 } });\n\n const [title, setTitle] = useState('');\n const [slug, setSlug] = useState('');\n const [slugTouched, setSlugTouched] = useState(false);\n const [summary, setSummary] = useState('');\n const [body, setBody] = useState('');\n const [format, setFormat] = useState<TcpStoryFormat>('ARTICLE');\n const [pickedTopic, setPickedTopic] = useState(topicId ?? '');\n const [languages, setLanguages] = useState<string[]>([]);\n const [tags, setTags] = useState<string[]>([]);\n const [readingMins, setReadingMins] = useState('');\n const [coverFileId, setCoverFileId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const effectiveSlug = slugTouched ? slug : slugify(title);\n\n const reset = () => {\n setTitle('');\n setSlug('');\n setSlugTouched(false);\n setSummary('');\n setBody('');\n setFormat('ARTICLE');\n setPickedTopic(topicId ?? '');\n setLanguages([]);\n setTags([]);\n setReadingMins('');\n setCoverFileId(null);\n setError(null);\n };\n\n const submit = async () => {\n if (!title.trim()) {\n setError('A title is required.');\n return;\n }\n if (!effectiveSlug) {\n setError('A URL slug is required.');\n return;\n }\n try {\n const created = await createStory.mutateAsync({\n title: title.trim(),\n slug: effectiveSlug,\n summary: summary.trim() || null,\n body: body.trim() || null,\n format,\n topicId: pickedTopic || null,\n languages: languages.length ? languages : null,\n tags: tags.length ? tags : null,\n readingMins: readingMins ? Number(readingMins) : null,\n coverFileId,\n });\n notify.success('Story created', created.title);\n onOpenChange(false);\n reset();\n onCreated?.(created);\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>Draft a story</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Begin a new piece — it enters the editorial lifecycle as a draft you can refine and\n publish later.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex max-h-[62vh] flex-col gap-4 overflow-y-auto py-2\">\n <div className=\"grid gap-1.5\">\n <Label>Cover image</Label>\n <FileUploadField\n folder={FILE_FOLDERS.covers}\n accept=\"image/*\"\n label=\"Upload cover\"\n hint=\"A hero image for the story. PNG or JPG, up to 25 MB.\"\n fileId={coverFileId}\n onUploaded={setCoverFileId}\n onCleared={() => setCoverFileId(null)}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-title\">Title</Label>\n <Input\n id=\"story-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. The Weavers of the Coromandel Coast\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-slug\">URL slug</Label>\n <Input\n id=\"story-slug\"\n value={effectiveSlug}\n onChange={(e) => {\n setSlugTouched(true);\n setSlug(slugify(e.target.value));\n }}\n placeholder=\"the-weavers-of-the-coromandel-coast\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"grid gap-1.5\">\n <Label>Format</Label>\n <Select value={format} onValueChange={(v) => setFormat(v as TcpStoryFormat)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {FORMATS.map((f) => (\n <SelectItem key={f} value={f}>\n {formatEnumLabel(f)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-reading\">Reading time (min)</Label>\n <Input\n id=\"story-reading\"\n type=\"number\"\n min={0}\n value={readingMins}\n onChange={(e) => setReadingMins(e.target.value)}\n placeholder=\"Optional\"\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label>Topic</Label>\n <Select value={pickedTopic} onValueChange={setPickedTopic}>\n <SelectTrigger>\n <SelectValue placeholder=\"Choose a topic (optional)\" />\n </SelectTrigger>\n <SelectContent>\n {(topicsQuery.data?.items ?? []).map((topic) => (\n <SelectItem key={topic.id} value={topic.id}>\n {topic.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-summary\">Summary</Label>\n <Textarea\n id=\"story-summary\"\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n placeholder=\"A short standfirst that introduces the story.\"\n rows={2}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-body\">Body</Label>\n <Textarea\n id=\"story-body\"\n value={body}\n onChange={(e) => setBody(e.target.value)}\n placeholder=\"Write the story… (line breaks are preserved)\"\n rows={5}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-langs\">Languages</Label>\n <TagInput\n id=\"story-langs\"\n value={languages}\n onChange={setLanguages}\n placeholder=\"e.g. English, Tamil…\"\n preserveCase\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-tags\">Tags</Label>\n <TagInput id=\"story-tags\" value={tags} onChange={setTags} placeholder=\"Add tags…\" />\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={createStory.isPending}>\n Create story\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateStoryDialog.js","names":[],"sources":["../../../src/theculturepost/components/CreateStoryDialog.tsx"],"sourcesContent":["/**\n * CreateStoryDialog — draft a new story.\n * @module theculturepost/components\n *\n * Writes `tcpCreateStory`. A wide, two-column composer that mirrors the editorial\n * \"Draft a story\" surface: a real drag-and-drop cover dropzone (uploads to\n * wspace-files-svc and keeps the returned `coverFileId`), a title with an\n * auto-derived URL slug + uniqueness hint, char counters on summary/body, and\n * chip multiselects for languages/tags. New stories begin at the start of the\n * editorial lifecycle (IDEA/DRAFT on the svc). Every field maps to a real\n * `TcpCreateStoryInput` field — no bookmarks, no fabricated metadata.\n */\n\nimport { useRef, useState, type ChangeEvent, type DragEvent } from 'react';\nimport { UploadCloud, Loader2, X, CheckCircle2, FileCheck2 } 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 { TagInput } from './TagInput';\nimport { cn } from '../utils/cn';\nimport { useTcpCreateStory, useTcpTopics, uploadImageToFiles, FILE_FOLDERS } from '../hooks';\nimport { slugify } from '../utils/slug';\nimport { formatEnumLabel } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { TcpStory, TcpStoryFormat } from '../types';\n\nconst FORMATS: TcpStoryFormat[] = [\n 'ARTICLE',\n 'ESSAY',\n 'EXPLAINER',\n 'ORAL_HISTORY',\n 'PHOTO_STORY',\n 'TIMELINE',\n];\n\nconst SUMMARY_MAX = 200;\nconst COVER_MAX_MB = 25;\n\n// ---------------------------------------------------------------------------\n// Cover dropzone (real drag-and-drop → wspace-files-svc)\n// ---------------------------------------------------------------------------\n\ninterface CoverDropzoneProps {\n coverFileId: string | null;\n onUploaded: (fileId: string) => void;\n onCleared: () => void;\n}\n\nfunction CoverDropzone({ coverFileId, onUploaded, onCleared }: CoverDropzoneProps) {\n const inputRef = useRef<HTMLInputElement | null>(null);\n const [uploading, setUploading] = useState(false);\n const [dragActive, setDragActive] = useState(false);\n const [previewUrl, setPreviewUrl] = useState<string | null>(null);\n const [fileName, setFileName] = useState<string | null>(null);\n\n const handleFile = async (file: File) => {\n if (!file.type.startsWith('image/')) {\n notify.error('Unsupported file', 'Please choose an image (JPG or PNG).');\n return;\n }\n if (file.size > COVER_MAX_MB * 1024 * 1024) {\n notify.error('File too large', `Please choose an image under ${COVER_MAX_MB} MB.`);\n return;\n }\n setUploading(true);\n try {\n const uploaded = await uploadImageToFiles(file, FILE_FOLDERS.covers);\n setPreviewUrl(uploaded.url);\n setFileName(uploaded.name);\n onUploaded(uploaded.id);\n notify.success('Cover uploaded', uploaded.name);\n } catch (err) {\n notify.error('Upload failed', describeError(err));\n } finally {\n setUploading(false);\n }\n };\n\n const onInputChange = (event: ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n event.target.value = '';\n if (file) void handleFile(file);\n };\n\n const onDrop = (event: DragEvent<HTMLDivElement>) => {\n event.preventDefault();\n setDragActive(false);\n if (uploading) return;\n const file = event.dataTransfer.files?.[0];\n if (file) void handleFile(file);\n };\n\n const clear = () => {\n setPreviewUrl(null);\n setFileName(null);\n onCleared();\n };\n\n const hiddenInput = (\n <input\n ref={inputRef}\n type=\"file\"\n accept=\"image/*\"\n className=\"sr-only\"\n onChange={onInputChange}\n disabled={uploading}\n aria-label=\"Upload cover image\"\n />\n );\n\n // Uploaded image preview\n if (previewUrl) {\n return (\n <div className=\"relative overflow-hidden rounded-2xl border border-border-seam\">\n {hiddenInput}\n <img src={previewUrl} alt=\"Cover preview\" className=\"max-h-52 w-full object-cover\" />\n <div className=\"absolute right-2 top-2 flex gap-1.5\">\n <button\n type=\"button\"\n onClick={() => inputRef.current?.click()}\n className=\"rounded-full bg-bg-canvas/85 px-2.5 py-1 text-xs font-medium text-text-primary backdrop-blur-sm hover:bg-bg-canvas\"\n >\n Replace\n </button>\n <button\n type=\"button\"\n onClick={clear}\n aria-label=\"Remove cover\"\n className=\"rounded-full bg-bg-canvas/85 p-1.5 text-text-primary backdrop-blur-sm hover:bg-bg-canvas\"\n >\n <X className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n </div>\n );\n }\n\n // Cover set but no local preview (defensive)\n if (coverFileId) {\n return (\n <div className=\"flex items-center justify-between gap-2 rounded-2xl border border-border-seam bg-bg-surface px-3 py-2.5 text-sm\">\n {hiddenInput}\n <span className=\"inline-flex min-w-0 items-center gap-2 text-text-primary\">\n <FileCheck2 className=\"h-4 w-4 shrink-0 text-status-success-text\" aria-hidden=\"true\" />\n <span className=\"truncate\">{fileName ?? 'Cover selected'}</span>\n </span>\n <button\n type=\"button\"\n onClick={clear}\n aria-label=\"Remove cover\"\n className=\"text-text-tertiary hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n );\n }\n\n // Empty dropzone\n return (\n <div\n onDragOver={(event) => {\n event.preventDefault();\n if (!uploading) setDragActive(true);\n }}\n onDragLeave={(event) => {\n event.preventDefault();\n setDragActive(false);\n }}\n onDrop={onDrop}\n className={cn(\n 'rounded-2xl border-2 border-dashed px-4 py-6 text-center transition-colors',\n dragActive\n ? 'border-accent bg-[var(--color-accent-soft)]'\n : 'border-border-seam bg-bg-sunken/40'\n )}\n >\n {hiddenInput}\n {uploading ? (\n <div className=\"flex items-center justify-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"h-5 w-5 animate-spin\" aria-hidden=\"true\" /> Uploading cover…\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center gap-3 sm:flex-row\">\n <UploadCloud className=\"h-8 w-8 text-accent\" aria-hidden=\"true\" />\n <p className=\"text-sm text-text-secondary\">Drag and drop an image here, or</p>\n <Button type=\"button\" size=\"sm\" onClick={() => inputRef.current?.click()}>\n Upload cover\n </Button>\n </div>\n )}\n <p className=\"mt-3 text-xs text-text-tertiary\">\n Recommended: 16:9 ratio, min 1200×675px. JPG or PNG, up to {COVER_MAX_MB} MB.\n </p>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Dialog\n// ---------------------------------------------------------------------------\n\ninterface CreateStoryDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Pre-select a topic (e.g. from a topic detail page). */\n topicId?: string;\n onCreated?: (story: TcpStory) => void;\n}\n\nexport function CreateStoryDialog({\n open,\n onOpenChange,\n topicId,\n onCreated,\n}: CreateStoryDialogProps) {\n const createStory = useTcpCreateStory();\n const topicsQuery = useTcpTopics({ pagination: { limit: 100 } });\n\n const [title, setTitle] = useState('');\n const [slug, setSlug] = useState('');\n const [slugTouched, setSlugTouched] = useState(false);\n const [summary, setSummary] = useState('');\n const [body, setBody] = useState('');\n const [format, setFormat] = useState<TcpStoryFormat>('ARTICLE');\n const [pickedTopic, setPickedTopic] = useState(topicId ?? '');\n const [languages, setLanguages] = useState<string[]>([]);\n const [tags, setTags] = useState<string[]>([]);\n const [readingMins, setReadingMins] = useState('');\n const [coverFileId, setCoverFileId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n // Bumped on reset to force the cover dropzone to drop its local preview state.\n const [resetNonce, setResetNonce] = useState(0);\n\n const effectiveSlug = slugTouched ? slug : slugify(title);\n\n const reset = () => {\n setTitle('');\n setSlug('');\n setSlugTouched(false);\n setSummary('');\n setBody('');\n setFormat('ARTICLE');\n setPickedTopic(topicId ?? '');\n setLanguages([]);\n setTags([]);\n setReadingMins('');\n setCoverFileId(null);\n setError(null);\n setResetNonce((n) => n + 1);\n };\n\n const submit = async () => {\n if (!title.trim()) {\n setError('A title is required.');\n return;\n }\n if (!effectiveSlug) {\n setError('A URL slug is required.');\n return;\n }\n try {\n const created = await createStory.mutateAsync({\n title: title.trim(),\n slug: effectiveSlug,\n summary: summary.trim() || null,\n body: body.trim() || null,\n format,\n topicId: pickedTopic || null,\n languages: languages.length ? languages : null,\n tags: tags.length ? tags : null,\n readingMins: readingMins ? Number(readingMins) : null,\n coverFileId,\n });\n notify.success('Story created', created.title);\n onOpenChange(false);\n reset();\n onCreated?.(created);\n } catch (err) {\n setError(describeError(err));\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-3xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"font-display\">Draft a story</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Begin a new piece — it enters the editorial lifecycle as a draft you can refine and\n publish later.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex max-h-[64vh] flex-col gap-5 overflow-y-auto py-2\">\n {/* Cover image */}\n <div className=\"grid gap-1.5\">\n <Label>Cover image</Label>\n <CoverDropzone\n key={resetNonce}\n coverFileId={coverFileId}\n onUploaded={setCoverFileId}\n onCleared={() => setCoverFileId(null)}\n />\n </div>\n\n {/* Title + slug */}\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-title\">Title</Label>\n <Input\n id=\"story-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. The Weavers of the Coromandel Coast\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-slug\">URL slug</Label>\n <div className=\"relative\">\n <Input\n id=\"story-slug\"\n value={effectiveSlug}\n onChange={(e) => {\n setSlugTouched(true);\n setSlug(slugify(e.target.value));\n }}\n placeholder=\"the-weavers-of-the-coromandel-coast\"\n className={effectiveSlug ? 'pr-9' : undefined}\n />\n {effectiveSlug && (\n <CheckCircle2\n className=\"pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-status-success-text\"\n aria-hidden=\"true\"\n />\n )}\n </div>\n <p className=\"text-xs text-text-tertiary\">\n Auto-derived from the title — used in the story’s URL, so keep it unique.\n </p>\n </div>\n </div>\n\n {/* Format + reading time */}\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label>Format</Label>\n <Select value={format} onValueChange={(v) => setFormat(v as TcpStoryFormat)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {FORMATS.map((f) => (\n <SelectItem key={f} value={f}>\n {formatEnumLabel(f)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-reading\">Reading time (min)</Label>\n <Input\n id=\"story-reading\"\n type=\"number\"\n min={0}\n value={readingMins}\n onChange={(e) => setReadingMins(e.target.value)}\n placeholder=\"Optional\"\n />\n </div>\n </div>\n\n {/* Topic */}\n <div className=\"grid gap-1.5\">\n <Label>Topic</Label>\n <Select value={pickedTopic} onValueChange={setPickedTopic}>\n <SelectTrigger>\n <SelectValue placeholder=\"Choose a topic (optional)\" />\n </SelectTrigger>\n <SelectContent>\n {(topicsQuery.data?.items ?? []).map((topic) => (\n <SelectItem key={topic.id} value={topic.id}>\n {topic.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {/* Summary */}\n <div className=\"grid gap-1.5\">\n <div className=\"flex items-center justify-between gap-2\">\n <Label htmlFor=\"story-summary\">Summary</Label>\n <span className=\"text-xs tabular-nums text-text-tertiary\">\n {summary.length}/{SUMMARY_MAX}\n </span>\n </div>\n <Textarea\n id=\"story-summary\"\n value={summary}\n onChange={(e) => setSummary(e.target.value.slice(0, SUMMARY_MAX))}\n maxLength={SUMMARY_MAX}\n placeholder=\"A short standfirst that introduces the story.\"\n rows={2}\n />\n </div>\n\n {/* Body */}\n <div className=\"grid gap-1.5\">\n <div className=\"flex items-center justify-between gap-2\">\n <Label htmlFor=\"story-body\">Body</Label>\n <span className=\"text-xs tabular-nums text-text-tertiary\">\n {body.length} {body.length === 1 ? 'character' : 'characters'}\n </span>\n </div>\n <Textarea\n id=\"story-body\"\n value={body}\n onChange={(e) => setBody(e.target.value)}\n placeholder=\"Write the story… (line breaks are preserved)\"\n rows={5}\n />\n </div>\n\n {/* Languages + tags */}\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-langs\">Languages</Label>\n <TagInput\n id=\"story-langs\"\n value={languages}\n onChange={setLanguages}\n placeholder=\"e.g. English, Tamil…\"\n preserveCase\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"story-tags\">Tags</Label>\n <TagInput id=\"story-tags\" value={tags} onChange={setTags} placeholder=\"Add tags…\" />\n </div>\n </div>\n\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={createStory.isPending}>\n {createStory.isPending ? (\n <>\n <Loader2 className=\"mr-1.5 h-4 w-4 animate-spin\" aria-hidden=\"true\" /> Creating…\n </>\n ) : (\n 'Create story'\n )}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|