@burdenoff/microfe-theculturepost 2026.704.2 → 2026.705.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/theculturepost/TheCulturePostRoot.js +63 -45
  2. package/dist/theculturepost/TheCulturePostRoot.js.map +1 -1
  3. package/dist/theculturepost/TheCulturePostRoutes.js +17 -9
  4. package/dist/theculturepost/TheCulturePostRoutes.js.map +1 -1
  5. package/dist/theculturepost/components/AddCitationDialog.js +131 -102
  6. package/dist/theculturepost/components/AddCitationDialog.js.map +1 -1
  7. package/dist/theculturepost/components/ArchiveItemCard.js +56 -43
  8. package/dist/theculturepost/components/ArchiveItemCard.js.map +1 -1
  9. package/dist/theculturepost/components/AudioPlayer.js +74 -0
  10. package/dist/theculturepost/components/AudioPlayer.js.map +1 -0
  11. package/dist/theculturepost/components/CitationList.js +215 -105
  12. package/dist/theculturepost/components/CitationList.js.map +1 -1
  13. package/dist/theculturepost/components/CollectionCard.js +32 -32
  14. package/dist/theculturepost/components/CollectionCard.js.map +1 -1
  15. package/dist/theculturepost/components/CreateArchiveItemDialog.js +133 -93
  16. package/dist/theculturepost/components/CreateArchiveItemDialog.js.map +1 -1
  17. package/dist/theculturepost/components/CreateCollectionDialog.js +91 -70
  18. package/dist/theculturepost/components/CreateCollectionDialog.js.map +1 -1
  19. package/dist/theculturepost/components/CreateStoryDialog.js +311 -132
  20. package/dist/theculturepost/components/CreateStoryDialog.js.map +1 -1
  21. package/dist/theculturepost/components/DecorativeCover.js +78 -0
  22. package/dist/theculturepost/components/DecorativeCover.js.map +1 -0
  23. package/dist/theculturepost/components/DecorativeDivider.js +45 -0
  24. package/dist/theculturepost/components/DecorativeDivider.js.map +1 -0
  25. package/dist/theculturepost/components/DiscussionCard.js +75 -60
  26. package/dist/theculturepost/components/DiscussionCard.js.map +1 -1
  27. package/dist/theculturepost/components/HistoryTimeline.js +115 -0
  28. package/dist/theculturepost/components/HistoryTimeline.js.map +1 -0
  29. package/dist/theculturepost/components/LifecycleStepper.js +33 -0
  30. package/dist/theculturepost/components/LifecycleStepper.js.map +1 -0
  31. package/dist/theculturepost/components/OpenDiscussionDialog.js +66 -56
  32. package/dist/theculturepost/components/OpenDiscussionDialog.js.map +1 -1
  33. package/dist/theculturepost/components/OralHistoryCard.js +6 -73
  34. package/dist/theculturepost/components/OverlayBadge.js +22 -0
  35. package/dist/theculturepost/components/OverlayBadge.js.map +1 -0
  36. package/dist/theculturepost/components/PageHero.js +62 -0
  37. package/dist/theculturepost/components/PageHero.js.map +1 -0
  38. package/dist/theculturepost/components/RecordOralHistoryDialog.js +306 -131
  39. package/dist/theculturepost/components/RecordOralHistoryDialog.js.map +1 -1
  40. package/dist/theculturepost/components/ReplyThread.js +268 -139
  41. package/dist/theculturepost/components/ReplyThread.js.map +1 -1
  42. package/dist/theculturepost/components/StatusBadge.js +9 -9
  43. package/dist/theculturepost/components/StatusBadge.js.map +1 -1
  44. package/dist/theculturepost/components/StoryCard.js +54 -49
  45. package/dist/theculturepost/components/StoryCard.js.map +1 -1
  46. package/dist/theculturepost/components/TopicCard.js +60 -41
  47. package/dist/theculturepost/components/TopicCard.js.map +1 -1
  48. package/dist/theculturepost/components/Waveform.js +31 -0
  49. package/dist/theculturepost/components/Waveform.js.map +1 -0
  50. package/dist/theculturepost/components/categoryMeta.js +20 -0
  51. package/dist/theculturepost/components/categoryMeta.js.map +1 -0
  52. package/dist/theculturepost/components/index.js +41 -0
  53. package/dist/theculturepost/components/viz/BarChart.js +53 -0
  54. package/dist/theculturepost/components/viz/BarChart.js.map +1 -0
  55. package/dist/theculturepost/components/viz/Donut.js +58 -0
  56. package/dist/theculturepost/components/viz/Donut.js.map +1 -0
  57. package/dist/theculturepost/components/viz/StatTile.js +35 -0
  58. package/dist/theculturepost/components/viz/StatTile.js.map +1 -0
  59. package/dist/theculturepost/components/viz/index.js +3 -0
  60. package/dist/theculturepost/hooks/useFiles.js +1 -1
  61. package/dist/theculturepost/hooks/useFiles.js.map +1 -1
  62. package/dist/theculturepost/pages/HomePage.js +247 -124
  63. package/dist/theculturepost/pages/HomePage.js.map +1 -1
  64. package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js +319 -127
  65. package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js.map +1 -1
  66. package/dist/theculturepost/pages/archive/ArchivePage.js +265 -180
  67. package/dist/theculturepost/pages/archive/ArchivePage.js.map +1 -1
  68. package/dist/theculturepost/pages/archive/CollectionDetailPage.js +158 -143
  69. package/dist/theculturepost/pages/archive/CollectionDetailPage.js.map +1 -1
  70. package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js +155 -93
  71. package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js.map +1 -1
  72. package/dist/theculturepost/pages/discussions/DiscussionsPage.js +16 -16
  73. package/dist/theculturepost/pages/discussions/DiscussionsPage.js.map +1 -1
  74. package/dist/theculturepost/pages/governance/GovernancePage.js +293 -0
  75. package/dist/theculturepost/pages/governance/GovernancePage.js.map +1 -0
  76. package/dist/theculturepost/pages/stories/StoriesPage.js +18 -18
  77. package/dist/theculturepost/pages/stories/StoriesPage.js.map +1 -1
  78. package/dist/theculturepost/pages/stories/StoryDetailPage.js +603 -285
  79. package/dist/theculturepost/pages/stories/StoryDetailPage.js.map +1 -1
  80. package/dist/theculturepost/pages/timeline/TimelinePage.js +162 -0
  81. package/dist/theculturepost/pages/timeline/TimelinePage.js.map +1 -0
  82. package/dist/theculturepost/pages/topics/TopicDetailPage.js +239 -163
  83. package/dist/theculturepost/pages/topics/TopicDetailPage.js.map +1 -1
  84. package/dist/theculturepost/pages/topics/TopicsPage.js +76 -74
  85. package/dist/theculturepost/pages/topics/TopicsPage.js.map +1 -1
  86. package/dist/theculturepost/styles/injectDisplayFont.js +11 -0
  87. package/dist/theculturepost/styles/injectDisplayFont.js.map +1 -0
  88. package/dist/theculturepost/utils/formatters.js +6 -1
  89. package/dist/theculturepost/utils/formatters.js.map +1 -1
  90. package/package.json +1 -1
  91. package/dist/theculturepost/components/OralHistoryCard.js.map +0 -1
@@ -1,177 +1,353 @@
1
- import { FILE_FOLDERS as e } from "../hooks/useFiles.js";
2
- import { formatEnumLabel as t } from "../utils/formatters.js";
3
- import { useTcpCreateStory as n, useTcpTopics as r } from "../hooks/useTcpApi.js";
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 ee, notify as te } from "../lib/toast.js";
7
- import { FileUploadField as a } from "./FileUploadField.js";
8
- import { TagInput as o } from "./TagInput.js";
9
- import { useState as s } from "react";
10
- import { jsx as c, jsxs as l } from "react/jsx-runtime";
11
- import { Button as u, Input as d, Label as f, ResponsiveDialog as ne, ResponsiveDialogContent as p, ResponsiveDialogDescription as m, ResponsiveDialogFooter as h, ResponsiveDialogHeader as g, ResponsiveDialogTitle as re, Select as _, SelectContent as v, SelectItem as y, SelectTrigger as b, SelectValue as x, Textarea as S } from "@burdenoff/fe-libs/ui";
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 C = [
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 w({ open: w, onOpenChange: T, topicId: E, onCreated: ie }) {
22
- let D = n(), O = r({ pagination: { limit: 100 } }), [k, A] = s(""), [j, M] = s(""), [N, P] = s(!1), [F, I] = s(""), [L, R] = s(""), [z, B] = s("ARTICLE"), [V, H] = s(E ?? ""), [U, W] = s([]), [G, K] = s([]), [q, J] = s(""), [Y, X] = s(null), [Z, Q] = s(null), $ = N ? j : i(k), ae = () => {
23
- A(""), M(""), P(!1), I(""), R(""), B("ARTICLE"), H(E ?? ""), W([]), K([]), J(""), X(null), Q(null);
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__ */ c(ne, {
26
- open: w,
27
- onOpenChange: T,
28
- children: /* @__PURE__ */ l(p, {
29
- className: "sm:max-w-lg",
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__ */ l(g, { children: [/* @__PURE__ */ c(re, { children: "Draft a story" }), /* @__PURE__ */ c(m, { children: "Begin a new piece — it enters the editorial lifecycle as a draft you can refine and publish later." })] }),
32
- /* @__PURE__ */ l("div", {
33
- className: "flex max-h-[62vh] flex-col gap-4 overflow-y-auto py-2",
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__ */ l("div", {
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__ */ c(f, {
50
- htmlFor: "story-title",
51
- children: "Title"
52
- }), /* @__PURE__ */ c(d, {
53
- id: "story-title",
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__ */ l("div", {
60
- className: "grid gap-1.5",
61
- children: [/* @__PURE__ */ c(f, {
62
- htmlFor: "story-slug",
63
- children: "URL slug"
64
- }), /* @__PURE__ */ c(d, {
65
- id: "story-slug",
66
- value: $,
67
- onChange: (e) => {
68
- P(!0), M(i(e.target.value));
69
- },
70
- placeholder: "the-weavers-of-the-coromandel-coast"
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__ */ l("div", {
74
- className: "grid grid-cols-2 gap-3",
75
- children: [/* @__PURE__ */ l("div", {
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__ */ c(f, { children: "Format" }), /* @__PURE__ */ l(_, {
78
- value: z,
79
- onValueChange: (e) => B(e),
80
- children: [/* @__PURE__ */ c(b, { children: /* @__PURE__ */ c(x, {}) }), /* @__PURE__ */ c(v, { children: C.map((e) => /* @__PURE__ */ c(y, {
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: t(e)
235
+ children: r(e)
83
236
  }, e)) })]
84
237
  })]
85
- }), /* @__PURE__ */ l("div", {
238
+ }), /* @__PURE__ */ _("div", {
86
239
  className: "grid gap-1.5",
87
- children: [/* @__PURE__ */ c(f, {
240
+ children: [/* @__PURE__ */ g(b, {
88
241
  htmlFor: "story-reading",
89
242
  children: "Reading time (min)"
90
- }), /* @__PURE__ */ c(d, {
243
+ }), /* @__PURE__ */ g(y, {
91
244
  id: "story-reading",
92
245
  type: "number",
93
246
  min: 0,
94
- value: q,
95
- onChange: (e) => J(e.target.value),
247
+ value: J,
248
+ onChange: (e) => Y(e.target.value),
96
249
  placeholder: "Optional"
97
250
  })]
98
251
  })]
99
252
  }),
100
- /* @__PURE__ */ l("div", {
253
+ /* @__PURE__ */ _("div", {
101
254
  className: "grid gap-1.5",
102
- children: [/* @__PURE__ */ c(f, { children: "Topic" }), /* @__PURE__ */ l(_, {
103
- value: V,
104
- onValueChange: H,
105
- children: [/* @__PURE__ */ c(b, { children: /* @__PURE__ */ c(x, { placeholder: "Choose a topic (optional)" }) }), /* @__PURE__ */ c(v, { children: (O.data?.items ?? []).map((e) => /* @__PURE__ */ c(y, {
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__ */ l("div", {
264
+ /* @__PURE__ */ _("div", {
112
265
  className: "grid gap-1.5",
113
- children: [/* @__PURE__ */ c(f, {
114
- htmlFor: "story-summary",
115
- children: "Summary"
116
- }), /* @__PURE__ */ c(S, {
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: F,
119
- onChange: (e) => I(e.target.value),
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__ */ l("div", {
288
+ /* @__PURE__ */ _("div", {
125
289
  className: "grid gap-1.5",
126
- children: [/* @__PURE__ */ c(f, {
127
- htmlFor: "story-body",
128
- children: "Body"
129
- }), /* @__PURE__ */ c(S, {
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: L,
132
- onChange: (e) => R(e.target.value),
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__ */ l("div", {
138
- className: "grid gap-1.5",
139
- children: [/* @__PURE__ */ c(f, {
140
- htmlFor: "story-langs",
141
- children: "Languages"
142
- }), /* @__PURE__ */ c(o, {
143
- id: "story-langs",
144
- value: U,
145
- onChange: W,
146
- placeholder: "e.g. English, Tamil…",
147
- preserveCase: !0
148
- })]
149
- }),
150
- /* @__PURE__ */ l("div", {
151
- className: "grid gap-1.5",
152
- children: [/* @__PURE__ */ c(f, {
153
- htmlFor: "story-tags",
154
- children: "Tags"
155
- }), /* @__PURE__ */ c(o, {
156
- id: "story-tags",
157
- value: G,
158
- onChange: K,
159
- placeholder: "Add tags…"
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
- Z && /* @__PURE__ */ c("p", {
338
+ se && /* @__PURE__ */ g("p", {
163
339
  className: "text-sm text-status-error-text",
164
- children: Z
340
+ children: se
165
341
  })
166
342
  ]
167
343
  }),
168
- /* @__PURE__ */ l(h, { children: [/* @__PURE__ */ c(u, {
344
+ /* @__PURE__ */ _(w, { children: [/* @__PURE__ */ g(v, {
169
345
  variant: "outline",
170
- onClick: () => T(!1),
346
+ onClick: () => t(!1),
171
347
  children: "Cancel"
172
- }), /* @__PURE__ */ c(u, {
348
+ }), /* @__PURE__ */ g(v, {
173
349
  onClick: async () => {
174
- if (!k.trim()) {
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 D.mutateAsync({
184
- title: k.trim(),
359
+ let e = await d.mutateAsync({
360
+ title: m.trim(),
185
361
  slug: $,
186
- summary: F.trim() || null,
187
- body: L.trim() || null,
188
- format: z,
189
- topicId: V || null,
190
- languages: U.length ? U : null,
191
- tags: G.length ? G : null,
192
- readingMins: q ? Number(q) : null,
193
- coverFileId: Y
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
- te.success("Story created", e.title), T(!1), ae(), ie?.(e);
371
+ o.success("Story created", e.title), t(!1), ue(), c?.(e);
196
372
  } catch (e) {
197
- Q(ee(e));
373
+ Q(a(e));
198
374
  }
199
375
  },
200
- disabled: D.isPending,
201
- children: "Create story"
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 { w as CreateStoryDialog };
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&rsquo;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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"}