@openrails/contentkit-ui 0.95.1 → 0.97.0

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 (61) hide show
  1. package/README.md +9 -1
  2. package/dist/client/discovery.d.ts +2 -1
  3. package/dist/client/discovery.js.map +1 -1
  4. package/dist/client/entities.d.ts +6 -0
  5. package/dist/client/entities.js +13 -0
  6. package/dist/client/entities.js.map +1 -1
  7. package/dist/client/generated/routes.d.ts +26 -0
  8. package/dist/client/generated/routes.js +44 -0
  9. package/dist/client/generated/routes.js.map +1 -1
  10. package/dist/client/generated/wire.d.ts +7 -11
  11. package/dist/client/generated/wire.js.map +1 -1
  12. package/dist/client/http.d.ts +1 -0
  13. package/dist/client/http.js +5 -4
  14. package/dist/client/http.js.map +1 -1
  15. package/dist/client/index.d.ts +1 -1
  16. package/dist/client/reader-preferences.js +3 -7
  17. package/dist/client/reader-preferences.js.map +1 -1
  18. package/dist/components/admin/content-manager.d.ts +3 -1
  19. package/dist/components/admin/content-manager.js +241 -239
  20. package/dist/components/admin/content-manager.js.map +1 -1
  21. package/dist/components/admin/group-manager.js +133 -119
  22. package/dist/components/admin/group-manager.js.map +1 -1
  23. package/dist/components/content/editors.d.ts +1 -0
  24. package/dist/components/content/editors.js +1 -0
  25. package/dist/components/content/editors.js.map +1 -1
  26. package/dist/components/content/filters.d.ts +1 -1
  27. package/dist/components/content/filters.js +1 -1
  28. package/dist/components/content/filters.js.map +1 -1
  29. package/dist/components/groups/banner.d.ts +3 -1
  30. package/dist/components/groups/banner.js +29 -26
  31. package/dist/components/groups/banner.js.map +1 -1
  32. package/dist/components/groups/editor.d.ts +4 -1
  33. package/dist/components/groups/editor.js +369 -315
  34. package/dist/components/groups/editor.js.map +1 -1
  35. package/dist/components/groups/memberships.d.ts +2 -0
  36. package/dist/components/groups/memberships.js +1 -0
  37. package/dist/components/groups/memberships.js.map +1 -1
  38. package/dist/components/groups/parts.d.ts +4 -1
  39. package/dist/components/groups/parts.js +32 -30
  40. package/dist/components/groups/parts.js.map +1 -1
  41. package/dist/locales/en.d.ts +2 -0
  42. package/dist/locales/groups/de.js +2 -0
  43. package/dist/locales/groups/de.js.map +1 -1
  44. package/dist/locales/groups/en.d.ts +2 -0
  45. package/dist/locales/groups/en.js +2 -0
  46. package/dist/locales/groups/en.js.map +1 -1
  47. package/dist/locales/groups/es.js +2 -0
  48. package/dist/locales/groups/es.js.map +1 -1
  49. package/dist/locales/groups/ja.js +2 -0
  50. package/dist/locales/groups/ja.js.map +1 -1
  51. package/dist/locales/groups/ko.js +2 -0
  52. package/dist/locales/groups/ko.js.map +1 -1
  53. package/dist/locales/groups/zh.js +2 -0
  54. package/dist/locales/groups/zh.js.map +1 -1
  55. package/dist/react/discovery.d.ts +2 -1
  56. package/dist/react/discovery.js +54 -29
  57. package/dist/react/discovery.js.map +1 -1
  58. package/dist/react/groups.d.ts +3 -1
  59. package/dist/react/groups.js +7 -7
  60. package/dist/react/groups.js.map +1 -1
  61. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"content-manager.js","names":[],"sources":["../../../src/components/admin/content-manager.tsx"],"sourcesContent":["import { useEffect, useId, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../../client/errors.js\";\nimport { imageRefs } from \"../../client/contents.js\";\nimport type { Content } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useAdminLocation, useFlowClient, useStaffAccess, type AdminLocation, type FlowClientOptions, type StaffAccess } from \"../../react/admin.js\";\nimport { useContentKitClient, useErrorReporter, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContent } from \"../../react/contents.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport { keyOf, useContentScope, usePaged } from \"../../react/use-resource.js\";\nimport { Alert, AlertDescription } from \"#ckui/ui/alert\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button, buttonVariants } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Textarea } from \"#ckui/ui/textarea\";\nimport { localInput } from \"../social/bans.js\";\nimport { contentError, ErrorLine, formatDate, RelativeTime, useAction } from \"../social/parts.js\";\nimport { ContentSections } from \"../content/editors.js\";\nimport type { Labels } from \"../groups/parts.js\";\nimport { AdminRoot, LoadFailed, NoAccess, Pager, Rows, pageOf, useConfirm, type AdminFlowProps, type AdminRouteProps } from \"./shell.js\";\n\ntype Choice = string | { value: string; label: string };\n\n/** What ContentManager hands its body editor. */\nexport interface BodyEditorProps {\n /** The body as HTML, its images at URLs that show them now. */\n value: string;\n onChange: (html: string) => void;\n /** Uploads an image to the item and resolves with a URL that shows it; absent until the item exists. */\n uploadImage?: (file: File) => Promise<string>;\n disabled?: boolean;\n placeholder?: string;\n \"aria-label\"?: string;\n}\n\nexport interface ContentManagerProps extends AdminFlowProps, AdminRouteProps {\n /** The content kind managed. */\n kind: string;\n /** Languages to write in: codes, or codes with labels; the first is a new item's. */\n languages?: readonly Choice[];\n /**\n * The body editor: `richTextEditor` from `@openrails/contentkit-ui/editor`,\n * or the host's own. Without it, the body is a plain HTML field.\n */\n renderEditor?: (props: BodyEditorProps) => ReactNode;\n /** A link to a live item's page. */\n contentHref?: (content: Content) => string | undefined;\n /** Display names of kinds, traits, tiers, attributes and values by identifier. */\n labels?: Labels;\n pageSize?: number;\n}\n\n/** Whether the caller manages content: root:content:write holders, creators and owner-group editors. */\nexport function useContentManagerAccess(o: FlowClientOptions = {}): StaffAccess<\"contents\"> {\n return useStaffAccess([\"contents\"] as const, o);\n}\n\nexport type ContentStatus = \"draft\" | \"scheduled\" | \"published\" | \"held\" | \"rejected\";\n\n/** An item's status as the manager names it. */\nexport function contentStatus(c: Content): ContentStatus {\n if (c.moderation === \"rejected\") return \"rejected\";\n if (c.moderation === \"held\") return \"held\";\n if (c.state === \"draft\") return \"draft\";\n if (!c.live) return \"scheduled\";\n return \"published\";\n}\n\nconst choices = (list?: readonly Choice[]) => (list ?? []).map((c) => (typeof c === \"string\" ? { value: c, label: c } : c));\nconst badge: Record<ContentStatus, \"default\" | \"secondary\" | \"outline\" | \"destructive\"> = { published: \"default\", scheduled: \"secondary\", draft: \"outline\", held: \"secondary\", rejected: \"destructive\" };\nconst states = [\"draft\", \"scheduled\", \"published\"] as const;\n\n/**\n * Content items of one kind for their creators and staff: every one the\n * caller manages, by state and text, deleted ones on their own to restore;\n * create and edit title, slug, description, schedule, cover and body with\n * inline images per language, publish or keep a draft, and delete; then its\n * listing and attributes, access rule and price, versions (order, languages,\n * traits, schedule, access override, price, segments, media) and groups. The\n * list is at basePath, a new item at basePath/new and each item at\n * basePath/{id}, filters in the query.\n */\nexport function ContentManager({ client: own, baseUrl, auth, guard, appearance, className, language, messages, ...p }: ContentManagerProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"content-manager\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Contents {...p} client={client} />\n </AdminRoot>\n );\n}\n\ntype Inner = Omit<ContentManagerProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\"> & { client: ContentKitClient };\n\nfunction Contents({ basePath, navigation, ...p }: Inner) {\n const { t } = useMessages();\n const loc = useAdminLocation(basePath, navigation);\n const access = useContentManagerAccess({ client: p.client });\n if (access.loading) return <Rows />;\n if (!access.allowed.contents) return <NoAccess>{t(\"contentManager.noAccess\")}</NoAccess>;\n if (!loc.view) return <List {...p} loc={loc} />;\n return (\n <>\n <Button variant=\"ghost\" size=\"sm\" className=\"justify-self-start\" onClick={() => loc.go(\"\")}>\n {t(\"contentManager.back\")}\n </Button>\n <ContentEditor\n key={loc.view}\n {...p}\n id={loc.view === \"new\" ? null : loc.view}\n onCreated={(c) => loc.go(c.id, undefined, { replace: true })}\n onDeleted={() => loc.go(\"\", undefined, { replace: true })}\n />\n </>\n );\n}\n\nfunction List({ loc, client, kind, languages: offered, contentHref, pageSize = 20, onError }: Inner & { loc: AdminLocation }) {\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const language = choices(offered)[0]?.value;\n const query = loc.query.get(\"q\") ?? \"\";\n const status = states.find((s) => s === loc.query.get(\"status\")) ?? \"\";\n const deleted = loc.query.get(\"deleted\") === \"1\";\n const page = pageOf(loc.query);\n const filters = { q: query, status, deleted: deleted ? \"1\" : undefined };\n const filter = (next: Partial<typeof filters>) => loc.go(\"\", { ...filters, ...next });\n const [text, setText] = useState(query);\n useEffect(() => setText(query), [query]);\n const { store, scope } = useContentScope(client);\n const state = deleted ? \"deleted\" : status || undefined;\n const list = usePaged(store, keyOf(\"contents-page\", scope, kind, query, state, pageSize), { type: \"contents\", scope: deleted ? \"deleted\" : undefined }, page, pageSize, (q, signal) =>\n client.contents.manage({ ...q, kind, q: query || undefined, state, language }, signal),\n );\n const del = useAction(\"content.save\", onError);\n const { confirm, dialog } = useConfirm();\n const remove = async (c: Content) => {\n if (await confirm({ title: t(\"contentManager.deleteTitle\", { title: c.title }), description: t(\"contentManager.deleteBody\"), confirm: t(\"contentManager.delete\") })) {\n await del.run(() => client.contents.remove(c.id));\n }\n };\n return (\n <div className=\"grid gap-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"text-base font-semibold\">{t(\"contentManager.title\")}</h2>\n <Button size=\"sm\" onClick={() => loc.go(\"new\")}>\n {t(\"contentManager.new\")}\n </Button>\n </div>\n <div className=\"flex gap-2\" role=\"group\" aria-label={t(\"contentManager.title\")}>\n <Button size=\"sm\" variant={deleted ? \"outline\" : \"default\"} aria-pressed={!deleted} onClick={() => loc.go(\"\", { q: query })}>\n {t(\"contentManager.items\")}\n </Button>\n <Button size=\"sm\" variant={deleted ? \"default\" : \"outline\"} aria-pressed={deleted} onClick={() => loc.go(\"\", { q: query, deleted: \"1\" })}>\n {t(\"contentManager.deleted\")}\n </Button>\n </div>\n <div className=\"grid gap-2 sm:grid-cols-[minmax(0,1fr)_auto]\">\n <form\n className=\"flex gap-2\"\n role=\"search\"\n onSubmit={(e) => {\n e.preventDefault();\n filter({ q: text.trim() });\n }}\n >\n <Label htmlFor={`${ids}-q`} className=\"sr-only\">\n {t(\"contentManager.search\")}\n </Label>\n <Input id={`${ids}-q`} className=\"h-8\" type=\"search\" value={text} placeholder={t(\"contentManager.search\")} onChange={(e) => setText(e.target.value)} />\n <Button type=\"submit\" size=\"sm\" variant=\"outline\">\n {t(\"moderation.filter\")}\n </Button>\n </form>\n {!deleted && (\n <Select value={status} onValueChange={(v) => filter({ status: String(v ?? \"\") as typeof status })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"contentManager.status\")}>\n <SelectValue>{(v: string) => t(v ? `contentManager.${v as (typeof states)[number]}` : \"contentManager.allStatuses\")}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{t(\"contentManager.allStatuses\")}</SelectItem>\n {states.map((s) => (\n <SelectItem key={s} value={s}>\n {t(`contentManager.${s}`)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </div>\n <ErrorLine error={del.error} />\n {list.loading && !list.items.length ? (\n <Rows />\n ) : list.error && !list.items.length ? (\n <LoadFailed error={list.error} message={t(\"contentManager.loadFailed\")} retry={list.reload} />\n ) : !list.items.length ? (\n <p className=\"text-muted-foreground\">{t(deleted ? \"contentManager.noDeleted\" : \"contentManager.empty\")}</p>\n ) : (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\" aria-busy={list.loading}>\n {list.items.map((c) => {\n const s = contentStatus(c);\n const href = s === \"published\" ? contentHref?.(c) : undefined;\n return (\n <li key={c.id} className=\"flex flex-wrap items-start gap-3 p-3\" data-ckui=\"content-row\" data-status={deleted ? \"deleted\" : s}>\n <div className=\"grid min-w-0 flex-1 gap-1\">\n {deleted ? (\n <span className=\"font-medium break-words\">{c.title}</span>\n ) : (\n <button type=\"button\" className=\"text-left font-medium break-words hover:underline\" onClick={() => loc.go(c.id)}>\n {c.title}\n </button>\n )}\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted-foreground\">\n {!deleted && <Badge variant={badge[s]}>{t(`contentManager.${s}`)}</Badge>}\n <span>{c.language}</span>\n <RelativeTime at={deleted && c.deleted_at ? c.deleted_at : c.updated_at} />\n {s === \"scheduled\" && c.publish_at && <span>{t(\"pollManager.goesLive\", { date: formatDate(m, c.publish_at) })}</span>}\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {deleted ? (\n <Button size=\"sm\" variant=\"outline\" disabled={del.pending} onClick={() => void del.run(() => client.contents.restore(c.id))}>\n {t(\"moderation.restore\")}\n </Button>\n ) : (\n <>\n {href && (\n <a className={buttonVariants({ size: \"sm\", variant: \"ghost\" })} href={href}>\n {t(\"contentManager.view\")}\n </a>\n )}\n <Button size=\"sm\" variant=\"outline\" onClick={() => loc.go(c.id)}>\n {t(\"comments.edit\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" disabled={del.pending} onClick={() => void remove(c)}>\n {t(\"comments.delete\")}\n </Button>\n </>\n )}\n </div>\n </li>\n );\n })}\n </ul>\n )}\n <Pager page={page} hasNext={list.hasNext} disabled={list.loading} onPage={(n) => loc.go(\"\", { ...filters, page: n > 1 ? n : undefined })} />\n {dialog}\n </div>\n );\n}\n\ninterface Form {\n title: string;\n slug: string;\n description: string;\n publishAt: string;\n body: string;\n}\n\nconst empty: Form = { title: \"\", slug: \"\", description: \"\", publishAt: \"\", body: \"\" };\n\nexport interface ContentEditorProps {\n /** The item edited; null creates one of kind. */\n id: string | null;\n kind: string;\n /** Languages to write in: codes, or codes with labels; the first is a new item's. */\n languages?: readonly Choice[];\n /** The body editor; without it, a plain HTML field. */\n renderEditor?: (props: BodyEditorProps) => ReactNode;\n /** A link to a live item's page. */\n contentHref?: (content: Content) => string | undefined;\n labels?: Labels;\n /** A new item's owner group: a channel its editors write for. */\n ownerGroup?: string;\n /** After create: show the new item (the editor goes on editing it). */\n onCreated?: (content: Content) => void;\n onDeleted?: (content: Content) => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n}\n\n/**\n * One content item for its creators and staff: title, slug, description,\n * schedule, cover and body per language, publish, unpublish and delete;\n * then listing and attributes, access rule and price, versions and groups.\n */\nexport function ContentEditor({ id, client: own, kind, languages: offered, renderEditor, contentHref, labels, ownerGroup, onCreated, onDeleted, onError }: ContentEditorProps) {\n const client = useContentKitClient(own);\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const languages = choices(offered);\n const [language, setLanguage] = useState(languages[0]?.value ?? \"en\");\n const ed = useContent(id, kind, language, { client });\n // A hidden version's cover is edited here; a visible version's in its own form.\n const hidden = !!useKinds({ client }).kinds?.kinds[kind]?.versions.hidden;\n const report = useErrorReporter(onError);\n // The body is kept as stored (image references); the editor shows it with URLs.\n const [form, setForm] = useState<Form | null>(id ? null : empty);\n const [error, setError] = useState<ContentKitError | string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const { confirm, dialog } = useConfirm();\n const item = ed.content;\n useEffect(() => {\n if (!item) return;\n const stored = item.text?.[language];\n setForm((f) => f ?? { title: stored?.title ?? \"\", slug: stored?.slug ?? \"\", description: stored?.description ?? \"\", body: stored?.body ?? \"\",\n publishAt: item.publish_at && !item.live ? localInput(new Date(item.publish_at)) : \"\" });\n }, [item, language]);\n const { imageSrc } = ed;\n const shown = useMemo(\n () => (form?.body ?? \"\").replace(imageRefs(), (ref, name: string) => {\n const src = imageSrc(name);\n return src && src !== name ? src.replaceAll(\"&\", \"&amp;\") : ref;\n }),\n [form?.body, imageSrc],\n );\n if (id && !form) {\n if (ed.error) return <LoadFailed error={ed.error} message={t(\"contentManager.loadFailed\")} retry={ed.reload} />;\n return (\n <div className=\"grid gap-3\" aria-busy>\n <Skeleton className=\"h-9 w-full\" />\n <Skeleton className=\"h-48 w-full\" />\n </div>\n );\n }\n const f = form!;\n const set = (patch: Partial<Form>) => setForm((cur) => ({ ...cur!, ...patch }));\n const fail = (e: unknown) => {\n const err = toContentKitError(e);\n setError(err);\n report(err, \"content.save\");\n };\n const text = () => ({ title: f.title.trim(), slug: f.slug.trim() || undefined, description: f.description.trim(), body: f.body });\n const save = async () => {\n setError(null);\n setNotice(null);\n if (!f.title.trim()) return setError(t(\"contentManager.titleRequired\"));\n try {\n if (!item) {\n const created = await ed.create({ kind, text: { [language]: text() }, owner_group_id: ownerGroup });\n onCreated?.(created);\n return;\n }\n const saved = await ed.putText(text());\n setNotice(t(\"pollEditor.saved\"));\n setForm((cur) => ({ ...cur!, slug: saved.text?.[language]?.slug ?? cur!.slug }));\n } catch (e) {\n fail(e);\n }\n };\n const publish = async (live: boolean) => {\n setError(null);\n setNotice(null);\n try {\n if (live) await ed.publish(f.publishAt ? new Date(f.publishAt).toISOString() : undefined);\n else await ed.unpublish();\n setNotice(t(live ? \"contentManager.publishedNotice\" : \"contentManager.unpublishedNotice\"));\n } catch (e) {\n fail(e);\n }\n };\n const remove = async () => {\n if (!item || !(await confirm({ title: t(\"contentManager.deleteTitle\", { title: item.title }), description: t(\"contentManager.deleteBody\"), confirm: t(\"contentManager.delete\") }))) return;\n try {\n await ed.remove();\n onDeleted?.(item);\n } catch (e) {\n fail(e);\n }\n };\n const s = item ? contentStatus(item) : \"draft\";\n const href = item && s === \"published\" ? contentHref?.(item) : undefined;\n const editor: BodyEditorProps = {\n value: shown,\n onChange: (html) => set({ body: ed.storedBody(html) }),\n uploadImage: item ? (file) => ed.uploadImage(file) : undefined,\n disabled: ed.saving,\n placeholder: t(\"contentManager.bodyPlaceholder\"),\n \"aria-label\": t(\"contentManager.body\"),\n };\n return (\n <>\n <form\n className=\"grid gap-4\"\n data-ckui=\"content-form\"\n onSubmit={(e) => {\n e.preventDefault();\n void save();\n }}\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold\">{t(item ? \"contentManager.editTitle\" : \"contentManager.newTitle\")}</h2>\n {item && <Badge variant={badge[s]}>{t(`contentManager.${s}`)}</Badge>}\n {href && (\n <a className=\"text-xs text-muted-foreground hover:underline\" href={href}>\n {t(\"contentManager.view\")}\n </a>\n )}\n </div>\n {item?.moderation && (\n <Alert variant={item.moderation === \"rejected\" ? \"destructive\" : \"default\"}>\n <AlertDescription>\n {item.moderation_reason\n ? t(`contentManager.${item.moderation === \"rejected\" ? \"rejectedBecause\" : \"heldBecause\"}`, { reason: item.moderation_reason })\n : t(`contentManager.${item.moderation === \"rejected\" ? \"rejectedNote\" : \"heldNote\"}`)}\n </AlertDescription>\n </Alert>\n )}\n {languages.length > 1 && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-lang`}>{t(\"contentManager.language\")}</Label>\n <Select\n value={language}\n onValueChange={(v) => {\n setLanguage(String(v ?? \"\"));\n setForm(item ? null : empty);\n }}\n >\n <SelectTrigger id={`${ids}-lang`} className=\"w-full\">\n <SelectValue>{(v: string) => languages.find((l) => l.value === v)?.label ?? v}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l.value} value={l.value}>\n {l.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-title`}>{t(\"contentManager.itemTitle\")}</Label>\n <Input id={`${ids}-title`} value={f.title} required maxLength={300} onChange={(e) => set({ title: e.target.value })} />\n </div>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-slug`}>{t(\"contentManager.slug\")}</Label>\n <Input id={`${ids}-slug`} value={f.slug} placeholder={t(\"contentManager.slugAuto\")} onChange={(e) => set({ slug: e.target.value })} />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-publish`}>{t(\"contentManager.publishAt\")}</Label>\n <Input id={`${ids}-publish`} type=\"datetime-local\" value={f.publishAt} onChange={(e) => set({ publishAt: e.target.value })} />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-description`}>{t(\"contentManager.description\")}</Label>\n <Textarea id={`${ids}-description`} rows={2} value={f.description} onChange={(e) => set({ description: e.target.value })} />\n </div>\n {(hidden || !item) && <Cover item={item} src={ed.coverSrc} saving={ed.saving} onFile={(file) => void ed.setCover(file).catch(fail)} />}\n <div className=\"grid gap-1.5\">\n <span className=\"text-sm font-medium\">{t(\"contentManager.body\")}</span>\n {renderEditor ? renderEditor(editor) : <Textarea aria-label={editor[\"aria-label\"]} rows={12} value={f.body} placeholder={editor.placeholder} onChange={(e) => set({ body: e.target.value })} />}\n {!item && <p className=\"text-xs text-muted-foreground\">{t(\"contentManager.imagesAfterCreate\")}</p>}\n </div>\n {error && <ErrorLine>{typeof error === \"string\" ? error : contentError(m, error)}</ErrorLine>}\n {notice && !error && (\n <p role=\"status\" className=\"text-sm text-muted-foreground\">\n {notice}\n </p>\n )}\n <div className=\"flex flex-wrap gap-2 border-t border-border pt-4\">\n <Button type=\"submit\" disabled={ed.saving}>\n {ed.saving ? t(\"common.saving\") : t(item ? \"common.save\" : \"contentManager.createDraft\")}\n </Button>\n {item && !item.live && (\n <Button type=\"button\" variant=\"outline\" disabled={ed.saving} onClick={() => void publish(true)}>\n {t(\"contentManager.publish\")}\n </Button>\n )}\n {item?.state === \"published\" && (\n <Button type=\"button\" variant=\"outline\" disabled={ed.saving} onClick={() => void publish(false)}>\n {t(\"contentManager.unpublish\")}\n </Button>\n )}\n {item && (\n <Button type=\"button\" variant=\"destructive\" className=\"ms-auto\" disabled={ed.saving} onClick={() => void remove()}>\n {t(\"contentManager.delete\")}\n </Button>\n )}\n </div>\n {dialog}\n </form>\n {item && <ContentSections content={item} client={client} onError={onError} labels={labels} />}\n </>\n );\n}\n\nfunction Cover({ item, src, saving, onFile }: { item: Content | null; src?: string; saving: boolean; onFile: (file: File | null) => void }) {\n const { t } = useMessages();\n const file = useRef<HTMLInputElement>(null);\n return (\n <div className=\"grid gap-1.5\" data-ckui=\"content-cover\">\n <span className=\"text-sm font-medium\">{t(\"contentManager.cover\")}</span>\n {item ? (\n <div className=\"flex flex-wrap items-center gap-3\">\n {src ? <img src={src} alt=\"\" className=\"h-20 w-36 rounded-md border border-border object-cover\" /> : <span className=\"text-muted-foreground\">{t(\"contentManager.noCover\")}</span>}\n <Button type=\"button\" size=\"sm\" variant=\"outline\" disabled={saving} onClick={() => file.current?.click()}>\n {t(src ? \"pollEditor.replaceImage\" : \"contentManager.addCover\")}\n </Button>\n {src && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" disabled={saving} onClick={() => onFile(null)}>\n {t(\"pollEditor.removeImage\")}\n </Button>\n )}\n <input\n ref={file}\n type=\"file\"\n accept=\"image/*\"\n hidden\n aria-label={t(\"contentManager.cover\")}\n onChange={(e) => {\n const f = e.target.files?.[0];\n e.target.value = \"\";\n if (f) onFile(f);\n }}\n />\n </div>\n ) : (\n <p className=\"text-xs text-muted-foreground\">{t(\"contentManager.imagesAfterCreate\")}</p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyDA,SAAgB,EAAwB,IAAuB,CAAC,GAA4B;CAC1F,OAAO,EAAe,CAAC,UAAU,GAAY,CAAC;AAChD;AAKA,SAAgB,EAAc,GAA2B;CAKvD,OAJI,EAAE,eAAe,aAAmB,aACpC,EAAE,eAAe,SAAe,SAChC,EAAE,UAAU,UAAgB,UAC3B,EAAE,OACA,cADa;AAEtB;AAEA,IAAM,MAAW,OAA8B,KAAQ,CAAC,EAAA,CAAG,KAAK,MAAO,OAAO,KAAM,WAAW;CAAE,OAAO;CAAG,OAAO;AAAE,IAAI,CAAE,GACpH,KAAoF;CAAE,WAAW;CAAW,WAAW;CAAa,OAAO;CAAW,MAAM;CAAa,UAAU;AAAc,GACjM,IAAS;CAAC;CAAS;CAAa;AAAW;AAYjD,SAAgB,EAAe,EAAE,QAAQ,GAAK,YAAS,SAAM,UAAO,eAAY,cAAW,aAAU,aAAU,GAAG,KAA0B;CAC1I,IAAM,IAAS,EAAc;EAAE,QAAQ;EAAK;EAAS;CAAK,CAAC;CAC3D,OACE,kBAAC,GAAD;EAAW,MAAK;EAA8B;EAAuB;EAAqB;EAAoB;EAAiB;EAC7H,UAAA,kBAAC,GAAD;GAAU,GAAI;GAAW;EAAS,CAAA;CACzB,CAAA;AAEf;AAIA,SAAS,EAAS,EAAE,aAAU,eAAY,GAAG,KAAY;CACvD,IAAM,EAAE,SAAM,EAAY,GACpB,IAAM,EAAiB,GAAU,CAAU,GAC3C,IAAS,EAAwB,EAAE,QAAQ,EAAE,OAAO,CAAC;CAI3D,OAHI,EAAO,UAAgB,kBAAC,GAAD,CAAO,CAAA,IAC7B,EAAO,QAAQ,WACf,EAAI,OAEP,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;EAAQ,SAAQ;EAAQ,MAAK;EAAK,WAAU;EAAqB,eAAe,EAAI,GAAG,EAAE;EACtF,UAAA,EAAE,qBAAqB;CAClB,CAAA,GACR,kBAAC,GAAD;EAEE,GAAI;EACJ,IAAI,EAAI,SAAS,QAAQ,OAAO,EAAI;EACpC,YAAY,MAAM,EAAI,GAAG,EAAE,IAAI,KAAA,GAAW,EAAE,SAAS,GAAK,CAAC;EAC3D,iBAAiB,EAAI,GAAG,IAAI,KAAA,GAAW,EAAE,SAAS,GAAK,CAAC;CACzD,GALM,EAAI,IAKV,CACD,EAAA,CAAA,IAbkB,kBAAC,GAAD;EAAM,GAAI;EAAQ;CAAM,CAAA,IADT,kBAAC,GAAD,EAAA,UAAW,EAAE,yBAAyB,EAAY,CAAA;AAgBzF;AAEA,SAAS,EAAK,EAAE,QAAK,WAAQ,SAAM,WAAW,GAAS,gBAAa,cAAW,IAAI,cAA2C;CAC5H,IAAM,IAAI,EAAY,GAChB,EAAE,SAAM,GACR,IAAM,GAAM,GACZ,IAAW,GAAQ,CAAO,CAAC,CAAC,EAAE,EAAE,OAChC,IAAQ,EAAI,MAAM,IAAI,GAAG,KAAK,IAC9B,IAAS,EAAO,MAAM,MAAM,MAAM,EAAI,MAAM,IAAI,QAAQ,CAAC,KAAK,IAC9D,IAAU,EAAI,MAAM,IAAI,SAAS,MAAM,KACvC,IAAO,EAAO,EAAI,KAAK,GACvB,IAAU;EAAE,GAAG;EAAO;EAAQ,SAAS,IAAU,MAAM,KAAA;CAAU,GACjE,KAAU,MAAkC,EAAI,GAAG,IAAI;EAAE,GAAG;EAAS,GAAG;CAAK,CAAC,GAC9E,CAAC,GAAM,KAAW,EAAS,CAAK;CACtC,SAAgB,EAAQ,CAAK,GAAG,CAAC,CAAK,CAAC;CACvC,IAAM,EAAE,UAAO,aAAU,EAAgB,CAAM,GACzC,IAAQ,IAAU,YAAY,KAAU,KAAA,GACxC,IAAO,EAAS,GAAO,EAAM,iBAAiB,GAAO,GAAM,GAAO,GAAO,CAAQ,GAAG;EAAE,MAAM;EAAY,OAAO,IAAU,YAAY,KAAA;CAAU,GAAG,GAAM,IAAW,GAAG,MAC1K,EAAO,SAAS,OAAO;EAAE,GAAG;EAAG;EAAM,GAAG,KAAS,KAAA;EAAW;EAAO;CAAS,GAAG,CAAM,CACvF,GACM,IAAM,GAAU,gBAAgB,CAAO,GACvC,EAAE,YAAS,cAAW,GAAW,GACjC,IAAS,OAAO,MAAe;EACnC,AAAI,MAAM,EAAQ;GAAE,OAAO,EAAE,8BAA8B,EAAE,OAAO,EAAE,MAAM,CAAC;GAAG,aAAa,EAAE,2BAA2B;GAAG,SAAS,EAAE,uBAAuB;EAAE,CAAC,KAChK,MAAM,EAAI,UAAU,EAAO,SAAS,OAAO,EAAE,EAAE,CAAC;CAEpD;CACA,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;KAA2B,UAAA,EAAE,sBAAsB;IAAM,CAAA,GACvE,kBAAC,GAAD;KAAQ,MAAK;KAAK,eAAe,EAAI,GAAG,KAAK;KAC1C,UAAA,EAAE,oBAAoB;IACjB,CAAA,CACL;;GACL,kBAAC,OAAD;IAAK,WAAU;IAAa,MAAK;IAAQ,cAAY,EAAE,sBAAsB;IAA7E,UAAA,CACE,kBAAC,GAAD;KAAQ,MAAK;KAAK,SAAS,IAAU,YAAY;KAAW,gBAAc,CAAC;KAAS,eAAe,EAAI,GAAG,IAAI,EAAE,GAAG,EAAM,CAAC;KACvH,UAAA,EAAE,sBAAsB;IACnB,CAAA,GACR,kBAAC,GAAD;KAAQ,MAAK;KAAK,SAAS,IAAU,YAAY;KAAW,gBAAc;KAAS,eAAe,EAAI,GAAG,IAAI;MAAE,GAAG;MAAO,SAAS;KAAI,CAAC;KACpI,UAAA,EAAE,wBAAwB;IACrB,CAAA,CACL;;GACL,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,QAAD;KACE,WAAU;KACV,MAAK;KACL,WAAW,MAAM;MAEf,AADA,EAAE,eAAe,GACjB,EAAO,EAAE,GAAG,EAAK,KAAK,EAAE,CAAC;KAC3B;KANF,UAAA;MAQE,kBAAC,GAAD;OAAO,SAAS,GAAG,EAAI;OAAK,WAAU;OACnC,UAAA,EAAE,uBAAuB;MACrB,CAAA;MACP,kBAAC,GAAD;OAAO,IAAI,GAAG,EAAI;OAAK,WAAU;OAAM,MAAK;OAAS,OAAO;OAAM,aAAa,EAAE,uBAAuB;OAAG,WAAW,MAAM,EAAQ,EAAE,OAAO,KAAK;MAAI,CAAA;MACtJ,kBAAC,GAAD;OAAQ,MAAK;OAAS,MAAK;OAAK,SAAQ;OACrC,UAAA,EAAE,mBAAmB;MAChB,CAAA;KACJ;IACL,CAAA,GAAA,CAAC,KACA,kBAAC,IAAD;KAAQ,OAAO;KAAQ,gBAAgB,MAAM,EAAO,EAAE,QAAQ,OAAO,KAAK,EAAE,EAAmB,CAAC;KAAhG,UAAA,CACE,kBAAC,IAAD;MAAe,MAAK;MAAK,cAAY,EAAE,uBAAuB;MAC5D,UAAA,kBAAC,IAAD,EAAA,WAAe,MAAc,EAAE,IAAI,kBAAkB,MAAiC,4BAA4B,EAAe,CAAA;KACpH,CAAA,GACf,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,GAAD;MAAY,OAAM;MAAI,UAAA,EAAE,4BAA4B;KAAc,CAAA,GACjE,EAAO,KAAK,MACX,kBAAC,GAAD;MAAoB,OAAO;MACxB,UAAA,EAAE,kBAAkB,GAAG;KACd,GAFK,CAEL,CACb,CACY,EAAA,CAAA,CACT;IAEP,CAAA,CAAA;;GACL,kBAAC,IAAD,EAAW,OAAO,EAAI,MAAQ,CAAA;GAC7B,EAAK,WAAW,CAAC,EAAK,MAAM,SAC3B,kBAAC,GAAD,CAAO,CAAA,IACL,EAAK,SAAS,CAAC,EAAK,MAAM,SAC5B,kBAAC,IAAD;IAAY,OAAO,EAAK;IAAO,SAAS,EAAE,2BAA2B;IAAG,OAAO,EAAK;GAAS,CAAA,IAC1F,EAAK,MAAM,SAGd,kBAAC,MAAD;IAAI,WAAU;IAA8D,aAAW,EAAK;IACzF,UAAA,EAAK,MAAM,KAAK,MAAM;KACrB,IAAM,IAAI,EAAc,CAAC,GACnB,IAAO,MAAM,cAAc,IAAc,CAAC,IAAI,KAAA;KACpD,OACE,kBAAC,MAAD;MAAe,WAAU;MAAuC,aAAU;MAAc,eAAa,IAAU,YAAY;MAA3H,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACG,IACC,kBAAC,QAAD;QAAM,WAAU;QAA2B,UAAA,EAAE;OAAY,CAAA,IAEzD,kBAAC,UAAD;QAAQ,MAAK;QAAS,WAAU;QAAoD,eAAe,EAAI,GAAG,EAAE,EAAE;QAC3G,UAAA,EAAE;OACG,CAAA,GAEV,kBAAC,OAAD;QAAK,WAAU;QAAf,UAAA;SACG,CAAC,KAAW,kBAAC,IAAD;UAAO,SAAS,GAAM;UAAK,UAAA,EAAE,kBAAkB,GAAG;SAAS,CAAA;SACxE,kBAAC,QAAD,EAAA,UAAO,EAAE,SAAe,CAAA;SACxB,kBAAC,GAAD,EAAc,IAAI,KAAW,EAAE,aAAa,EAAE,aAAa,EAAE,WAAa,CAAA;SACzE,MAAM,eAAe,EAAE,cAAc,kBAAC,QAAD,EAAA,UAAO,EAAE,wBAAwB,EAAE,MAAM,GAAW,GAAG,EAAE,UAAU,EAAE,CAAC,EAAQ,CAAA;QACjH;OACF,CAAA,CAAA;MACL,CAAA,GAAA,kBAAC,OAAD;OAAK,WAAU;OACZ,UAAA,IACC,kBAAC,GAAD;QAAQ,MAAK;QAAK,SAAQ;QAAU,UAAU,EAAI;QAAS,eAAe,KAAK,EAAI,UAAU,EAAO,SAAS,QAAQ,EAAE,EAAE,CAAC;QACvH,UAAA,EAAE,oBAAoB;OACjB,CAAA,IAER,kBAAA,GAAA,EAAA,UAAA;QACG,KACC,kBAAC,KAAD;SAAG,WAAW,GAAe;UAAE,MAAM;UAAM,SAAS;SAAQ,CAAC;SAAS;SACnE,UAAA,EAAE,qBAAqB;QACvB,CAAA;QAEL,kBAAC,GAAD;SAAQ,MAAK;SAAK,SAAQ;SAAU,eAAe,EAAI,GAAG,EAAE,EAAE;SAC3D,UAAA,EAAE,eAAe;QACZ,CAAA;QACR,kBAAC,GAAD;SAAQ,MAAK;SAAK,SAAQ;SAAc,UAAU,EAAI;SAAS,eAAe,KAAK,EAAO,CAAC;SACxF,UAAA,EAAE,iBAAiB;QACd,CAAA;OACR,EAAA,CAAA;MAED,CAAA,CACH;KArCK,GAAA,EAAE,EAqCP;IAER,CAAC;GACC,CAAA,IA/CJ,kBAAC,KAAD;IAAG,WAAU;IAAyB,UAAA,EAAE,IAAU,6BAA6B,sBAAsB;GAAK,CAAA;GAiD5G,kBAAC,IAAD;IAAa;IAAM,SAAS,EAAK;IAAS,UAAU,EAAK;IAAS,SAAS,MAAM,EAAI,GAAG,IAAI;KAAE,GAAG;KAAS,MAAM,IAAI,IAAI,IAAI,KAAA;IAAU,CAAC;GAAI,CAAA;GAC1I;EACE;;AAET;AAUA,IAAM,IAAc;CAAE,OAAO;CAAI,MAAM;CAAI,aAAa;CAAI,WAAW;CAAI,MAAM;AAAG;AA2BpF,SAAgB,EAAc,EAAE,OAAI,QAAQ,GAAK,SAAM,WAAW,IAAS,iBAAc,iBAAa,YAAQ,eAAY,cAAW,eAAW,cAA+B;CAC7K,IAAM,IAAS,EAAoB,CAAG,GAChC,IAAI,EAAY,GAChB,EAAE,SAAM,GACR,IAAM,GAAM,GACZ,IAAY,GAAQ,EAAO,GAC3B,CAAC,GAAU,KAAe,EAAS,EAAU,EAAE,EAAE,SAAS,IAAI,GAC9D,IAAK,EAAW,GAAI,GAAM,GAAU,EAAE,UAAO,CAAC,GAE9C,IAAS,CAAC,CAAC,EAAS,EAAE,UAAO,CAAC,CAAC,CAAC,OAAO,MAAM,EAAK,EAAE,SAAS,QAC7D,IAAS,EAAiB,CAAO,GAEjC,CAAC,GAAM,KAAW,EAAsB,IAAK,OAAO,CAAK,GACzD,CAAC,GAAO,KAAY,EAA0C,IAAI,GAClE,CAAC,IAAQ,KAAa,EAAwB,IAAI,GAClD,EAAE,aAAS,eAAW,GAAW,GACjC,IAAO,EAAG;CAChB,SAAgB;EACd,IAAI,CAAC,GAAM;EACX,IAAM,IAAS,EAAK,OAAO;EAC3B,GAAS,MAAM,KAAK;GAAE,OAAO,GAAQ,SAAS;GAAI,MAAM,GAAQ,QAAQ;GAAI,aAAa,GAAQ,eAAe;GAAI,MAAM,GAAQ,QAAQ;GACxI,WAAW,EAAK,cAAc,CAAC,EAAK,OAAO,EAAW,IAAI,KAAK,EAAK,UAAU,CAAC,IAAI;EAAG,CAAC;CAC3F,GAAG,CAAC,GAAM,CAAQ,CAAC;CACnB,IAAM,EAAE,iBAAa,GACf,KAAQ,SACL,GAAM,QAAQ,GAAA,CAAI,QAAQ,EAAU,IAAI,GAAK,MAAiB;EACnE,IAAM,IAAM,GAAS,CAAI;EACzB,OAAO,KAAO,MAAQ,IAAO,EAAI,WAAW,KAAK,OAAO,IAAI;CAC9D,CAAC,GACD,CAAC,GAAM,MAAM,EAAQ,CACvB;CACA,IAAI,KAAM,CAAC,GAET,OADI,EAAG,QAAc,kBAAC,IAAD;EAAY,OAAO,EAAG;EAAO,SAAS,EAAE,2BAA2B;EAAG,OAAO,EAAG;CAAS,CAAA,IAE5G,kBAAC,OAAD;EAAK,WAAU;EAAa,aAAA;EAA5B,UAAA,CACE,kBAAC,GAAD,EAAU,WAAU,aAAc,CAAA,GAClC,kBAAC,GAAD,EAAU,WAAU,cAAe,CAAA,CAChC;;CAGT,IAAM,IAAI,GACJ,KAAO,MAAyB,GAAS,OAAS;EAAE,GAAG;EAAM,GAAG;CAAM,EAAE,GACxE,KAAQ,MAAe;EAC3B,IAAM,IAAM,EAAkB,CAAC;EAE/B,AADA,EAAS,CAAG,GACZ,EAAO,GAAK,cAAc;CAC5B,GACM,YAAc;EAAE,OAAO,EAAE,MAAM,KAAK;EAAG,MAAM,EAAE,KAAK,KAAK,KAAK,KAAA;EAAW,aAAa,EAAE,YAAY,KAAK;EAAG,MAAM,EAAE;CAAK,IACzH,KAAO,YAAY;EAGvB,IAFA,EAAS,IAAI,GACb,EAAU,IAAI,GACV,CAAC,EAAE,MAAM,KAAK,GAAG,OAAO,EAAS,EAAE,8BAA8B,CAAC;EACtE,IAAI;GACF,IAAI,CAAC,GAAM;IACT,IAAM,IAAU,MAAM,EAAG,OAAO;KAAE;KAAM,MAAM,GAAG,IAAW,GAAK,EAAE;KAAG,gBAAgB;IAAW,CAAC;IAClG,IAAY,CAAO;IACnB;GACF;GACA,IAAM,IAAQ,MAAM,EAAG,QAAQ,GAAK,CAAC;GAErC,AADA,EAAU,EAAE,kBAAkB,CAAC,GAC/B,GAAS,OAAS;IAAE,GAAG;IAAM,MAAM,EAAM,OAAO,EAAS,EAAE,QAAQ,EAAK;GAAK,EAAE;EACjF,SAAS,GAAG;GACV,EAAK,CAAC;EACR;CACF,GACM,KAAU,OAAO,MAAkB;EAEvC,AADA,EAAS,IAAI,GACb,EAAU,IAAI;EACd,IAAI;GAGF,AAFI,IAAM,MAAM,EAAG,QAAQ,EAAE,YAAY,IAAI,KAAK,EAAE,SAAS,CAAC,CAAC,YAAY,IAAI,KAAA,CAAS,IACnF,MAAM,EAAG,UAAU,GACxB,EAAU,EAAE,IAAO,mCAAmC,kCAAkC,CAAC;EAC3F,SAAS,GAAG;GACV,EAAK,CAAC;EACR;CACF,GACM,KAAS,YAAY;EACrB,IAAC,KAAU,MAAM,GAAQ;GAAE,OAAO,EAAE,8BAA8B,EAAE,OAAO,EAAK,MAAM,CAAC;GAAG,aAAa,EAAE,2BAA2B;GAAG,SAAS,EAAE,uBAAuB;EAAE,CAAC,GAChL,IAAI;GAEF,AADA,MAAM,EAAG,OAAO,GAChB,KAAY,CAAI;EAClB,SAAS,GAAG;GACV,EAAK,CAAC;EACR;CACF,GACM,IAAI,IAAO,EAAc,CAAI,IAAI,SACjC,KAAO,KAAQ,MAAM,cAAc,KAAc,CAAI,IAAI,KAAA,GACzD,IAA0B;EAC9B,OAAO;EACP,WAAW,MAAS,EAAI,EAAE,MAAM,EAAG,WAAW,CAAI,EAAE,CAAC;EACrD,aAAa,KAAQ,MAAS,EAAG,YAAY,CAAI,IAAI,KAAA;EACrD,UAAU,EAAG;EACb,aAAa,EAAE,gCAAgC;EAC/C,cAAc,EAAE,qBAAqB;CACvC;CACA,OACE,kBAAA,GAAA,EAAA,UAAA,CACA,kBAAC,QAAD;EACE,WAAU;EACV,aAAU;EACV,WAAW,MAAM;GAEf,AADA,EAAE,eAAe,GACjB,GAAU;EACZ;EANF,UAAA;GAQE,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,kBAAC,MAAD;MAAI,WAAU;MAA2B,UAAA,EAAE,IAAO,6BAA6B,yBAAyB;KAAM,CAAA;KAC7G,KAAQ,kBAAC,IAAD;MAAO,SAAS,GAAM;MAAK,UAAA,EAAE,kBAAkB,GAAG;KAAS,CAAA;KACnE,MACC,kBAAC,KAAD;MAAG,WAAU;MAAsD;MAChE,UAAA,EAAE,qBAAqB;KACvB,CAAA;IAEF;;GACJ,GAAM,cACL,kBAAC,GAAD;IAAO,SAAS,EAAK,eAAe,aAAa,gBAAgB;IAC/D,UAAA,kBAAC,GAAD,EAAA,UACG,EAAK,oBACF,EAAE,kBAAkB,EAAK,eAAe,aAAa,oBAAoB,iBAAiB,EAAE,QAAQ,EAAK,kBAAkB,CAAC,IAC5H,EAAE,kBAAkB,EAAK,eAAe,aAAa,iBAAiB,YAAY,EACtE,CAAA;GACb,CAAA;GAER,EAAU,SAAS,KAClB,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAO,SAAS,GAAG,EAAI;KAAS,UAAA,EAAE,yBAAyB;IAAS,CAAA,GACpE,kBAAC,IAAD;KACE,OAAO;KACP,gBAAgB,MAAM;MAEpB,AADA,EAAY,OAAO,KAAK,EAAE,CAAC,GAC3B,EAAQ,IAAO,OAAO,CAAK;KAC7B;KALF,UAAA,CAOE,kBAAC,IAAD;MAAe,IAAI,GAAG,EAAI;MAAQ,WAAU;MAC1C,UAAA,kBAAC,IAAD,EAAA,WAAe,MAAc,EAAU,MAAM,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,SAAS,EAAe,CAAA;KAC9E,CAAA,GACf,kBAAC,IAAD,EAAA,UACG,EAAU,KAAK,MACd,kBAAC,GAAD;MAA0B,OAAO,EAAE;MAChC,UAAA,EAAE;KACO,GAFK,EAAE,KAEP,CACb,EACY,CAAA,CACT;IACL,CAAA,CAAA;;GAEP,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAO,SAAS,GAAG,EAAI;KAAU,UAAA,EAAE,0BAA0B;IAAS,CAAA,GACtE,kBAAC,GAAD;KAAO,IAAI,GAAG,EAAI;KAAS,OAAO,EAAE;KAAO,UAAA;KAAS,WAAW;KAAK,WAAW,MAAM,EAAI,EAAE,OAAO,EAAE,OAAO,MAAM,CAAC;IAAI,CAAA,CACnH;;GACL,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,kBAAC,GAAD;MAAO,SAAS,GAAG,EAAI;MAAS,UAAA,EAAE,qBAAqB;KAAS,CAAA,GAChE,kBAAC,GAAD;MAAO,IAAI,GAAG,EAAI;MAAQ,OAAO,EAAE;MAAM,aAAa,EAAE,yBAAyB;MAAG,WAAW,MAAM,EAAI,EAAE,MAAM,EAAE,OAAO,MAAM,CAAC;KAAI,CAAA,CAClI;IACL,CAAA,GAAA,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,kBAAC,GAAD;MAAO,SAAS,GAAG,EAAI;MAAY,UAAA,EAAE,0BAA0B;KAAS,CAAA,GACxE,kBAAC,GAAD;MAAO,IAAI,GAAG,EAAI;MAAW,MAAK;MAAiB,OAAO,EAAE;MAAW,WAAW,MAAM,EAAI,EAAE,WAAW,EAAE,OAAO,MAAM,CAAC;KAAI,CAAA,CAC1H;IACF,CAAA,CAAA;;GACL,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAO,SAAS,GAAG,EAAI;KAAgB,UAAA,EAAE,4BAA4B;IAAS,CAAA,GAC9E,kBAAC,GAAD;KAAU,IAAI,GAAG,EAAI;KAAe,MAAM;KAAG,OAAO,EAAE;KAAa,WAAW,MAAM,EAAI,EAAE,aAAa,EAAE,OAAO,MAAM,CAAC;IAAI,CAAA,CACxH;;IACH,KAAU,CAAC,MAAS,kBAAC,GAAD;IAAa;IAAM,KAAK,EAAG;IAAU,QAAQ,EAAG;IAAQ,SAAS,MAAS,KAAK,EAAG,SAAS,CAAI,CAAC,CAAC,MAAM,CAAI;GAAI,CAAA;GACrI,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,kBAAC,QAAD;MAAM,WAAU;MAAuB,UAAA,EAAE,qBAAqB;KAAQ,CAAA;KACrE,IAAe,EAAa,CAAM,IAAI,kBAAC,GAAD;MAAU,cAAY,EAAO;MAAe,MAAM;MAAI,OAAO,EAAE;MAAM,aAAa,EAAO;MAAa,WAAW,MAAM,EAAI,EAAE,MAAM,EAAE,OAAO,MAAM,CAAC;KAAI,CAAA;KAC7L,CAAC,KAAQ,kBAAC,KAAD;MAAG,WAAU;MAAiC,UAAA,EAAE,kCAAkC;KAAK,CAAA;IAC9F;;GACJ,KAAS,kBAAC,IAAD,EAAA,UAAY,OAAO,KAAU,WAAW,IAAQ,EAAa,GAAG,CAAK,EAAa,CAAA;GAC3F,MAAU,CAAC,KACV,kBAAC,KAAD;IAAG,MAAK;IAAS,WAAU;IACxB,UAAA;GACA,CAAA;GAEL,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,kBAAC,GAAD;MAAQ,MAAK;MAAS,UAAU,EAAG;MAChC,UAAA,EAAG,SAAS,EAAE,eAAe,IAAI,EAAE,IAAO,gBAAgB,4BAA4B;KACjF,CAAA;KACP,KAAQ,CAAC,EAAK,QACb,kBAAC,GAAD;MAAQ,MAAK;MAAS,SAAQ;MAAU,UAAU,EAAG;MAAQ,eAAe,KAAK,GAAQ,EAAI;MAC1F,UAAA,EAAE,wBAAwB;KACrB,CAAA;KAET,GAAM,UAAU,eACf,kBAAC,GAAD;MAAQ,MAAK;MAAS,SAAQ;MAAU,UAAU,EAAG;MAAQ,eAAe,KAAK,GAAQ,EAAK;MAC3F,UAAA,EAAE,0BAA0B;KACvB,CAAA;KAET,KACC,kBAAC,GAAD;MAAQ,MAAK;MAAS,SAAQ;MAAc,WAAU;MAAU,UAAU,EAAG;MAAQ,eAAe,KAAK,GAAO;MAC7G,UAAA,EAAE,uBAAuB;KACpB,CAAA;IAEP;;GACJ;EACG;CACL,CAAA,GAAA,KAAQ,kBAAC,GAAD;EAAiB,SAAS;EAAc;EAAiB;EAAiB;CAAS,CAAA,CAC1F,EAAA,CAAA;AAEN;AAEA,SAAS,EAAM,EAAE,SAAM,QAAK,WAAQ,aAAwG;CAC1I,IAAM,EAAE,SAAM,EAAY,GACpB,IAAO,EAAyB,IAAI;CAC1C,OACE,kBAAC,OAAD;EAAK,WAAU;EAAe,aAAU;EAAxC,UAAA,CACE,kBAAC,QAAD;GAAM,WAAU;GAAuB,UAAA,EAAE,sBAAsB;EAAQ,CAAA,GACtE,IACC,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA;IACG,IAAM,kBAAC,OAAD;KAAU;KAAK,KAAI;KAAG,WAAU;IAA0D,CAAA,IAAI,kBAAC,QAAD;KAAM,WAAU;KAAyB,UAAA,EAAE,wBAAwB;IAAQ,CAAA;IAChL,kBAAC,GAAD;KAAQ,MAAK;KAAS,MAAK;KAAK,SAAQ;KAAU,UAAU;KAAQ,eAAe,EAAK,SAAS,MAAM;KACpG,UAAA,EAAE,IAAM,4BAA4B,yBAAyB;IACxD,CAAA;IACP,KACC,kBAAC,GAAD;KAAQ,MAAK;KAAS,MAAK;KAAK,SAAQ;KAAQ,UAAU;KAAQ,eAAe,EAAO,IAAI;KACzF,UAAA,EAAE,wBAAwB;IACrB,CAAA;IAEV,kBAAC,SAAD;KACE,KAAK;KACL,MAAK;KACL,QAAO;KACP,QAAA;KACA,cAAY,EAAE,sBAAsB;KACpC,WAAW,MAAM;MACf,IAAM,IAAI,EAAE,OAAO,QAAQ;MAE3B,AADA,EAAE,OAAO,QAAQ,IACb,KAAG,EAAO,CAAC;KACjB;IACD,CAAA;GACE;EAEL,CAAA,IAAA,kBAAC,KAAD;GAAG,WAAU;GAAiC,UAAA,EAAE,kCAAkC;EAAK,CAAA,CAEtF;;AAET"}
1
+ {"version":3,"file":"content-manager.js","names":[],"sources":["../../../src/components/admin/content-manager.tsx"],"sourcesContent":["import { useEffect, useId, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../../client/errors.js\";\nimport { imageRefs } from \"../../client/contents.js\";\nimport type { Content } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useAdminLocation, useFlowClient, useStaffAccess, type AdminLocation, type FlowClientOptions, type StaffAccess } from \"../../react/admin.js\";\nimport { useContentKitClient, useErrorReporter, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContent } from \"../../react/contents.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport { keyOf, useContentScope, usePaged } from \"../../react/use-resource.js\";\nimport { Alert, AlertDescription } from \"#ckui/ui/alert\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button, buttonVariants } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Textarea } from \"#ckui/ui/textarea\";\nimport { localInput } from \"../social/bans.js\";\nimport { contentError, ErrorLine, formatDate, RelativeTime, useAction } from \"../social/parts.js\";\nimport { ContentSections } from \"../content/editors.js\";\nimport type { Labels } from \"../groups/parts.js\";\nimport { AdminRoot, LoadFailed, NoAccess, Pager, Rows, pageOf, useConfirm, type AdminFlowProps, type AdminRouteProps } from \"./shell.js\";\n\ntype Choice = string | { value: string; label: string };\n\n/** What ContentManager hands its body editor. */\nexport interface BodyEditorProps {\n /** The body as HTML, its images at URLs that show them now. */\n value: string;\n onChange: (html: string) => void;\n /** Uploads an image to the item and resolves with a URL that shows it; absent until the item exists. */\n uploadImage?: (file: File) => Promise<string>;\n disabled?: boolean;\n placeholder?: string;\n \"aria-label\"?: string;\n}\n\nexport interface ContentManagerProps extends AdminFlowProps, AdminRouteProps {\n /** The content kind managed. */\n kind: string;\n /** Languages to write in: codes, or codes with labels; the first is a new item's. */\n languages?: readonly Choice[];\n /**\n * The body editor: `richTextEditor` from `@openrails/contentkit-ui/editor`,\n * or the host's own. Without it, the body is a plain HTML field.\n */\n renderEditor?: (props: BodyEditorProps) => ReactNode;\n /** A link to a live item's page. */\n contentHref?: (content: Content) => string | undefined;\n /** Display names of kinds, traits, tiers, attributes and values by identifier. */\n labels?: Labels;\n pageSize?: number;\n}\n\n/** Whether the caller manages content: root:content:write holders, creators and owner-group editors. */\nexport function useContentManagerAccess(o: FlowClientOptions = {}): StaffAccess<\"contents\"> {\n return useStaffAccess([\"contents\"] as const, o);\n}\n\nexport type ContentStatus = \"draft\" | \"scheduled\" | \"published\" | \"held\" | \"rejected\";\n\n/** An item's status as the manager names it. */\nexport function contentStatus(c: Content): ContentStatus {\n if (c.moderation === \"rejected\") return \"rejected\";\n if (c.moderation === \"held\") return \"held\";\n if (c.state === \"draft\") return \"draft\";\n if (!c.live) return \"scheduled\";\n return \"published\";\n}\n\nconst choices = (list?: readonly Choice[]) => (list ?? []).map((c) => (typeof c === \"string\" ? { value: c, label: c } : c));\nconst badge: Record<ContentStatus, \"default\" | \"secondary\" | \"outline\" | \"destructive\"> = { published: \"default\", scheduled: \"secondary\", draft: \"outline\", held: \"secondary\", rejected: \"destructive\" };\nconst states = [\"draft\", \"scheduled\", \"published\"] as const;\n\n/**\n * Content items of one kind for their creators and staff: every one the\n * caller manages, by state and text, deleted ones on their own to restore;\n * create and edit title, slug, description, schedule, cover and body with\n * inline images per language, publish or keep a draft, and delete; then its\n * listing and attributes, access rule and price, versions (order, languages,\n * traits, schedule, access override, price, segments, media) and groups. The\n * list is at basePath, a new item at basePath/new and each item at\n * basePath/{id}, filters in the query.\n */\nexport function ContentManager({ client: own, baseUrl, auth, guard, appearance, className, language, messages, ...p }: ContentManagerProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"content-manager\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Contents {...p} client={client} />\n </AdminRoot>\n );\n}\n\ntype Inner = Omit<ContentManagerProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\"> & { client: ContentKitClient };\n\nfunction Contents({ basePath, navigation, ...p }: Inner) {\n const { t } = useMessages();\n const loc = useAdminLocation(basePath, navigation);\n const access = useContentManagerAccess({ client: p.client });\n if (access.loading) return <Rows />;\n if (!access.allowed.contents) return <NoAccess>{t(\"contentManager.noAccess\")}</NoAccess>;\n if (!loc.view) return <List {...p} loc={loc} />;\n return (\n <>\n <Button variant=\"ghost\" size=\"sm\" className=\"justify-self-start\" onClick={() => loc.go(\"\")}>\n {t(\"contentManager.back\")}\n </Button>\n <ContentEditor\n management\n key={loc.view}\n {...p}\n id={loc.view === \"new\" ? null : loc.view}\n onCreated={(c) => loc.go(c.id, undefined, { replace: true })}\n onDeleted={() => loc.go(\"\", undefined, { replace: true })}\n />\n </>\n );\n}\n\nfunction List({ loc, client, kind, languages: offered, contentHref, pageSize = 20, onError }: Inner & { loc: AdminLocation }) {\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const language = choices(offered)[0]?.value;\n const query = loc.query.get(\"q\") ?? \"\";\n const status = states.find((s) => s === loc.query.get(\"status\")) ?? \"\";\n const deleted = loc.query.get(\"deleted\") === \"1\";\n const page = pageOf(loc.query);\n const filters = { q: query, status, deleted: deleted ? \"1\" : undefined };\n const filter = (next: Partial<typeof filters>) => loc.go(\"\", { ...filters, ...next });\n const [text, setText] = useState(query);\n useEffect(() => setText(query), [query]);\n const { store, scope } = useContentScope(client);\n const state = deleted ? \"deleted\" : status || undefined;\n const list = usePaged(store, keyOf(\"contents-page\", scope, kind, query, state, pageSize), { type: \"contents\", scope: deleted ? \"deleted\" : undefined }, page, pageSize, (q, signal) =>\n client.contents.manage({ ...q, kind, q: query || undefined, state, language }, signal),\n );\n const del = useAction(\"content.save\", onError);\n const { confirm, dialog } = useConfirm();\n const remove = async (c: Content) => {\n if (await confirm({ title: t(\"contentManager.deleteTitle\", { title: c.title }), description: t(\"contentManager.deleteBody\"), confirm: t(\"contentManager.delete\") })) {\n await del.run(() => client.contents.remove(c.id));\n }\n };\n return (\n <div className=\"grid gap-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"text-base font-semibold\">{t(\"contentManager.title\")}</h2>\n <Button size=\"sm\" onClick={() => loc.go(\"new\")}>\n {t(\"contentManager.new\")}\n </Button>\n </div>\n <div className=\"flex gap-2\" role=\"group\" aria-label={t(\"contentManager.title\")}>\n <Button size=\"sm\" variant={deleted ? \"outline\" : \"default\"} aria-pressed={!deleted} onClick={() => loc.go(\"\", { q: query })}>\n {t(\"contentManager.items\")}\n </Button>\n <Button size=\"sm\" variant={deleted ? \"default\" : \"outline\"} aria-pressed={deleted} onClick={() => loc.go(\"\", { q: query, deleted: \"1\" })}>\n {t(\"contentManager.deleted\")}\n </Button>\n </div>\n <div className=\"grid gap-2 sm:grid-cols-[minmax(0,1fr)_auto]\">\n <form\n className=\"flex gap-2\"\n role=\"search\"\n onSubmit={(e) => {\n e.preventDefault();\n filter({ q: text.trim() });\n }}\n >\n <Label htmlFor={`${ids}-q`} className=\"sr-only\">\n {t(\"contentManager.search\")}\n </Label>\n <Input id={`${ids}-q`} className=\"h-8\" type=\"search\" value={text} placeholder={t(\"contentManager.search\")} onChange={(e) => setText(e.target.value)} />\n <Button type=\"submit\" size=\"sm\" variant=\"outline\">\n {t(\"moderation.filter\")}\n </Button>\n </form>\n {!deleted && (\n <Select value={status} onValueChange={(v) => filter({ status: String(v ?? \"\") as typeof status })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"contentManager.status\")}>\n <SelectValue>{(v: string) => t(v ? `contentManager.${v as (typeof states)[number]}` : \"contentManager.allStatuses\")}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{t(\"contentManager.allStatuses\")}</SelectItem>\n {states.map((s) => (\n <SelectItem key={s} value={s}>\n {t(`contentManager.${s}`)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </div>\n <ErrorLine error={del.error} />\n {list.loading && !list.items.length ? (\n <Rows />\n ) : list.error && !list.items.length ? (\n <LoadFailed error={list.error} message={t(\"contentManager.loadFailed\")} retry={list.reload} />\n ) : !list.items.length ? (\n <p className=\"text-muted-foreground\">{t(deleted ? \"contentManager.noDeleted\" : \"contentManager.empty\")}</p>\n ) : (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\" aria-busy={list.loading}>\n {list.items.map((c) => {\n const s = contentStatus(c);\n const href = s === \"published\" ? contentHref?.(c) : undefined;\n return (\n <li key={c.id} className=\"flex flex-wrap items-start gap-3 p-3\" data-ckui=\"content-row\" data-status={deleted ? \"deleted\" : s}>\n <div className=\"grid min-w-0 flex-1 gap-1\">\n {deleted ? (\n <span className=\"font-medium break-words\">{c.title}</span>\n ) : (\n <button type=\"button\" className=\"text-left font-medium break-words hover:underline\" onClick={() => loc.go(c.id)}>\n {c.title}\n </button>\n )}\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted-foreground\">\n {!deleted && <Badge variant={badge[s]}>{t(`contentManager.${s}`)}</Badge>}\n <span>{c.language}</span>\n <RelativeTime at={deleted && c.deleted_at ? c.deleted_at : c.updated_at} />\n {s === \"scheduled\" && c.publish_at && <span>{t(\"pollManager.goesLive\", { date: formatDate(m, c.publish_at) })}</span>}\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {deleted ? (\n <Button size=\"sm\" variant=\"outline\" disabled={del.pending} onClick={() => void del.run(() => client.contents.restore(c.id))}>\n {t(\"moderation.restore\")}\n </Button>\n ) : (\n <>\n {href && (\n <a className={buttonVariants({ size: \"sm\", variant: \"ghost\" })} href={href}>\n {t(\"contentManager.view\")}\n </a>\n )}\n <Button size=\"sm\" variant=\"outline\" onClick={() => loc.go(c.id)}>\n {t(\"comments.edit\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" disabled={del.pending} onClick={() => void remove(c)}>\n {t(\"comments.delete\")}\n </Button>\n </>\n )}\n </div>\n </li>\n );\n })}\n </ul>\n )}\n <Pager page={page} hasNext={list.hasNext} disabled={list.loading} onPage={(n) => loc.go(\"\", { ...filters, page: n > 1 ? n : undefined })} />\n {dialog}\n </div>\n );\n}\n\ninterface Form {\n title: string;\n slug: string;\n description: string;\n publishAt: string;\n body: string;\n}\n\nconst empty: Form = { title: \"\", slug: \"\", description: \"\", publishAt: \"\", body: \"\" };\n\nexport interface ContentEditorProps {\n /** Staff entity lookup, including internal classifications. */\n management?: boolean;\n /** The item edited; null creates one of kind. */\n id: string | null;\n kind: string;\n /** Languages to write in: codes, or codes with labels; the first is a new item's. */\n languages?: readonly Choice[];\n /** The body editor; without it, a plain HTML field. */\n renderEditor?: (props: BodyEditorProps) => ReactNode;\n /** A link to a live item's page. */\n contentHref?: (content: Content) => string | undefined;\n labels?: Labels;\n /** A new item's owner group: a channel its editors write for. */\n ownerGroup?: string;\n /** After create: show the new item (the editor goes on editing it). */\n onCreated?: (content: Content) => void;\n onDeleted?: (content: Content) => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n}\n\n/**\n * One content item for its creators and staff: title, slug, description,\n * schedule, cover and body per language, publish, unpublish and delete;\n * then listing and attributes, access rule and price, versions and groups.\n */\nexport function ContentEditor({ id, client: own, kind, languages: offered, renderEditor, contentHref, labels, ownerGroup, onCreated, onDeleted, onError, management }: ContentEditorProps) {\n const client = useContentKitClient(own);\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const languages = choices(offered);\n const [language, setLanguage] = useState(languages[0]?.value ?? \"en\");\n const ed = useContent(id, kind, language, { client });\n // A hidden version's cover is edited here; a visible version's in its own form.\n const hidden = !!useKinds({ client }).kinds?.kinds[kind]?.versions.hidden;\n const report = useErrorReporter(onError);\n // The body is kept as stored (image references); the editor shows it with URLs.\n const [form, setForm] = useState<Form | null>(id ? null : empty);\n const [error, setError] = useState<ContentKitError | string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const { confirm, dialog } = useConfirm();\n const item = ed.content;\n useEffect(() => {\n if (!item) return;\n const stored = item.text?.[language];\n setForm((f) => f ?? { title: stored?.title ?? \"\", slug: stored?.slug ?? \"\", description: stored?.description ?? \"\", body: stored?.body ?? \"\",\n publishAt: item.publish_at && !item.live ? localInput(new Date(item.publish_at)) : \"\" });\n }, [item, language]);\n const { imageSrc } = ed;\n const shown = useMemo(\n () => (form?.body ?? \"\").replace(imageRefs(), (ref, name: string) => {\n const src = imageSrc(name);\n return src && src !== name ? src.replaceAll(\"&\", \"&amp;\") : ref;\n }),\n [form?.body, imageSrc],\n );\n if (id && !form) {\n if (ed.error) return <LoadFailed error={ed.error} message={t(\"contentManager.loadFailed\")} retry={ed.reload} />;\n return (\n <div className=\"grid gap-3\" aria-busy>\n <Skeleton className=\"h-9 w-full\" />\n <Skeleton className=\"h-48 w-full\" />\n </div>\n );\n }\n const f = form!;\n const set = (patch: Partial<Form>) => setForm((cur) => ({ ...cur!, ...patch }));\n const fail = (e: unknown) => {\n const err = toContentKitError(e);\n setError(err);\n report(err, \"content.save\");\n };\n const text = () => ({ title: f.title.trim(), slug: f.slug.trim() || undefined, description: f.description.trim(), body: f.body });\n const save = async () => {\n setError(null);\n setNotice(null);\n if (!f.title.trim()) return setError(t(\"contentManager.titleRequired\"));\n try {\n if (!item) {\n const created = await ed.create({ kind, text: { [language]: text() }, owner_group_id: ownerGroup });\n onCreated?.(created);\n return;\n }\n const saved = await ed.putText(text());\n setNotice(t(\"pollEditor.saved\"));\n setForm((cur) => ({ ...cur!, slug: saved.text?.[language]?.slug ?? cur!.slug }));\n } catch (e) {\n fail(e);\n }\n };\n const publish = async (live: boolean) => {\n setError(null);\n setNotice(null);\n try {\n if (live) await ed.publish(f.publishAt ? new Date(f.publishAt).toISOString() : undefined);\n else await ed.unpublish();\n setNotice(t(live ? \"contentManager.publishedNotice\" : \"contentManager.unpublishedNotice\"));\n } catch (e) {\n fail(e);\n }\n };\n const remove = async () => {\n if (!item || !(await confirm({ title: t(\"contentManager.deleteTitle\", { title: item.title }), description: t(\"contentManager.deleteBody\"), confirm: t(\"contentManager.delete\") }))) return;\n try {\n await ed.remove();\n onDeleted?.(item);\n } catch (e) {\n fail(e);\n }\n };\n const s = item ? contentStatus(item) : \"draft\";\n const href = item && s === \"published\" ? contentHref?.(item) : undefined;\n const editor: BodyEditorProps = {\n value: shown,\n onChange: (html) => set({ body: ed.storedBody(html) }),\n uploadImage: item ? (file) => ed.uploadImage(file) : undefined,\n disabled: ed.saving,\n placeholder: t(\"contentManager.bodyPlaceholder\"),\n \"aria-label\": t(\"contentManager.body\"),\n };\n return (\n <>\n <form\n className=\"grid gap-4\"\n data-ckui=\"content-form\"\n onSubmit={(e) => {\n e.preventDefault();\n void save();\n }}\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold\">{t(item ? \"contentManager.editTitle\" : \"contentManager.newTitle\")}</h2>\n {item && <Badge variant={badge[s]}>{t(`contentManager.${s}`)}</Badge>}\n {href && (\n <a className=\"text-xs text-muted-foreground hover:underline\" href={href}>\n {t(\"contentManager.view\")}\n </a>\n )}\n </div>\n {item?.moderation && (\n <Alert variant={item.moderation === \"rejected\" ? \"destructive\" : \"default\"}>\n <AlertDescription>\n {item.moderation_reason\n ? t(`contentManager.${item.moderation === \"rejected\" ? \"rejectedBecause\" : \"heldBecause\"}`, { reason: item.moderation_reason })\n : t(`contentManager.${item.moderation === \"rejected\" ? \"rejectedNote\" : \"heldNote\"}`)}\n </AlertDescription>\n </Alert>\n )}\n {languages.length > 1 && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-lang`}>{t(\"contentManager.language\")}</Label>\n <Select\n value={language}\n onValueChange={(v) => {\n setLanguage(String(v ?? \"\"));\n setForm(item ? null : empty);\n }}\n >\n <SelectTrigger id={`${ids}-lang`} className=\"w-full\">\n <SelectValue>{(v: string) => languages.find((l) => l.value === v)?.label ?? v}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l.value} value={l.value}>\n {l.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-title`}>{t(\"contentManager.itemTitle\")}</Label>\n <Input id={`${ids}-title`} value={f.title} required maxLength={300} onChange={(e) => set({ title: e.target.value })} />\n </div>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-slug`}>{t(\"contentManager.slug\")}</Label>\n <Input id={`${ids}-slug`} value={f.slug} placeholder={t(\"contentManager.slugAuto\")} onChange={(e) => set({ slug: e.target.value })} />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-publish`}>{t(\"contentManager.publishAt\")}</Label>\n <Input id={`${ids}-publish`} type=\"datetime-local\" value={f.publishAt} onChange={(e) => set({ publishAt: e.target.value })} />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-description`}>{t(\"contentManager.description\")}</Label>\n <Textarea id={`${ids}-description`} rows={2} value={f.description} onChange={(e) => set({ description: e.target.value })} />\n </div>\n {(hidden || !item) && <Cover item={item} src={ed.coverSrc} saving={ed.saving} onFile={(file) => void ed.setCover(file).catch(fail)} />}\n <div className=\"grid gap-1.5\">\n <span className=\"text-sm font-medium\">{t(\"contentManager.body\")}</span>\n {renderEditor ? renderEditor(editor) : <Textarea aria-label={editor[\"aria-label\"]} rows={12} value={f.body} placeholder={editor.placeholder} onChange={(e) => set({ body: e.target.value })} />}\n {!item && <p className=\"text-xs text-muted-foreground\">{t(\"contentManager.imagesAfterCreate\")}</p>}\n </div>\n {error && <ErrorLine>{typeof error === \"string\" ? error : contentError(m, error)}</ErrorLine>}\n {notice && !error && (\n <p role=\"status\" className=\"text-sm text-muted-foreground\">\n {notice}\n </p>\n )}\n <div className=\"flex flex-wrap gap-2 border-t border-border pt-4\">\n <Button type=\"submit\" disabled={ed.saving}>\n {ed.saving ? t(\"common.saving\") : t(item ? \"common.save\" : \"contentManager.createDraft\")}\n </Button>\n {item && !item.live && (\n <Button type=\"button\" variant=\"outline\" disabled={ed.saving} onClick={() => void publish(true)}>\n {t(\"contentManager.publish\")}\n </Button>\n )}\n {item?.state === \"published\" && (\n <Button type=\"button\" variant=\"outline\" disabled={ed.saving} onClick={() => void publish(false)}>\n {t(\"contentManager.unpublish\")}\n </Button>\n )}\n {item && (\n <Button type=\"button\" variant=\"destructive\" className=\"ms-auto\" disabled={ed.saving} onClick={() => void remove()}>\n {t(\"contentManager.delete\")}\n </Button>\n )}\n </div>\n {dialog}\n </form>\n {item && <ContentSections management={management} content={item} client={client} onError={onError} labels={labels} />}\n </>\n );\n}\n\nfunction Cover({ item, src, saving, onFile }: { item: Content | null; src?: string; saving: boolean; onFile: (file: File | null) => void }) {\n const { t } = useMessages();\n const file = useRef<HTMLInputElement>(null);\n return (\n <div className=\"grid gap-1.5\" data-ckui=\"content-cover\">\n <span className=\"text-sm font-medium\">{t(\"contentManager.cover\")}</span>\n {item ? (\n <div className=\"flex flex-wrap items-center gap-3\">\n {src ? <img src={src} alt=\"\" className=\"h-20 w-36 rounded-md border border-border object-cover\" /> : <span className=\"text-muted-foreground\">{t(\"contentManager.noCover\")}</span>}\n <Button type=\"button\" size=\"sm\" variant=\"outline\" disabled={saving} onClick={() => file.current?.click()}>\n {t(src ? \"pollEditor.replaceImage\" : \"contentManager.addCover\")}\n </Button>\n {src && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" disabled={saving} onClick={() => onFile(null)}>\n {t(\"pollEditor.removeImage\")}\n </Button>\n )}\n <input\n ref={file}\n type=\"file\"\n accept=\"image/*\"\n hidden\n aria-label={t(\"contentManager.cover\")}\n onChange={(e) => {\n const f = e.target.files?.[0];\n e.target.value = \"\";\n if (f) onFile(f);\n }}\n />\n </div>\n ) : (\n <p className=\"text-xs text-muted-foreground\">{t(\"contentManager.imagesAfterCreate\")}</p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,82 +1,83 @@
1
1
  import { useLanguageName as e, useMessages as t } from "../../i18n/context.js";
2
2
  import { useGroups as n, useKinds as r } from "../../react/groups.js";
3
3
  import { Button as i } from "../../ui/button.js";
4
- import { Select as a, SelectContent as o, SelectItem as s, SelectTrigger as c, SelectValue as l } from "../../ui/select.js";
5
- import { AdminRoot as u, LoadFailed as d, NoAccess as f, Rows as p } from "./shell.js";
6
- import { useAdminLocation as m, useFlowClient as h, useStaffAccess as g } from "../../react/admin.js";
7
- import { Tabs as _, TabsList as v, TabsTrigger as y } from "../../ui/tabs.js";
8
- import { LETTERS as b } from "../../client/group-fields.js";
9
- import { labelOf as x } from "../groups/parts.js";
10
- import { GroupEditor as S } from "../groups/editor.js";
11
- import { GroupMembers as C } from "../groups/memberships.js";
12
- import { useMemo as w } from "react";
13
- import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
4
+ import { Badge as a } from "../../ui/badge.js";
5
+ import { Select as o, SelectContent as s, SelectItem as c, SelectTrigger as l, SelectValue as u } from "../../ui/select.js";
6
+ import { AdminRoot as d, LoadFailed as f, NoAccess as p, Rows as m } from "./shell.js";
7
+ import { useAdminLocation as h, useFlowClient as g, useStaffAccess as _ } from "../../react/admin.js";
8
+ import { Tabs as v, TabsList as y, TabsTrigger as b } from "../../ui/tabs.js";
9
+ import { LETTERS as x } from "../../client/group-fields.js";
10
+ import { labelOf as S } from "../groups/parts.js";
11
+ import { GroupEditor as C } from "../groups/editor.js";
12
+ import { GroupMembers as w } from "../groups/memberships.js";
13
+ import { useMemo as T } from "react";
14
+ import { Fragment as E, jsx as D, jsxs as O } from "react/jsx-runtime";
14
15
  //#region src/components/admin/group-manager.tsx
15
- function O(e = {}) {
16
- return g(["groups"], e);
16
+ function k(e = {}) {
17
+ return _(["groups"], e);
17
18
  }
18
- var k = [
19
+ var A = [
19
20
  "name",
20
21
  "count",
21
22
  "newest"
22
23
  ];
23
- function A({ client: e, baseUrl: t, auth: n, guard: r, appearance: i, className: a, language: o, messages: s, ...c }) {
24
- let l = h({
24
+ function j({ client: e, baseUrl: t, auth: n, guard: r, appearance: i, className: a, language: o, messages: s, ...c }) {
25
+ let l = g({
25
26
  client: e,
26
27
  baseUrl: t,
27
28
  auth: n
28
29
  });
29
- return /* @__PURE__ */ E(u, {
30
+ return /* @__PURE__ */ D(d, {
30
31
  flow: "group-manager",
31
32
  appearance: i,
32
33
  className: a,
33
34
  language: o,
34
35
  messages: s,
35
36
  guard: r,
36
- children: /* @__PURE__ */ E(j, {
37
+ children: /* @__PURE__ */ D(M, {
37
38
  ...c,
38
39
  client: l
39
40
  })
40
41
  });
41
42
  }
42
- function j({ basePath: e, navigation: n, ...i }) {
43
- let { t: a } = t(), o = m(e, n), s = O({ client: i.client }), c = r({ client: i.client }), l = c.kinds, u = w(() => i.groupKinds ?? Object.entries(l?.entity_kinds ?? {}).filter(([, e]) => e.owner !== "user").map(([e]) => e).sort(), [i.groupKinds, l]);
44
- if (s.loading || c.loading) return /* @__PURE__ */ E(p, {});
45
- if (!s.allowed.groups) return /* @__PURE__ */ E(f, { children: a("groupManager.noAccess") });
46
- if (!l) return /* @__PURE__ */ E(d, {
43
+ function M({ basePath: e, navigation: n, ...i }) {
44
+ let { t: a } = t(), o = h(e, n), s = k({ client: i.client }), c = r({ client: i.client }), l = c.kinds, u = T(() => i.groupKinds ?? Object.entries(l?.entity_kinds ?? {}).filter(([, e]) => e.owner !== "user").map(([e]) => e).sort(), [i.groupKinds, l]);
45
+ if (s.loading || c.loading) return /* @__PURE__ */ D(m, {});
46
+ if (!s.allowed.groups) return /* @__PURE__ */ D(p, { children: a("groupManager.noAccess") });
47
+ if (!l) return /* @__PURE__ */ D(f, {
47
48
  error: c.error,
48
49
  message: a("groupManager.kindsFailed"),
49
50
  retry: c.reload
50
51
  });
51
- if (!u.length) return /* @__PURE__ */ E("p", {
52
+ if (!u.length) return /* @__PURE__ */ D("p", {
52
53
  className: "text-muted-foreground",
53
54
  children: a("groupManager.noKinds")
54
55
  });
55
- let [h, g] = o.view.split("/"), b = u.includes(h ?? "") ? h : u[0], S = {
56
+ let [d, g] = o.view.split("/"), _ = u.includes(d ?? "") ? d : u[0], x = {
56
57
  ...i,
57
58
  loc: o,
58
- kind: b,
59
+ kind: _,
59
60
  kinds: l
60
61
  };
61
- return /* @__PURE__ */ D(T, { children: [u.length > 1 && /* @__PURE__ */ E(_, {
62
- value: b,
62
+ return /* @__PURE__ */ O(E, { children: [u.length > 1 && /* @__PURE__ */ D(v, {
63
+ value: _,
63
64
  onValueChange: (e) => o.go(String(e)),
64
- children: /* @__PURE__ */ E(v, {
65
+ children: /* @__PURE__ */ D(y, {
65
66
  className: "flex-wrap group-data-horizontal/tabs:h-auto",
66
- children: u.map((e) => /* @__PURE__ */ E(y, {
67
+ children: u.map((e) => /* @__PURE__ */ D(b, {
67
68
  value: e,
68
69
  className: "h-7.5",
69
- children: x(i.labels, e)
70
+ children: S(i.labels, e)
70
71
  }, e))
71
72
  })
72
- }), g ? g === "new" ? /* @__PURE__ */ E(P, { ...S }, b) : /* @__PURE__ */ E(F, {
73
- ...S,
73
+ }), g ? g === "new" ? /* @__PURE__ */ D(F, { ...x }, _) : /* @__PURE__ */ D(I, {
74
+ ...x,
74
75
  id: g
75
- }, g) : /* @__PURE__ */ E(N, { ...S }, b)] });
76
+ }, g) : /* @__PURE__ */ D(P, { ...x }, _)] });
76
77
  }
77
- function M({ loc: e, kind: n }) {
78
+ function N({ loc: e, kind: n }) {
78
79
  let { t: r } = t();
79
- return /* @__PURE__ */ E(i, {
80
+ return /* @__PURE__ */ D(i, {
80
81
  variant: "ghost",
81
82
  size: "sm",
82
83
  className: "justify-self-start",
@@ -84,128 +85,140 @@ function M({ loc: e, kind: n }) {
84
85
  children: r("groupManager.back")
85
86
  });
86
87
  }
87
- function N({ loc: r, kind: u, kinds: f, client: m, contentKind: h, labels: g, pageSize: _ = 50 }) {
88
- let v = t(), { t: y } = v, S = e(), C = f.languages, w = r.query.get("letter") ?? "", T = b.includes(w) ? w : void 0, O = r.query.get("sort") ?? "", A = k.includes(O) ? O : "name", j = r.query.get("language") ?? "", M = C.includes(j) ? j : C[0], N = {
89
- letter: T,
90
- sort: A === "name" ? void 0 : A,
91
- language: M === C[0] ? void 0 : M
92
- }, P = (e) => r.go(u, {
93
- ...N,
88
+ function P({ loc: r, kind: d, kinds: p, client: h, contentKind: g, labels: _, pageSize: v = 50 }) {
89
+ let y = t(), { t: b } = y, C = e(), w = p.languages, T = r.query.get("letter") ?? "", E = x.includes(T) ? T : void 0, k = r.query.get("sort") ?? "", j = A.includes(k) ? k : "name", M = r.query.get("language") ?? "", N = w.includes(M) ? M : w[0], P = {
90
+ letter: E,
91
+ sort: j === "name" ? void 0 : j,
92
+ language: N === w[0] ? void 0 : N
93
+ }, F = (e) => r.go(d, {
94
+ ...P,
94
95
  ...e
95
- }), F = n({
96
- kind: u,
97
- letter: T,
98
- sort: A,
99
- language: M,
100
- contentKind: h,
101
- pageSize: _
102
- }, { client: m }), I = x(g, u);
103
- return /* @__PURE__ */ D("div", {
96
+ }), I = n({
97
+ kind: d,
98
+ letter: E,
99
+ sort: j,
100
+ language: N,
101
+ contentKind: g,
102
+ pageSize: v
103
+ }, {
104
+ client: h,
105
+ management: !0
106
+ }), L = S(_, d);
107
+ return /* @__PURE__ */ O("div", {
104
108
  className: "grid gap-4",
105
109
  "data-ckui": "group-list",
106
- "data-kind": u,
110
+ "data-kind": d,
107
111
  children: [
108
- /* @__PURE__ */ D("div", {
112
+ /* @__PURE__ */ O("div", {
109
113
  className: "flex flex-wrap items-center justify-between gap-2",
110
- children: [/* @__PURE__ */ E("h2", {
114
+ children: [/* @__PURE__ */ D("h2", {
111
115
  className: "text-base font-semibold",
112
- children: I
113
- }), /* @__PURE__ */ E(i, {
116
+ children: L
117
+ }), /* @__PURE__ */ D(i, {
114
118
  size: "sm",
115
- onClick: () => r.go(`${u}/new`),
116
- children: y("groupManager.new", { kind: I })
119
+ onClick: () => r.go(`${d}/new`),
120
+ children: b("groupManager.new", { kind: L })
117
121
  })]
118
122
  }),
119
- /* @__PURE__ */ D("div", {
123
+ /* @__PURE__ */ O("div", {
120
124
  className: "flex flex-wrap items-center gap-2",
121
- children: [/* @__PURE__ */ D(a, {
122
- value: A,
123
- onValueChange: (e) => P({ sort: e === "name" ? void 0 : String(e) }),
124
- children: [/* @__PURE__ */ E(c, {
125
+ children: [/* @__PURE__ */ O(o, {
126
+ value: j,
127
+ onValueChange: (e) => F({ sort: e === "name" ? void 0 : String(e) }),
128
+ children: [/* @__PURE__ */ D(l, {
125
129
  size: "sm",
126
- "aria-label": y("groupManager.sort"),
127
- children: /* @__PURE__ */ E(l, { children: (e) => y(`groupManager.sort_${e}`) })
128
- }), /* @__PURE__ */ E(o, { children: k.map((e) => /* @__PURE__ */ E(s, {
130
+ "aria-label": b("groupManager.sort"),
131
+ children: /* @__PURE__ */ D(u, { children: (e) => b(`groupManager.sort_${e}`) })
132
+ }), /* @__PURE__ */ D(s, { children: A.map((e) => /* @__PURE__ */ D(c, {
129
133
  value: e,
130
- children: y(`groupManager.sort_${e}`)
134
+ children: b(`groupManager.sort_${e}`)
131
135
  }, e)) })]
132
- }), C.length > 1 && /* @__PURE__ */ D(a, {
133
- value: M,
134
- onValueChange: (e) => P({ language: e === C[0] ? void 0 : String(e) }),
135
- children: [/* @__PURE__ */ E(c, {
136
+ }), w.length > 1 && /* @__PURE__ */ O(o, {
137
+ value: N,
138
+ onValueChange: (e) => F({ language: e === w[0] ? void 0 : String(e) }),
139
+ children: [/* @__PURE__ */ D(l, {
136
140
  size: "sm",
137
- "aria-label": y("groupManager.language"),
138
- children: /* @__PURE__ */ E(l, { children: (e) => S(e) })
139
- }), /* @__PURE__ */ E(o, { children: C.map((e) => /* @__PURE__ */ E(s, {
141
+ "aria-label": b("groupManager.language"),
142
+ children: /* @__PURE__ */ D(u, { children: (e) => C(e) })
143
+ }), /* @__PURE__ */ D(s, { children: w.map((e) => /* @__PURE__ */ D(c, {
140
144
  value: e,
141
- children: S(e)
145
+ children: C(e)
142
146
  }, e)) })]
143
147
  })]
144
148
  }),
145
- /* @__PURE__ */ E("nav", {
146
- "aria-label": y("groups.byLetter"),
149
+ /* @__PURE__ */ D("nav", {
150
+ "aria-label": b("groups.byLetter"),
147
151
  className: "flex flex-wrap gap-1",
148
152
  "data-ckui": "letters",
149
- children: [void 0, ...b].map((e) => /* @__PURE__ */ E(i, {
153
+ children: [void 0, ...x].map((e) => /* @__PURE__ */ D(i, {
150
154
  size: "xs",
151
- variant: T === e ? "default" : "ghost",
152
- "aria-pressed": T === e,
153
- "aria-label": e === void 0 ? void 0 : e === "#" ? y("groups.otherLetters") : e.toUpperCase(),
155
+ variant: E === e ? "default" : "ghost",
156
+ "aria-pressed": E === e,
157
+ "aria-label": e === void 0 ? void 0 : e === "#" ? b("groups.otherLetters") : e.toUpperCase(),
154
158
  className: "min-w-7",
155
- onClick: () => P({ letter: e }),
156
- children: e === void 0 ? y("groups.all") : e.toUpperCase()
159
+ onClick: () => F({ letter: e }),
160
+ children: e === void 0 ? b("groups.all") : e.toUpperCase()
157
161
  }, e ?? "all"))
158
162
  }),
159
- F.loading && !F.items.length ? /* @__PURE__ */ E(p, {}) : F.error && !F.items.length ? /* @__PURE__ */ E(d, {
160
- error: F.error,
161
- message: y("groupManager.loadFailed"),
162
- retry: F.reload
163
- }) : F.items.length ? /* @__PURE__ */ E("ul", {
163
+ I.loading && !I.items.length ? /* @__PURE__ */ D(m, {}) : I.error && !I.items.length ? /* @__PURE__ */ D(f, {
164
+ error: I.error,
165
+ message: b("groupManager.loadFailed"),
166
+ retry: I.reload
167
+ }) : I.items.length ? /* @__PURE__ */ D("ul", {
164
168
  className: "grid divide-y divide-border rounded-lg border border-border",
165
- "aria-busy": F.loading,
166
- children: F.items.map((e) => /* @__PURE__ */ D("li", {
169
+ "aria-busy": I.loading,
170
+ children: I.items.map((e) => /* @__PURE__ */ O("li", {
167
171
  className: "flex flex-wrap items-center gap-3 p-3",
168
172
  "data-ckui": "group-row",
169
173
  "data-slug": e.slug,
170
- children: [/* @__PURE__ */ D("div", {
174
+ children: [/* @__PURE__ */ O("div", {
171
175
  className: "grid min-w-0 flex-1 gap-0.5",
172
- children: [/* @__PURE__ */ E("button", {
173
- type: "button",
174
- className: "text-left font-medium break-words hover:underline",
175
- onClick: () => r.go(`${u}/${e.id}`),
176
- children: e.name || e.slug
177
- }), /* @__PURE__ */ E("span", {
178
- className: "text-xs text-muted-foreground",
179
- children: e.slug
180
- })]
181
- }), /* @__PURE__ */ E("span", {
176
+ children: [
177
+ /* @__PURE__ */ D("button", {
178
+ type: "button",
179
+ className: "text-left font-medium break-words hover:underline",
180
+ onClick: () => r.go(`${d}/${e.id}`),
181
+ children: e.name || e.slug
182
+ }),
183
+ /* @__PURE__ */ D("span", {
184
+ className: "text-xs text-muted-foreground",
185
+ children: e.slug
186
+ }),
187
+ e.internal && /* @__PURE__ */ D(a, {
188
+ variant: "secondary",
189
+ className: "justify-self-start",
190
+ children: b("groupEditor.internal")
191
+ })
192
+ ]
193
+ }), /* @__PURE__ */ D("span", {
182
194
  className: "text-sm text-muted-foreground tabular-nums",
183
- title: y("groupManager.count"),
184
- children: new Intl.NumberFormat(v.language).format(e.count)
195
+ title: b("groupManager.count"),
196
+ children: new Intl.NumberFormat(y.language).format(e.count)
185
197
  })]
186
198
  }, e.id))
187
- }) : /* @__PURE__ */ E("p", {
199
+ }) : /* @__PURE__ */ D("p", {
188
200
  className: "text-muted-foreground",
189
- children: y("groups.empty")
201
+ children: b("groups.empty")
190
202
  }),
191
- F.hasMore && /* @__PURE__ */ E(i, {
203
+ I.hasMore && /* @__PURE__ */ D(i, {
192
204
  variant: "outline",
193
205
  size: "sm",
194
206
  className: "justify-self-center",
195
- disabled: F.loadingMore,
196
- onClick: () => void F.loadMore(),
197
- children: F.loadingMore ? y("groups.loading") : y("groups.loadMore")
207
+ disabled: I.loadingMore,
208
+ onClick: () => void I.loadMore(),
209
+ children: I.loadingMore ? b("groups.loading") : b("groups.loadMore")
198
210
  })
199
211
  ]
200
212
  });
201
213
  }
202
- function P({ loc: e, kind: t, client: n, labels: r, onError: i }) {
203
- return /* @__PURE__ */ D("div", {
214
+ function F({ loc: e, kind: t, client: n, labels: r, onError: i }) {
215
+ return /* @__PURE__ */ O("div", {
204
216
  className: "grid gap-4",
205
- children: [/* @__PURE__ */ E(M, {
217
+ children: [/* @__PURE__ */ D(N, {
206
218
  loc: e,
207
219
  kind: t
208
- }), /* @__PURE__ */ E(S, {
220
+ }), /* @__PURE__ */ D(C, {
221
+ management: !0,
209
222
  id: null,
210
223
  kind: t,
211
224
  labels: r,
@@ -215,20 +228,21 @@ function P({ loc: e, kind: t, client: n, labels: r, onError: i }) {
215
228
  })]
216
229
  });
217
230
  }
218
- function F({ loc: e, kind: t, client: n, labels: r, onError: i, renderContent: a, id: o }) {
219
- return /* @__PURE__ */ D("div", {
231
+ function I({ loc: e, kind: t, client: n, labels: r, onError: i, renderContent: a, id: o }) {
232
+ return /* @__PURE__ */ O("div", {
220
233
  className: "grid gap-4",
221
- children: [/* @__PURE__ */ E(M, {
234
+ children: [/* @__PURE__ */ D(N, {
222
235
  loc: e,
223
236
  kind: t
224
- }), /* @__PURE__ */ E(S, {
237
+ }), /* @__PURE__ */ D(C, {
238
+ management: !0,
225
239
  id: o,
226
240
  kind: t,
227
241
  labels: r,
228
242
  client: n,
229
243
  onError: i,
230
244
  onMerged: (n) => e.go(`${t}/${n.into}`, void 0, { replace: !0 }),
231
- children: (e) => /* @__PURE__ */ E(C, {
245
+ children: (e) => /* @__PURE__ */ D(w, {
232
246
  group: e,
233
247
  labels: r,
234
248
  client: n,
@@ -240,6 +254,6 @@ function F({ loc: e, kind: t, client: n, labels: r, onError: i, renderContent: a
240
254
  });
241
255
  }
242
256
  //#endregion
243
- export { A as GroupManager, O as useGroupManagerAccess };
257
+ export { j as GroupManager, k as useGroupManagerAccess };
244
258
 
245
259
  //# sourceMappingURL=group-manager.js.map