@burdenoff/microfe-theculturepost 2026.1112.2 → 2026.1113.1

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.
@@ -1,4 +1,12 @@
1
- import { TcpArchiveItem } from '../types';
1
+ import { TcpArchiveItem, TcpArchiveItemKind, TcpAccessLevel } from '../types';
2
+ export declare const KINDS: TcpArchiveItemKind[];
3
+ export declare const ACCESS_LEVELS: TcpAccessLevel[];
4
+ export declare const ACCESS_HELP: Record<TcpAccessLevel, string>;
5
+ export declare const TITLE_MAX = 160;
6
+ export declare const DESCRIPTION_MAX = 600;
7
+ export declare const PROVENANCE_MAX = 600;
8
+ export declare const ORIGIN_MAX = 160;
9
+ export declare const dateToIso: (value: string) => string | null;
2
10
  interface CreateArchiveItemDialogProps {
3
11
  open: boolean;
4
12
  onOpenChange: (open: boolean) => void;
@@ -1,15 +1,15 @@
1
1
  import { FILE_FOLDERS as e } from "../hooks/useFiles.js";
2
2
  import { TagInput as t } from "./TagInput.js";
3
- import { useTcpCollections as ee, useTcpCreateArchiveItem as te } from "../hooks/useTcpApi.js";
4
- import { Counter as n, MediaDropzone as ne } from "./RecordOralHistoryDialog.js";
3
+ import { useTcpCollections as ee, useTcpCreateArchiveItem as n } from "../hooks/useTcpApi.js";
4
+ import { Counter as r, MediaDropzone as te } from "./RecordOralHistoryDialog.js";
5
5
  import "../hooks/index.js";
6
- import { formatEnumLabel as r } from "../utils/formatters.js";
7
- import { describeError as i, notify as a } from "../lib/toast.js";
8
- import { useState as o } from "react";
9
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
10
- import { Button as l, Input as u, Label as d, ResponsiveDialog as re, ResponsiveDialogContent as ie, ResponsiveDialogDescription as ae, ResponsiveDialogFooter as f, ResponsiveDialogHeader as p, ResponsiveDialogTitle as oe, Select as m, SelectContent as h, SelectItem as g, SelectTrigger as _, SelectValue as v, Textarea as y } from "@burdenoff/fe-libs/ui";
6
+ import { formatEnumLabel as i } from "../utils/formatters.js";
7
+ import { describeError as a, notify as o } from "../lib/toast.js";
8
+ import { useState as s } from "react";
9
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
10
+ import { Button as u, Input as d, Label as f, ResponsiveDialog as ne, ResponsiveDialogContent as p, ResponsiveDialogDescription as re, ResponsiveDialogFooter as ie, ResponsiveDialogHeader as ae, ResponsiveDialogTitle as m, Select as h, SelectContent as g, SelectItem as _, SelectTrigger as v, SelectValue as y, Textarea as b } from "@burdenoff/fe-libs/ui";
11
11
  // src/theculturepost/components/CreateArchiveItemDialog.tsx
12
- var se = [
12
+ var x = [
13
13
  "DOCUMENT",
14
14
  "MANUSCRIPT",
15
15
  "PHOTOGRAPH",
@@ -17,38 +17,38 @@ var se = [
17
17
  "VIDEO",
18
18
  "MAP",
19
19
  "OBJECT"
20
- ], ce = [
20
+ ], S = [
21
21
  "PUBLIC",
22
22
  "COMMUNITY",
23
23
  "RESTRICTED",
24
24
  "EMBARGOED"
25
- ], b = {
25
+ ], C = {
26
26
  PUBLIC: "Visible to everyone.",
27
27
  COMMUNITY: "Available to community members.",
28
28
  RESTRICTED: "Visible to selected groups.",
29
29
  EMBARGOED: "Hidden until a future date."
30
- }, x = 160, S = 600, C = 600, le = 160, ue = (e) => {
30
+ }, w = (e) => {
31
31
  if (!e) return null;
32
32
  let t = new Date(e);
33
33
  return Number.isNaN(t.getTime()) ? null : t.toISOString();
34
34
  };
35
- function w({ open: w, onOpenChange: T, collectionId: E, onCreated: D }) {
36
- let O = te(), k = ee({ limit: 100 }), [A, j] = o(""), [M, N] = o(""), [P, F] = o("DOCUMENT"), [I, L] = o("PUBLIC"), [R, z] = o(E ?? ""), [B, V] = o(null), [H, U] = o(""), [W, G] = o(""), [K, q] = o([]), [J, Y] = o([]), [X, Z] = o(""), [Q, $] = o(null), de = () => {
37
- j(""), N(""), F("DOCUMENT"), L("PUBLIC"), z(E ?? ""), V(null), U(""), G(""), q([]), Y([]), Z(""), $(null);
35
+ function T({ open: T, onOpenChange: E, collectionId: D, onCreated: oe }) {
36
+ let O = n(), k = ee({ limit: 100 }), [A, j] = s(""), [M, N] = s(""), [P, F] = s("DOCUMENT"), [I, L] = s("PUBLIC"), [R, z] = s(D ?? ""), [B, V] = s(null), [H, U] = s(""), [W, G] = s(""), [K, q] = s([]), [J, Y] = s([]), [X, Z] = s(""), [Q, $] = s(null), se = () => {
37
+ j(""), N(""), F("DOCUMENT"), L("PUBLIC"), z(D ?? ""), V(null), U(""), G(""), q([]), Y([]), Z(""), $(null);
38
38
  };
39
- return /* @__PURE__ */ s(re, {
40
- open: w,
41
- onOpenChange: T,
42
- children: /* @__PURE__ */ c(ie, {
39
+ return /* @__PURE__ */ c(ne, {
40
+ open: T,
41
+ onOpenChange: E,
42
+ children: /* @__PURE__ */ l(p, {
43
43
  className: "sm:max-w-2xl",
44
44
  children: [
45
- /* @__PURE__ */ c(p, { children: [/* @__PURE__ */ s(oe, { children: "Add an artifact" }), /* @__PURE__ */ s(ae, { children: "Catalogue a manuscript, photograph, recording, map, object or document with its provenance." })] }),
46
- /* @__PURE__ */ c("div", {
45
+ /* @__PURE__ */ l(ae, { children: [/* @__PURE__ */ c(m, { children: "Add an artifact" }), /* @__PURE__ */ c(re, { children: "Catalogue a manuscript, photograph, recording, map, object or document with its provenance." })] }),
46
+ /* @__PURE__ */ l("div", {
47
47
  className: "flex max-h-[64vh] flex-col gap-4 overflow-y-auto py-2",
48
48
  children: [
49
- /* @__PURE__ */ c("div", {
49
+ /* @__PURE__ */ l("div", {
50
50
  className: "grid gap-1.5",
51
- children: [/* @__PURE__ */ s(d, { children: "File" }), /* @__PURE__ */ s(ne, {
51
+ children: [/* @__PURE__ */ c(f, { children: "File" }), /* @__PURE__ */ c(te, {
52
52
  folder: e.artifacts,
53
53
  accept: "image/*,audio/*,video/*,application/pdf",
54
54
  promptLabel: "Drag and drop the artifact file here",
@@ -58,111 +58,111 @@ function w({ open: w, onOpenChange: T, collectionId: E, onCreated: D }) {
58
58
  onClear: () => V(null)
59
59
  })]
60
60
  }),
61
- /* @__PURE__ */ c("div", {
61
+ /* @__PURE__ */ l("div", {
62
62
  className: "grid gap-1.5",
63
63
  children: [
64
- /* @__PURE__ */ c(d, {
64
+ /* @__PURE__ */ l(f, {
65
65
  htmlFor: "item-title",
66
- children: ["Title ", /* @__PURE__ */ s("span", {
66
+ children: ["Title ", /* @__PURE__ */ c("span", {
67
67
  className: "text-status-error-text",
68
68
  children: "*"
69
69
  })]
70
70
  }),
71
- /* @__PURE__ */ s(u, {
71
+ /* @__PURE__ */ c(d, {
72
72
  id: "item-title",
73
73
  value: A,
74
- maxLength: x,
74
+ maxLength: 160,
75
75
  onChange: (e) => j(e.target.value),
76
76
  placeholder: "e.g. Palm-leaf manuscript, 18th c."
77
77
  }),
78
- /* @__PURE__ */ s(n, {
78
+ /* @__PURE__ */ c(r, {
79
79
  length: A.length,
80
- max: x
80
+ max: 160
81
81
  })
82
82
  ]
83
83
  }),
84
- /* @__PURE__ */ c("div", {
84
+ /* @__PURE__ */ l("div", {
85
85
  className: "grid gap-3 sm:grid-cols-2",
86
- children: [/* @__PURE__ */ c("div", {
86
+ children: [/* @__PURE__ */ l("div", {
87
87
  className: "grid gap-1.5",
88
- children: [/* @__PURE__ */ s(d, { children: "Kind" }), /* @__PURE__ */ c(m, {
88
+ children: [/* @__PURE__ */ c(f, { children: "Kind" }), /* @__PURE__ */ l(h, {
89
89
  value: P,
90
90
  onValueChange: (e) => F(e),
91
- children: [/* @__PURE__ */ s(_, { children: /* @__PURE__ */ s(v, {}) }), /* @__PURE__ */ s(h, { children: se.map((e) => /* @__PURE__ */ s(g, {
91
+ children: [/* @__PURE__ */ c(v, { children: /* @__PURE__ */ c(y, {}) }), /* @__PURE__ */ c(g, { children: x.map((e) => /* @__PURE__ */ c(_, {
92
92
  value: e,
93
- children: r(e)
93
+ children: i(e)
94
94
  }, e)) })]
95
95
  })]
96
- }), /* @__PURE__ */ c("div", {
96
+ }), /* @__PURE__ */ l("div", {
97
97
  className: "grid gap-1.5",
98
98
  children: [
99
- /* @__PURE__ */ s(d, { children: "Access level" }),
100
- /* @__PURE__ */ c(m, {
99
+ /* @__PURE__ */ c(f, { children: "Access level" }),
100
+ /* @__PURE__ */ l(h, {
101
101
  value: I,
102
102
  onValueChange: (e) => L(e),
103
- children: [/* @__PURE__ */ s(_, { children: /* @__PURE__ */ s(v, {}) }), /* @__PURE__ */ s(h, { children: ce.map((e) => /* @__PURE__ */ s(g, {
103
+ children: [/* @__PURE__ */ c(v, { children: /* @__PURE__ */ c(y, {}) }), /* @__PURE__ */ c(g, { children: S.map((e) => /* @__PURE__ */ c(_, {
104
104
  value: e,
105
- children: r(e)
105
+ children: i(e)
106
106
  }, e)) })]
107
107
  }),
108
- /* @__PURE__ */ s("p", {
108
+ /* @__PURE__ */ c("p", {
109
109
  className: "text-xs text-text-tertiary",
110
- children: b[I]
110
+ children: C[I]
111
111
  })
112
112
  ]
113
113
  })]
114
114
  }),
115
- !E && /* @__PURE__ */ c("div", {
115
+ !D && /* @__PURE__ */ l("div", {
116
116
  className: "grid gap-1.5",
117
- children: [/* @__PURE__ */ s(d, { children: "Collection" }), /* @__PURE__ */ c(m, {
117
+ children: [/* @__PURE__ */ c(f, { children: "Collection" }), /* @__PURE__ */ l(h, {
118
118
  value: R,
119
119
  onValueChange: z,
120
- children: [/* @__PURE__ */ s(_, { children: /* @__PURE__ */ s(v, { placeholder: "Add to a collection (optional)" }) }), /* @__PURE__ */ s(h, { children: (k.data ?? []).map((e) => /* @__PURE__ */ s(g, {
120
+ children: [/* @__PURE__ */ c(v, { children: /* @__PURE__ */ c(y, { placeholder: "Add to a collection (optional)" }) }), /* @__PURE__ */ c(g, { children: (k.data ?? []).map((e) => /* @__PURE__ */ c(_, {
121
121
  value: e.id,
122
122
  children: e.name
123
123
  }, e.id)) })]
124
124
  })]
125
125
  }),
126
- /* @__PURE__ */ c("div", {
126
+ /* @__PURE__ */ l("div", {
127
127
  className: "grid gap-1.5",
128
128
  children: [
129
- /* @__PURE__ */ s(d, {
129
+ /* @__PURE__ */ c(f, {
130
130
  htmlFor: "item-desc",
131
131
  children: "Description"
132
132
  }),
133
- /* @__PURE__ */ s(y, {
133
+ /* @__PURE__ */ c(b, {
134
134
  id: "item-desc",
135
135
  value: M,
136
- maxLength: S,
136
+ maxLength: 600,
137
137
  onChange: (e) => N(e.target.value),
138
138
  rows: 2
139
139
  }),
140
- /* @__PURE__ */ s(n, {
140
+ /* @__PURE__ */ c(r, {
141
141
  length: M.length,
142
- max: S
142
+ max: 600
143
143
  })
144
144
  ]
145
145
  }),
146
- /* @__PURE__ */ c("div", {
146
+ /* @__PURE__ */ l("div", {
147
147
  className: "grid gap-3 sm:grid-cols-2",
148
- children: [/* @__PURE__ */ c("div", {
148
+ children: [/* @__PURE__ */ l("div", {
149
149
  className: "grid gap-1.5",
150
- children: [/* @__PURE__ */ s(d, {
150
+ children: [/* @__PURE__ */ c(f, {
151
151
  htmlFor: "item-origin",
152
152
  children: "Origin"
153
- }), /* @__PURE__ */ s(u, {
153
+ }), /* @__PURE__ */ c(d, {
154
154
  id: "item-origin",
155
155
  value: W,
156
- maxLength: le,
156
+ maxLength: 160,
157
157
  onChange: (e) => G(e.target.value),
158
158
  placeholder: "Place of origin"
159
159
  })]
160
- }), /* @__PURE__ */ c("div", {
160
+ }), /* @__PURE__ */ l("div", {
161
161
  className: "grid gap-1.5",
162
- children: [/* @__PURE__ */ s(d, {
162
+ children: [/* @__PURE__ */ c(f, {
163
163
  htmlFor: "item-dated",
164
164
  children: "Dated"
165
- }), /* @__PURE__ */ s(u, {
165
+ }), /* @__PURE__ */ c(d, {
166
166
  id: "item-dated",
167
167
  type: "date",
168
168
  value: X,
@@ -170,33 +170,33 @@ function w({ open: w, onOpenChange: T, collectionId: E, onCreated: D }) {
170
170
  })]
171
171
  })]
172
172
  }),
173
- /* @__PURE__ */ c("div", {
173
+ /* @__PURE__ */ l("div", {
174
174
  className: "grid gap-1.5",
175
175
  children: [
176
- /* @__PURE__ */ s(d, {
176
+ /* @__PURE__ */ c(f, {
177
177
  htmlFor: "item-provenance",
178
178
  children: "Provenance"
179
179
  }),
180
- /* @__PURE__ */ s(y, {
180
+ /* @__PURE__ */ c(b, {
181
181
  id: "item-provenance",
182
182
  value: H,
183
- maxLength: C,
183
+ maxLength: 600,
184
184
  onChange: (e) => U(e.target.value),
185
185
  placeholder: "Chain of custody / source history",
186
186
  rows: 2
187
187
  }),
188
- /* @__PURE__ */ s(n, {
188
+ /* @__PURE__ */ c(r, {
189
189
  length: H.length,
190
- max: C
190
+ max: 600
191
191
  })
192
192
  ]
193
193
  }),
194
- /* @__PURE__ */ c("div", {
194
+ /* @__PURE__ */ l("div", {
195
195
  className: "grid gap-1.5",
196
- children: [/* @__PURE__ */ s(d, {
196
+ children: [/* @__PURE__ */ c(f, {
197
197
  htmlFor: "item-langs",
198
198
  children: "Languages"
199
- }), /* @__PURE__ */ s(t, {
199
+ }), /* @__PURE__ */ c(t, {
200
200
  id: "item-langs",
201
201
  value: K,
202
202
  onChange: q,
@@ -204,29 +204,29 @@ function w({ open: w, onOpenChange: T, collectionId: E, onCreated: D }) {
204
204
  preserveCase: !0
205
205
  })]
206
206
  }),
207
- /* @__PURE__ */ c("div", {
207
+ /* @__PURE__ */ l("div", {
208
208
  className: "grid gap-1.5",
209
- children: [/* @__PURE__ */ s(d, {
209
+ children: [/* @__PURE__ */ c(f, {
210
210
  htmlFor: "item-tags",
211
211
  children: "Tags"
212
- }), /* @__PURE__ */ s(t, {
212
+ }), /* @__PURE__ */ c(t, {
213
213
  id: "item-tags",
214
214
  value: J,
215
215
  onChange: Y,
216
216
  placeholder: "Add tags…"
217
217
  })]
218
218
  }),
219
- Q && /* @__PURE__ */ s("p", {
219
+ Q && /* @__PURE__ */ c("p", {
220
220
  className: "text-sm text-status-error-text",
221
221
  children: Q
222
222
  })
223
223
  ]
224
224
  }),
225
- /* @__PURE__ */ c(f, { children: [/* @__PURE__ */ s(l, {
225
+ /* @__PURE__ */ l(ie, { children: [/* @__PURE__ */ c(u, {
226
226
  variant: "outline",
227
- onClick: () => T(!1),
227
+ onClick: () => E(!1),
228
228
  children: "Cancel"
229
- }), /* @__PURE__ */ s(l, {
229
+ }), /* @__PURE__ */ c(u, {
230
230
  onClick: async () => {
231
231
  if (!A.trim()) $("A title is required.");
232
232
  else try {
@@ -241,11 +241,11 @@ function w({ open: w, onOpenChange: T, collectionId: E, onCreated: D }) {
241
241
  origin: W.trim() || null,
242
242
  languages: K.length ? K : null,
243
243
  tags: J.length ? J : null,
244
- datedAt: ue(X)
244
+ datedAt: w(X)
245
245
  });
246
- a.success("Artifact added", e.title), T(!1), de(), D?.(e);
246
+ o.success("Artifact added", e.title), E(!1), se(), oe?.(e);
247
247
  } catch (e) {
248
- $(i(e));
248
+ $(a(e));
249
249
  }
250
250
  },
251
251
  disabled: O.isPending,
@@ -255,6 +255,6 @@ function w({ open: w, onOpenChange: T, collectionId: E, onCreated: D }) {
255
255
  })
256
256
  });
257
257
  }
258
- export { w as CreateArchiveItemDialog };
258
+ export { C as ACCESS_HELP, S as ACCESS_LEVELS, T as CreateArchiveItemDialog, x as KINDS, w as dateToIso };
259
259
 
260
260
  //# sourceMappingURL=CreateArchiveItemDialog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CreateArchiveItemDialog.js","names":["KINDS","ACCESS_LEVELS","ACCESS_HELP","TITLE_MAX","DESCRIPTION_MAX","PROVENANCE_MAX","ORIGIN_MAX","dateToIso","value","d","CreateArchiveItemDialog","createItem","useTcpCreateArchiveItem","collectionsQuery","useTcpCollections","title","setTitle","useState","description","setDescription","kind","setKind","accessLevel","setAccessLevel","pickedCollection","setPickedCollection","collectionId","file","setFile","provenance","setProvenance","origin","setOrigin","languages","setLanguages","tags","setTags","datedAt","setDatedAt","error","setError","reset","ResponsiveDialog","ResponsiveDialogContent","ResponsiveDialogHeader","ResponsiveDialogTitle","ResponsiveDialogDescription","Label","MediaDropzone","FILE_FOLDERS","ref","Input","Counter","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","k","formatEnumLabel","a","collection","Textarea","TagInput","ResponsiveDialogFooter","Button","onOpenChange","created","notify","onCreated","err","describeError"],"sources":["../../../src/theculturepost/components/CreateArchiveItemDialog.tsx"],"sourcesContent":["/**\n * CreateArchiveItemDialog — add an artifact to the archive.\n * @module theculturepost/components\n *\n * Writes `tcpCreateArchiveItem`. The binary is uploaded to wspace-files-svc via\n * the shared {@link MediaDropzone} (drag-drop) and only its id is stored here.\n * The schema exposes a SINGLE `file` per item — never a gallery. Kind + access\n * level are chosen up front.\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 { MediaDropzone, Counter, type UploadedRef } from './RecordOralHistoryDialog';\nimport { useTcpCreateArchiveItem, useTcpCollections, FILE_FOLDERS } from '../hooks';\nimport { formatEnumLabel } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { TcpArchiveItem, TcpArchiveItemKind, TcpAccessLevel } from '../types';\n\nconst KINDS: TcpArchiveItemKind[] = [\n 'DOCUMENT',\n 'MANUSCRIPT',\n 'PHOTOGRAPH',\n 'AUDIO',\n 'VIDEO',\n 'MAP',\n 'OBJECT',\n];\n\nconst ACCESS_LEVELS: TcpAccessLevel[] = ['PUBLIC', 'COMMUNITY', 'RESTRICTED', 'EMBARGOED'];\n\nconst ACCESS_HELP: Record<TcpAccessLevel, string> = {\n PUBLIC: 'Visible to everyone.',\n COMMUNITY: 'Available to community members.',\n RESTRICTED: 'Visible to selected groups.',\n EMBARGOED: 'Hidden until a future date.',\n};\n\nconst TITLE_MAX = 160;\nconst DESCRIPTION_MAX = 600;\nconst PROVENANCE_MAX = 600;\nconst ORIGIN_MAX = 160;\n\nconst dateToIso = (value: string): string | null => {\n if (!value) return null;\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d.toISOString();\n};\n\ninterface CreateArchiveItemDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Pre-select a collection (e.g. from a collection detail page). */\n collectionId?: string;\n onCreated?: (item: TcpArchiveItem) => void;\n}\n\nexport function CreateArchiveItemDialog({\n open,\n onOpenChange,\n collectionId,\n onCreated,\n}: CreateArchiveItemDialogProps) {\n const createItem = useTcpCreateArchiveItem();\n const collectionsQuery = useTcpCollections({ limit: 100 });\n\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [kind, setKind] = useState<TcpArchiveItemKind>('DOCUMENT');\n const [accessLevel, setAccessLevel] = useState<TcpAccessLevel>('PUBLIC');\n const [pickedCollection, setPickedCollection] = useState(collectionId ?? '');\n const [file, setFile] = useState<UploadedRef | null>(null);\n const [provenance, setProvenance] = useState('');\n const [origin, setOrigin] = useState('');\n const [languages, setLanguages] = useState<string[]>([]);\n const [tags, setTags] = useState<string[]>([]);\n const [datedAt, setDatedAt] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const reset = () => {\n setTitle('');\n setDescription('');\n setKind('DOCUMENT');\n setAccessLevel('PUBLIC');\n setPickedCollection(collectionId ?? '');\n setFile(null);\n setProvenance('');\n setOrigin('');\n setLanguages([]);\n setTags([]);\n setDatedAt('');\n setError(null);\n };\n\n const submit = async () => {\n if (!title.trim()) {\n setError('A title is required.');\n return;\n }\n try {\n const created = await createItem.mutateAsync({\n title: title.trim(),\n description: description.trim() || null,\n kind,\n accessLevel,\n collectionId: pickedCollection || null,\n fileId: file?.id ?? null,\n provenance: provenance.trim() || null,\n origin: origin.trim() || null,\n languages: languages.length ? languages : null,\n tags: tags.length ? tags : null,\n datedAt: dateToIso(datedAt),\n });\n notify.success('Artifact added', 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-2xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add an artifact</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Catalogue a manuscript, photograph, recording, map, object or document with its\n provenance.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex max-h-[64vh] flex-col gap-4 overflow-y-auto py-2\">\n <div className=\"grid gap-1.5\">\n <Label>File</Label>\n <MediaDropzone\n folder={FILE_FOLDERS.artifacts}\n accept=\"image/*,audio/*,video/*,application/pdf\"\n promptLabel=\"Drag and drop the artifact file here\"\n hint=\"Image, audio, video or PDF, up to 25 MB. One file per artifact.\"\n value={file}\n onUploaded={(ref) => setFile(ref)}\n onClear={() => setFile(null)}\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-title\">\n Title <span className=\"text-status-error-text\">*</span>\n </Label>\n <Input\n id=\"item-title\"\n value={title}\n maxLength={TITLE_MAX}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. Palm-leaf manuscript, 18th c.\"\n />\n <Counter length={title.length} max={TITLE_MAX} />\n </div>\n\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label>Kind</Label>\n <Select value={kind} onValueChange={(v) => setKind(v as TcpArchiveItemKind)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {KINDS.map((k) => (\n <SelectItem key={k} value={k}>\n {formatEnumLabel(k)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label>Access level</Label>\n <Select\n value={accessLevel}\n onValueChange={(v) => setAccessLevel(v as TcpAccessLevel)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ACCESS_LEVELS.map((a) => (\n <SelectItem key={a} value={a}>\n {formatEnumLabel(a)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <p className=\"text-xs text-text-tertiary\">{ACCESS_HELP[accessLevel]}</p>\n </div>\n </div>\n\n {!collectionId && (\n <div className=\"grid gap-1.5\">\n <Label>Collection</Label>\n <Select value={pickedCollection} onValueChange={setPickedCollection}>\n <SelectTrigger>\n <SelectValue placeholder=\"Add to a collection (optional)\" />\n </SelectTrigger>\n <SelectContent>\n {(collectionsQuery.data ?? []).map((collection) => (\n <SelectItem key={collection.id} value={collection.id}>\n {collection.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-desc\">Description</Label>\n <Textarea\n id=\"item-desc\"\n value={description}\n maxLength={DESCRIPTION_MAX}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n <Counter length={description.length} max={DESCRIPTION_MAX} />\n </div>\n\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-origin\">Origin</Label>\n <Input\n id=\"item-origin\"\n value={origin}\n maxLength={ORIGIN_MAX}\n onChange={(e) => setOrigin(e.target.value)}\n placeholder=\"Place of origin\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-dated\">Dated</Label>\n <Input\n id=\"item-dated\"\n type=\"date\"\n value={datedAt}\n onChange={(e) => setDatedAt(e.target.value)}\n />\n </div>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-provenance\">Provenance</Label>\n <Textarea\n id=\"item-provenance\"\n value={provenance}\n maxLength={PROVENANCE_MAX}\n onChange={(e) => setProvenance(e.target.value)}\n placeholder=\"Chain of custody / source history\"\n rows={2}\n />\n <Counter length={provenance.length} max={PROVENANCE_MAX} />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-langs\">Languages</Label>\n <TagInput\n id=\"item-langs\"\n value={languages}\n onChange={setLanguages}\n placeholder=\"e.g. Tamil, Sanskrit…\"\n preserveCase\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-tags\">Tags</Label>\n <TagInput id=\"item-tags\" value={tags} onChange={setTags} placeholder=\"Add tags…\" />\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={createItem.isPending}>\n Add artifact\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CreateArchiveItemDialog.js","names":["KINDS","ACCESS_LEVELS","ACCESS_HELP","dateToIso","value","d","CreateArchiveItemDialog","createItem","useTcpCreateArchiveItem","collectionsQuery","useTcpCollections","title","setTitle","useState","description","setDescription","kind","setKind","accessLevel","setAccessLevel","pickedCollection","setPickedCollection","collectionId","file","setFile","provenance","setProvenance","origin","setOrigin","languages","setLanguages","tags","setTags","datedAt","setDatedAt","error","setError","reset","ResponsiveDialog","ResponsiveDialogContent","ResponsiveDialogHeader","ResponsiveDialogTitle","ResponsiveDialogDescription","Label","MediaDropzone","FILE_FOLDERS","ref","Input","Counter","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","k","formatEnumLabel","a","collection","Textarea","TagInput","ResponsiveDialogFooter","Button","onOpenChange","created","notify","onCreated","err","describeError"],"sources":["../../../src/theculturepost/components/CreateArchiveItemDialog.tsx"],"sourcesContent":["/**\n * CreateArchiveItemDialog — add an artifact to the archive.\n * @module theculturepost/components\n *\n * Writes `tcpCreateArchiveItem`. The binary is uploaded to wspace-files-svc via\n * the shared {@link MediaDropzone} (drag-drop) and only its id is stored here.\n * The schema exposes a SINGLE `file` per item — never a gallery. Kind + access\n * level are chosen up front.\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 { MediaDropzone, Counter, type UploadedRef } from './RecordOralHistoryDialog';\nimport { useTcpCreateArchiveItem, useTcpCollections, FILE_FOLDERS } from '../hooks';\nimport { formatEnumLabel } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { TcpArchiveItem, TcpArchiveItemKind, TcpAccessLevel } from '../types';\n\nexport const KINDS: TcpArchiveItemKind[] = [\n 'DOCUMENT',\n 'MANUSCRIPT',\n 'PHOTOGRAPH',\n 'AUDIO',\n 'VIDEO',\n 'MAP',\n 'OBJECT',\n];\n\nexport const ACCESS_LEVELS: TcpAccessLevel[] = ['PUBLIC', 'COMMUNITY', 'RESTRICTED', 'EMBARGOED'];\n\nexport const ACCESS_HELP: Record<TcpAccessLevel, string> = {\n PUBLIC: 'Visible to everyone.',\n COMMUNITY: 'Available to community members.',\n RESTRICTED: 'Visible to selected groups.',\n EMBARGOED: 'Hidden until a future date.',\n};\n\nexport const TITLE_MAX = 160;\nexport const DESCRIPTION_MAX = 600;\nexport const PROVENANCE_MAX = 600;\nexport const ORIGIN_MAX = 160;\n\nexport const dateToIso = (value: string): string | null => {\n if (!value) return null;\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d.toISOString();\n};\n\ninterface CreateArchiveItemDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Pre-select a collection (e.g. from a collection detail page). */\n collectionId?: string;\n onCreated?: (item: TcpArchiveItem) => void;\n}\n\nexport function CreateArchiveItemDialog({\n open,\n onOpenChange,\n collectionId,\n onCreated,\n}: CreateArchiveItemDialogProps) {\n const createItem = useTcpCreateArchiveItem();\n const collectionsQuery = useTcpCollections({ limit: 100 });\n\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [kind, setKind] = useState<TcpArchiveItemKind>('DOCUMENT');\n const [accessLevel, setAccessLevel] = useState<TcpAccessLevel>('PUBLIC');\n const [pickedCollection, setPickedCollection] = useState(collectionId ?? '');\n const [file, setFile] = useState<UploadedRef | null>(null);\n const [provenance, setProvenance] = useState('');\n const [origin, setOrigin] = useState('');\n const [languages, setLanguages] = useState<string[]>([]);\n const [tags, setTags] = useState<string[]>([]);\n const [datedAt, setDatedAt] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const reset = () => {\n setTitle('');\n setDescription('');\n setKind('DOCUMENT');\n setAccessLevel('PUBLIC');\n setPickedCollection(collectionId ?? '');\n setFile(null);\n setProvenance('');\n setOrigin('');\n setLanguages([]);\n setTags([]);\n setDatedAt('');\n setError(null);\n };\n\n const submit = async () => {\n if (!title.trim()) {\n setError('A title is required.');\n return;\n }\n try {\n const created = await createItem.mutateAsync({\n title: title.trim(),\n description: description.trim() || null,\n kind,\n accessLevel,\n collectionId: pickedCollection || null,\n fileId: file?.id ?? null,\n provenance: provenance.trim() || null,\n origin: origin.trim() || null,\n languages: languages.length ? languages : null,\n tags: tags.length ? tags : null,\n datedAt: dateToIso(datedAt),\n });\n notify.success('Artifact added', 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-2xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add an artifact</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Catalogue a manuscript, photograph, recording, map, object or document with its\n provenance.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex max-h-[64vh] flex-col gap-4 overflow-y-auto py-2\">\n <div className=\"grid gap-1.5\">\n <Label>File</Label>\n <MediaDropzone\n folder={FILE_FOLDERS.artifacts}\n accept=\"image/*,audio/*,video/*,application/pdf\"\n promptLabel=\"Drag and drop the artifact file here\"\n hint=\"Image, audio, video or PDF, up to 25 MB. One file per artifact.\"\n value={file}\n onUploaded={(ref) => setFile(ref)}\n onClear={() => setFile(null)}\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-title\">\n Title <span className=\"text-status-error-text\">*</span>\n </Label>\n <Input\n id=\"item-title\"\n value={title}\n maxLength={TITLE_MAX}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. Palm-leaf manuscript, 18th c.\"\n />\n <Counter length={title.length} max={TITLE_MAX} />\n </div>\n\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label>Kind</Label>\n <Select value={kind} onValueChange={(v) => setKind(v as TcpArchiveItemKind)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {KINDS.map((k) => (\n <SelectItem key={k} value={k}>\n {formatEnumLabel(k)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label>Access level</Label>\n <Select\n value={accessLevel}\n onValueChange={(v) => setAccessLevel(v as TcpAccessLevel)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ACCESS_LEVELS.map((a) => (\n <SelectItem key={a} value={a}>\n {formatEnumLabel(a)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <p className=\"text-xs text-text-tertiary\">{ACCESS_HELP[accessLevel]}</p>\n </div>\n </div>\n\n {!collectionId && (\n <div className=\"grid gap-1.5\">\n <Label>Collection</Label>\n <Select value={pickedCollection} onValueChange={setPickedCollection}>\n <SelectTrigger>\n <SelectValue placeholder=\"Add to a collection (optional)\" />\n </SelectTrigger>\n <SelectContent>\n {(collectionsQuery.data ?? []).map((collection) => (\n <SelectItem key={collection.id} value={collection.id}>\n {collection.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-desc\">Description</Label>\n <Textarea\n id=\"item-desc\"\n value={description}\n maxLength={DESCRIPTION_MAX}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n <Counter length={description.length} max={DESCRIPTION_MAX} />\n </div>\n\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-origin\">Origin</Label>\n <Input\n id=\"item-origin\"\n value={origin}\n maxLength={ORIGIN_MAX}\n onChange={(e) => setOrigin(e.target.value)}\n placeholder=\"Place of origin\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-dated\">Dated</Label>\n <Input\n id=\"item-dated\"\n type=\"date\"\n value={datedAt}\n onChange={(e) => setDatedAt(e.target.value)}\n />\n </div>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-provenance\">Provenance</Label>\n <Textarea\n id=\"item-provenance\"\n value={provenance}\n maxLength={PROVENANCE_MAX}\n onChange={(e) => setProvenance(e.target.value)}\n placeholder=\"Chain of custody / source history\"\n rows={2}\n />\n <Counter length={provenance.length} max={PROVENANCE_MAX} />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-langs\">Languages</Label>\n <TagInput\n id=\"item-langs\"\n value={languages}\n onChange={setLanguages}\n placeholder=\"e.g. Tamil, Sanskrit…\"\n preserveCase\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"item-tags\">Tags</Label>\n <TagInput id=\"item-tags\" value={tags} onChange={setTags} placeholder=\"Add tags…\" />\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={createItem.isPending}>\n Add artifact\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -360,7 +360,8 @@ function P({ open: t, onOpenChange: n, topicId: r, onCreated: u }) {
360
360
  });
361
361
  l.success("Story created", e.title), n(!1), ue(), u?.(e);
362
362
  } catch (e) {
363
- Q(c(e));
363
+ let t = c(e);
364
+ Q(t), l.error("Could not create the story", t);
364
365
  }
365
366
  },
366
367
  disabled: p.isPending,
@@ -1 +1 @@
1
- {"version":3,"file":"CreateStoryDialog.js","names":["_Fragment","Fragment$1","FORMATS","SUMMARY_MAX","COVER_MAX_MB","CoverDropzone","inputRef","useRef","uploading","setUploading","useState","dragActive","setDragActive","previewUrl","setPreviewUrl","fileName","setFileName","handleFile","file","uploaded","uploadImageToFiles","FILE_FOLDERS","onUploaded","notify","err","describeError","onInputChange","event","onDrop","clear","onCleared","hiddenInput","X","coverFileId","FileCheck2","cn","Loader2","UploadCloud","Button","CreateStoryDialog","createStory","useTcpCreateStory","topicsQuery","useTcpTopics","title","setTitle","slug","setSlug","slugTouched","setSlugTouched","summary","setSummary","body","setBody","format","setFormat","pickedTopic","setPickedTopic","topicId","languages","setLanguages","tags","setTags","readingMins","setReadingMins","setCoverFileId","error","setError","resetNonce","setResetNonce","effectiveSlug","slugify","reset","n","ResponsiveDialog","ResponsiveDialogContent","ResponsiveDialogHeader","ResponsiveDialogTitle","ResponsiveDialogDescription","Label","Input","CheckCircle2","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","f","formatEnumLabel","topic","Textarea","TagInput","ResponsiveDialogFooter","onOpenChange","created","onCreated"],"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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"}
1
+ {"version":3,"file":"CreateStoryDialog.js","names":["_Fragment","Fragment$1","FORMATS","SUMMARY_MAX","COVER_MAX_MB","CoverDropzone","inputRef","useRef","uploading","setUploading","useState","dragActive","setDragActive","previewUrl","setPreviewUrl","fileName","setFileName","handleFile","file","uploaded","uploadImageToFiles","FILE_FOLDERS","onUploaded","notify","err","describeError","onInputChange","event","onDrop","clear","onCleared","hiddenInput","X","coverFileId","FileCheck2","cn","Loader2","UploadCloud","Button","CreateStoryDialog","createStory","useTcpCreateStory","topicsQuery","useTcpTopics","title","setTitle","slug","setSlug","slugTouched","setSlugTouched","summary","setSummary","body","setBody","format","setFormat","pickedTopic","setPickedTopic","topicId","languages","setLanguages","tags","setTags","readingMins","setReadingMins","setCoverFileId","error","setError","resetNonce","setResetNonce","effectiveSlug","slugify","reset","n","ResponsiveDialog","ResponsiveDialogContent","ResponsiveDialogHeader","ResponsiveDialogTitle","ResponsiveDialogDescription","Label","Input","CheckCircle2","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","f","formatEnumLabel","topic","Textarea","TagInput","ResponsiveDialogFooter","onOpenChange","created","onCreated","message"],"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 // Keep the dialog open with the entered values and surface the failure in\n // both the inline alert and a toast, so a failed create is never silent.\n const message = describeError(err);\n setError(message);\n notify.error('Could not create the story', message);\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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"}
@@ -0,0 +1,8 @@
1
+ import { TcpArchiveItem } from '../types';
2
+ interface EditArchiveItemDialogProps {
3
+ open: boolean;
4
+ onOpenChange: (open: boolean) => void;
5
+ item: TcpArchiveItem;
6
+ }
7
+ export declare function EditArchiveItemDialog({ open, onOpenChange, item }: EditArchiveItemDialogProps): import('react').JSX.Element;
8
+ export {};