@openrails/contentkit-ui 0.71.0 → 0.72.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 (120) hide show
  1. package/README.md +98 -10
  2. package/dist/client/content/bans.d.ts +2 -0
  3. package/dist/client/content/bans.js +24 -12
  4. package/dist/client/content/bans.js.map +1 -1
  5. package/dist/client/content/comments.d.ts +2 -0
  6. package/dist/client/content/comments.js.map +1 -1
  7. package/dist/client/content/polls.d.ts +9 -2
  8. package/dist/client/content/polls.js.map +1 -1
  9. package/dist/client/generated/routes.d.ts +28 -2
  10. package/dist/client/generated/routes.js +40 -0
  11. package/dist/client/generated/routes.js.map +1 -1
  12. package/dist/client/generated/wire.d.ts +4 -0
  13. package/dist/client/generated/wire.js.map +1 -1
  14. package/dist/client/index.d.ts +2 -2
  15. package/dist/client/taxonomy.d.ts +5 -1
  16. package/dist/client/taxonomy.js +7 -0
  17. package/dist/client/taxonomy.js.map +1 -1
  18. package/dist/components/admin/poll-manager.d.ts +28 -0
  19. package/dist/components/admin/poll-manager.js +297 -0
  20. package/dist/components/admin/poll-manager.js.map +1 -0
  21. package/dist/components/admin/post-manager.d.ts +45 -0
  22. package/dist/components/admin/post-manager.js +602 -0
  23. package/dist/components/admin/post-manager.js.map +1 -0
  24. package/dist/components/admin/shell.d.ts +73 -0
  25. package/dist/components/admin/shell.js +148 -0
  26. package/dist/components/admin/shell.js.map +1 -0
  27. package/dist/components/admin/taxonomy-manager.d.ts +54 -0
  28. package/dist/components/admin/taxonomy-manager.js +976 -0
  29. package/dist/components/admin/taxonomy-manager.js.map +1 -0
  30. package/dist/components/admin/user-comment-standing.d.ts +25 -0
  31. package/dist/components/admin/user-comment-standing.js +229 -0
  32. package/dist/components/admin/user-comment-standing.js.map +1 -0
  33. package/dist/components/social/bans.d.ts +5 -2
  34. package/dist/components/social/bans.js +132 -134
  35. package/dist/components/social/bans.js.map +1 -1
  36. package/dist/components/social/index.d.ts +1 -1
  37. package/dist/components/social/moderation.d.ts +11 -13
  38. package/dist/components/social/moderation.js +299 -218
  39. package/dist/components/social/moderation.js.map +1 -1
  40. package/dist/components/social/parts.d.ts +3 -2
  41. package/dist/components/social/parts.js +74 -48
  42. package/dist/components/social/parts.js.map +1 -1
  43. package/dist/components/social/poll-editor.js +1 -0
  44. package/dist/components/social/poll-editor.js.map +1 -1
  45. package/dist/editor/index.d.ts +1 -0
  46. package/dist/editor/rich-text-editor.d.ts +13 -0
  47. package/dist/editor/rich-text-editor.js +228 -0
  48. package/dist/editor/rich-text-editor.js.map +1 -0
  49. package/dist/editor.js +2 -0
  50. package/dist/index.d.ts +6 -0
  51. package/dist/index.js +13 -8
  52. package/dist/locales/admin/de.d.ts +2 -0
  53. package/dist/locales/admin/de.js +176 -0
  54. package/dist/locales/admin/de.js.map +1 -0
  55. package/dist/locales/admin/en.d.ts +171 -0
  56. package/dist/locales/admin/en.js +176 -0
  57. package/dist/locales/admin/en.js.map +1 -0
  58. package/dist/locales/admin/es.d.ts +2 -0
  59. package/dist/locales/admin/es.js +176 -0
  60. package/dist/locales/admin/es.js.map +1 -0
  61. package/dist/locales/admin/ja.d.ts +2 -0
  62. package/dist/locales/admin/ja.js +176 -0
  63. package/dist/locales/admin/ja.js.map +1 -0
  64. package/dist/locales/admin/ko.d.ts +2 -0
  65. package/dist/locales/admin/ko.js +176 -0
  66. package/dist/locales/admin/ko.js.map +1 -0
  67. package/dist/locales/admin/zh.d.ts +2 -0
  68. package/dist/locales/admin/zh.js +176 -0
  69. package/dist/locales/admin/zh.js.map +1 -0
  70. package/dist/locales/de.js +5 -3
  71. package/dist/locales/de.js.map +1 -1
  72. package/dist/locales/en.d.ts +176 -0
  73. package/dist/locales/en.js +5 -3
  74. package/dist/locales/en.js.map +1 -1
  75. package/dist/locales/es.js +5 -3
  76. package/dist/locales/es.js.map +1 -1
  77. package/dist/locales/ja.js +5 -3
  78. package/dist/locales/ja.js.map +1 -1
  79. package/dist/locales/ko.js +5 -3
  80. package/dist/locales/ko.js.map +1 -1
  81. package/dist/locales/social/de.js +8 -1
  82. package/dist/locales/social/de.js.map +1 -1
  83. package/dist/locales/social/en.d.ts +7 -0
  84. package/dist/locales/social/en.js +8 -1
  85. package/dist/locales/social/en.js.map +1 -1
  86. package/dist/locales/social/es.js +8 -1
  87. package/dist/locales/social/es.js.map +1 -1
  88. package/dist/locales/social/ja.js +8 -1
  89. package/dist/locales/social/ja.js.map +1 -1
  90. package/dist/locales/social/ko.js +8 -1
  91. package/dist/locales/social/ko.js.map +1 -1
  92. package/dist/locales/social/zh.js +8 -1
  93. package/dist/locales/social/zh.js.map +1 -1
  94. package/dist/locales/zh.js +5 -3
  95. package/dist/locales/zh.js.map +1 -1
  96. package/dist/provider.d.ts +2 -0
  97. package/dist/provider.js +23 -19
  98. package/dist/provider.js.map +1 -1
  99. package/dist/react/admin.d.ts +55 -0
  100. package/dist/react/admin.js +97 -0
  101. package/dist/react/admin.js.map +1 -0
  102. package/dist/react/comments.d.ts +18 -2
  103. package/dist/react/comments.js +24 -8
  104. package/dist/react/comments.js.map +1 -1
  105. package/dist/react/context.d.ts +7 -1
  106. package/dist/react/context.js +6 -1
  107. package/dist/react/context.js.map +1 -1
  108. package/dist/react/index.d.ts +4 -3
  109. package/dist/react/polls.js +88 -87
  110. package/dist/react/polls.js.map +1 -1
  111. package/dist/react/posts.js +123 -122
  112. package/dist/react/posts.js.map +1 -1
  113. package/dist/react/resources.js +3 -0
  114. package/dist/react/resources.js.map +1 -1
  115. package/dist/react/use-resource.d.ts +16 -0
  116. package/dist/react/use-resource.js +20 -1
  117. package/dist/react/use-resource.js.map +1 -1
  118. package/dist/react.js +21 -20
  119. package/dist/styles.css +1 -1
  120. package/package.json +37 -2
@@ -0,0 +1,297 @@
1
+ import { useMessages as e } from "../../i18n/context.js";
2
+ import { Button as t } from "../../ui/button.js";
3
+ import { keyOf as n, useContentScope as r, usePaged as i } from "../../react/use-resource.js";
4
+ import { Badge as a } from "../../ui/badge.js";
5
+ import { Input as o } from "../../ui/input.js";
6
+ import { Label as s } from "../../ui/label.js";
7
+ import { Select as c, SelectContent as l, SelectItem as u, SelectTrigger as d, SelectValue as f } from "../../ui/select.js";
8
+ import { ErrorLine as p, formatDate as m, useAction as h } from "../social/parts.js";
9
+ import { AdminRoot as g, LoadFailed as _, NoAccess as v, Pager as y, Rows as b, pageOf as x, useConfirm as S } from "./shell.js";
10
+ import { PollEditor as C } from "../social/poll-editor.js";
11
+ import { useAdminLocation as w, useFlowClient as T, useStaffAccess as E } from "../../react/admin.js";
12
+ import { useEffect as D, useId as O, useState as k } from "react";
13
+ import { Fragment as A, jsx as j, jsxs as M } from "react/jsx-runtime";
14
+ //#region src/components/admin/poll-manager.tsx
15
+ function N(e = {}) {
16
+ return E(["polls"], e);
17
+ }
18
+ function P(e, t = Date.now()) {
19
+ return e.is_active ? Date.parse(e.live_at) > t ? "scheduled" : e.closes_at && Date.parse(e.closes_at) <= t ? "closed" : "live" : "inactive";
20
+ }
21
+ var F = [
22
+ "live",
23
+ "scheduled",
24
+ "closed",
25
+ "inactive"
26
+ ], I = (e) => (e ?? []).map((e) => typeof e == "string" ? {
27
+ value: e,
28
+ label: e
29
+ } : e);
30
+ function L({ client: e, baseUrl: t, auth: n, guard: r, appearance: i, className: a, language: o, messages: s, ...c }) {
31
+ let l = T({
32
+ client: e,
33
+ baseUrl: t,
34
+ auth: n
35
+ });
36
+ return /* @__PURE__ */ j(g, {
37
+ flow: "poll-manager",
38
+ appearance: i,
39
+ className: a,
40
+ language: o,
41
+ messages: s,
42
+ guard: r,
43
+ children: /* @__PURE__ */ j(R, {
44
+ ...c,
45
+ client: l
46
+ })
47
+ });
48
+ }
49
+ function R({ basePath: n, navigation: r, ...i }) {
50
+ let { t: a } = e(), o = w(n, r), s = N({ client: i.client });
51
+ if (s.loading) return /* @__PURE__ */ j(b, {});
52
+ if (!s.allowed.polls) return /* @__PURE__ */ j(v, { children: a("pollManager.noAccess") });
53
+ if (!o.view) return /* @__PURE__ */ j(z, {
54
+ ...i,
55
+ loc: o
56
+ });
57
+ let c = /* @__PURE__ */ j(t, {
58
+ variant: "ghost",
59
+ size: "sm",
60
+ className: "justify-self-start",
61
+ onClick: () => o.go(""),
62
+ children: a("pollManager.back")
63
+ }), l = I(i.languages);
64
+ return /* @__PURE__ */ M(A, { children: [c, o.view === "new" ? /* @__PURE__ */ j(C, {
65
+ languages: l.length ? l : void 0,
66
+ freeText: i.freeText,
67
+ maxOptions: i.maxOptions,
68
+ client: i.client,
69
+ onError: i.onError,
70
+ onCreated: (e) => o.go(e.id, void 0, { replace: !0 })
71
+ }) : /* @__PURE__ */ j(C, {
72
+ poll: o.view,
73
+ maxOptions: i.maxOptions,
74
+ client: i.client,
75
+ onError: i.onError,
76
+ onDeleted: () => o.go("", void 0, { replace: !0 })
77
+ }, o.view)] });
78
+ }
79
+ function z({ loc: g, client: v, languages: C, pageSize: w = 20, onError: T }) {
80
+ let E = e(), { t: A } = E, N = O(), L = I(C), R = g.query.get("q") ?? "", z = g.query.get("language") ?? "", B = g.query.get("state") ?? "", V = F.includes(B) ? B : void 0, H = g.query.get("month") ?? "", U = g.query.get("date") ?? "", W = x(g.query), G = {
81
+ q: R,
82
+ language: z,
83
+ state: V,
84
+ month: H,
85
+ date: U
86
+ }, K = (e) => g.go("", {
87
+ ...G,
88
+ ...e
89
+ }), [q, J] = k(R);
90
+ D(() => J(R), [R]);
91
+ let { store: Y, scope: X } = r(v), Z = i(Y, n("polls-page", X, R, z, V, H, U, w), { type: "polls" }, W, w, (e, t) => v.polls.adminList({
92
+ ...e,
93
+ q: R || void 0,
94
+ language: z || void 0,
95
+ state: V,
96
+ month: H || void 0,
97
+ date: U || void 0
98
+ }, t)), Q = h("poll.save", T), { confirm: $, dialog: ee } = S(), te = async (e) => {
99
+ await $({
100
+ title: A("pollEditor.deleteTitle"),
101
+ description: A("pollEditor.deleteBody"),
102
+ confirm: A("pollEditor.delete"),
103
+ typed: A("admin.deleteWord")
104
+ }) && await Q.run(() => v.polls.delete(e.id));
105
+ };
106
+ return /* @__PURE__ */ M("div", {
107
+ className: "grid gap-4",
108
+ children: [
109
+ /* @__PURE__ */ M("div", {
110
+ className: "flex flex-wrap items-center justify-between gap-2",
111
+ children: [/* @__PURE__ */ j("h2", {
112
+ className: "text-base font-semibold",
113
+ children: A("pollManager.title")
114
+ }), /* @__PURE__ */ j(t, {
115
+ size: "sm",
116
+ onClick: () => g.go("new"),
117
+ children: A("pollManager.new")
118
+ })]
119
+ }),
120
+ /* @__PURE__ */ M("div", {
121
+ className: "grid gap-2 sm:grid-cols-[minmax(0,1fr)_auto]",
122
+ children: [/* @__PURE__ */ M("form", {
123
+ className: "flex gap-2",
124
+ role: "search",
125
+ onSubmit: (e) => {
126
+ e.preventDefault(), K({ q: q.trim() });
127
+ },
128
+ children: [
129
+ /* @__PURE__ */ j(s, {
130
+ htmlFor: `${N}-q`,
131
+ className: "sr-only",
132
+ children: A("pollManager.search")
133
+ }),
134
+ /* @__PURE__ */ j(o, {
135
+ id: `${N}-q`,
136
+ className: "h-8",
137
+ type: "search",
138
+ value: q,
139
+ placeholder: A("pollManager.search"),
140
+ onChange: (e) => J(e.target.value)
141
+ }),
142
+ /* @__PURE__ */ j(t, {
143
+ type: "submit",
144
+ size: "sm",
145
+ variant: "outline",
146
+ children: A("moderation.filter")
147
+ })
148
+ ]
149
+ }), /* @__PURE__ */ M("div", {
150
+ className: "flex flex-wrap gap-2",
151
+ children: [L.length > 0 && /* @__PURE__ */ M(c, {
152
+ value: z,
153
+ onValueChange: (e) => K({ language: String(e ?? "") }),
154
+ children: [/* @__PURE__ */ j(d, {
155
+ size: "sm",
156
+ "aria-label": A("pollEditor.language"),
157
+ children: /* @__PURE__ */ j(f, { children: (e) => e ? L.find((t) => t.value === e)?.label ?? e : A("pollManager.allLanguages") })
158
+ }), /* @__PURE__ */ M(l, { children: [/* @__PURE__ */ j(u, {
159
+ value: "",
160
+ children: A("pollManager.allLanguages")
161
+ }), L.map((e) => /* @__PURE__ */ j(u, {
162
+ value: e.value,
163
+ children: e.label
164
+ }, e.value))] })]
165
+ }), /* @__PURE__ */ M(c, {
166
+ value: V ?? "",
167
+ onValueChange: (e) => K({ state: String(e ?? "") || void 0 }),
168
+ children: [/* @__PURE__ */ j(d, {
169
+ size: "sm",
170
+ "aria-label": A("pollManager.state"),
171
+ children: /* @__PURE__ */ j(f, { children: (e) => A(e ? `pollManager.${e}` : "pollManager.allStates") })
172
+ }), /* @__PURE__ */ M(l, { children: [/* @__PURE__ */ j(u, {
173
+ value: "",
174
+ children: A("pollManager.allStates")
175
+ }), F.map((e) => /* @__PURE__ */ j(u, {
176
+ value: e,
177
+ children: A(`pollManager.${e}`)
178
+ }, e))] })]
179
+ })]
180
+ })]
181
+ }),
182
+ /* @__PURE__ */ M("div", {
183
+ className: "flex flex-wrap items-end gap-3",
184
+ children: [
185
+ /* @__PURE__ */ M("div", {
186
+ className: "grid gap-1",
187
+ children: [/* @__PURE__ */ j(s, {
188
+ htmlFor: `${N}-month`,
189
+ children: A("pollManager.month")
190
+ }), /* @__PURE__ */ j(o, {
191
+ id: `${N}-month`,
192
+ className: "h-8 w-44",
193
+ type: "month",
194
+ value: H,
195
+ onChange: (e) => K({
196
+ month: e.target.value,
197
+ date: ""
198
+ })
199
+ })]
200
+ }),
201
+ /* @__PURE__ */ M("div", {
202
+ className: "grid gap-1",
203
+ children: [/* @__PURE__ */ j(s, {
204
+ htmlFor: `${N}-date`,
205
+ children: A("pollManager.date")
206
+ }), /* @__PURE__ */ j(o, {
207
+ id: `${N}-date`,
208
+ className: "h-8 w-44",
209
+ type: "date",
210
+ value: U,
211
+ onChange: (e) => K({
212
+ date: e.target.value,
213
+ month: ""
214
+ })
215
+ })]
216
+ }),
217
+ (H || U || R || z || V) && /* @__PURE__ */ j(t, {
218
+ size: "sm",
219
+ variant: "ghost",
220
+ onClick: () => g.go(""),
221
+ children: A("pollManager.clear")
222
+ })
223
+ ]
224
+ }),
225
+ /* @__PURE__ */ j(p, { error: Q.error }),
226
+ Z.loading && !Z.items.length ? /* @__PURE__ */ j(b, {}) : Z.error && !Z.items.length ? /* @__PURE__ */ j(_, {
227
+ error: Z.error,
228
+ message: A("pollManager.loadFailed"),
229
+ retry: Z.reload
230
+ }) : Z.items.length ? /* @__PURE__ */ j("ul", {
231
+ className: "grid divide-y divide-border rounded-lg border border-border",
232
+ "aria-busy": Z.loading,
233
+ children: Z.items.map((e) => {
234
+ let n = P(e);
235
+ return /* @__PURE__ */ M("li", {
236
+ className: "flex flex-wrap items-start gap-3 p-3",
237
+ "data-ckui": "poll-row",
238
+ "data-state": n,
239
+ children: [/* @__PURE__ */ M("div", {
240
+ className: "grid min-w-0 flex-1 gap-1",
241
+ children: [/* @__PURE__ */ j("button", {
242
+ type: "button",
243
+ className: "text-left font-medium break-words hover:underline",
244
+ onClick: () => g.go(e.id),
245
+ children: e.question
246
+ }), /* @__PURE__ */ M("div", {
247
+ className: "flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted-foreground",
248
+ children: [
249
+ /* @__PURE__ */ j(a, {
250
+ variant: n === "live" ? "default" : n === "inactive" ? "outline" : "secondary",
251
+ children: A(`pollManager.${n}`)
252
+ }),
253
+ /* @__PURE__ */ j("span", { children: e.language }),
254
+ e.kind === "free_text" && /* @__PURE__ */ j("span", { children: A("pollEditor.freeText") }),
255
+ /* @__PURE__ */ j("span", { children: A(n === "scheduled" ? "pollManager.goesLive" : "pollManager.wentLive", { date: m(E, e.live_at) }) }),
256
+ e.closes_at && /* @__PURE__ */ j("span", { children: A("poll.closes", { date: m(E, e.closes_at) }) }),
257
+ /* @__PURE__ */ j("span", { children: e.kind === "free_text" ? E.plural("poll.answers", e.answer_count ?? 0) : E.plural("poll.votes", e.total_votes) })
258
+ ]
259
+ })]
260
+ }), /* @__PURE__ */ M("div", {
261
+ className: "flex gap-2",
262
+ children: [/* @__PURE__ */ j(t, {
263
+ size: "sm",
264
+ variant: "outline",
265
+ onClick: () => g.go(e.id),
266
+ children: A("comments.edit")
267
+ }), /* @__PURE__ */ j(t, {
268
+ size: "sm",
269
+ variant: "destructive",
270
+ disabled: Q.pending,
271
+ onClick: () => void te(e),
272
+ children: A("comments.delete")
273
+ })]
274
+ })]
275
+ }, e.id);
276
+ })
277
+ }) : /* @__PURE__ */ j("p", {
278
+ className: "text-muted-foreground",
279
+ children: A("pollManager.empty")
280
+ }),
281
+ /* @__PURE__ */ j(y, {
282
+ page: W,
283
+ hasNext: Z.hasNext,
284
+ disabled: Z.loading,
285
+ onPage: (e) => g.go("", {
286
+ ...G,
287
+ page: e > 1 ? e : void 0
288
+ })
289
+ }),
290
+ ee
291
+ ]
292
+ });
293
+ }
294
+ //#endregion
295
+ export { L as PollManager, P as pollState, N as usePollManagerAccess };
296
+
297
+ //# sourceMappingURL=poll-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll-manager.js","names":[],"sources":["../../../src/components/admin/poll-manager.tsx"],"sourcesContent":["import { useEffect, useId, useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { PollState } from \"../../client/content/polls.js\";\nimport type { Poll } 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 { keyOf, useContentScope, usePaged } from \"../../react/use-resource.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } 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 { PollEditor } from \"../social/poll-editor.js\";\nimport { ErrorLine, formatDate, useAction } from \"../social/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\nexport interface PollManagerProps extends AdminFlowProps, AdminRouteProps {\n /** Languages to filter by and write in: codes, or codes with labels; the first is a new poll's. */\n languages?: readonly Choice[];\n /** Offer free-text polls (the host registered an answer classifier). */\n freeText?: boolean;\n /** Most options per poll. Default 10. */\n maxOptions?: number;\n pageSize?: number;\n}\n\n/** Whether the caller may manage polls (root:polls:write). */\nexport function usePollManagerAccess(o: FlowClientOptions = {}): StaffAccess<\"polls\"> {\n return useStaffAccess([\"polls\"] as const, o);\n}\n\n/** A poll's state as the staff list names it. */\nexport function pollState(p: Poll, now = Date.now()): PollState {\n if (!p.is_active) return \"inactive\";\n if (Date.parse(p.live_at) > now) return \"scheduled\";\n if (p.closes_at && Date.parse(p.closes_at) <= now) return \"closed\";\n return \"live\";\n}\n\nconst STATES: readonly PollState[] = [\"live\", \"scheduled\", \"closed\", \"inactive\"];\nconst choices = (list?: readonly Choice[]) => (list ?? []).map((c) => (typeof c === \"string\" ? { value: c, label: c } : c));\n\n/**\n * Staff: every poll, by language, month or day, state and question, to\n * create, edit (PollEditor) and delete. The list is at basePath, a new poll\n * at basePath/new and each poll at basePath/{id}, filters in the query.\n */\nexport function PollManager({ client: own, baseUrl, auth, guard, appearance, className, language, messages, ...p }: PollManagerProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"poll-manager\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Polls {...p} client={client} />\n </AdminRoot>\n );\n}\n\ntype Inner = Omit<PollManagerProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\"> & { client: ContentKitClient };\n\nfunction Polls({ basePath, navigation, ...p }: Inner) {\n const { t } = useMessages();\n const loc = useAdminLocation(basePath, navigation);\n const access = usePollManagerAccess({ client: p.client });\n if (access.loading) return <Rows />;\n if (!access.allowed.polls) return <NoAccess>{t(\"pollManager.noAccess\")}</NoAccess>;\n if (!loc.view) return <List {...p} loc={loc} />;\n const back = (\n <Button variant=\"ghost\" size=\"sm\" className=\"justify-self-start\" onClick={() => loc.go(\"\")}>\n {t(\"pollManager.back\")}\n </Button>\n );\n const languages = choices(p.languages);\n return (\n <>\n {back}\n {loc.view === \"new\" ? (\n <PollEditor\n languages={languages.length ? languages : undefined}\n freeText={p.freeText}\n maxOptions={p.maxOptions}\n client={p.client}\n onError={p.onError}\n onCreated={(poll) => loc.go(poll.id, undefined, { replace: true })}\n />\n ) : (\n <PollEditor key={loc.view} poll={loc.view} maxOptions={p.maxOptions} client={p.client} onError={p.onError} onDeleted={() => loc.go(\"\", undefined, { replace: true })} />\n )}\n </>\n );\n}\n\nfunction List({ loc, client, languages: offered, pageSize = 20, onError }: Inner & { loc: AdminLocation }) {\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const languages = choices(offered);\n const query = loc.query.get(\"q\") ?? \"\";\n const language = loc.query.get(\"language\") ?? \"\";\n const stateParam = loc.query.get(\"state\") ?? \"\";\n const state = (STATES as readonly string[]).includes(stateParam) ? (stateParam as PollState) : undefined;\n const month = loc.query.get(\"month\") ?? \"\";\n const date = loc.query.get(\"date\") ?? \"\";\n const page = pageOf(loc.query);\n const filters = { q: query, language, state, month, date };\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 list = usePaged(store, keyOf(\"polls-page\", scope, query, language, state, month, date, pageSize), { type: \"polls\" }, page, pageSize, (q, signal) =>\n client.polls.adminList({ ...q, q: query || undefined, language: language || undefined, state, month: month || undefined, date: date || undefined }, signal),\n );\n const del = useAction(\"poll.save\", onError);\n const { confirm, dialog } = useConfirm();\n const remove = async (poll: Poll) => {\n const ok = await confirm({ title: t(\"pollEditor.deleteTitle\"), description: t(\"pollEditor.deleteBody\"), confirm: t(\"pollEditor.delete\"), typed: t(\"admin.deleteWord\") });\n if (ok) await del.run(() => client.polls.delete(poll.id));\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(\"pollManager.title\")}</h2>\n <Button size=\"sm\" onClick={() => loc.go(\"new\")}>\n {t(\"pollManager.new\")}\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(\"pollManager.search\")}\n </Label>\n <Input id={`${ids}-q`} className=\"h-8\" type=\"search\" value={text} placeholder={t(\"pollManager.search\")} onChange={(e) => setText(e.target.value)} />\n <Button type=\"submit\" size=\"sm\" variant=\"outline\">\n {t(\"moderation.filter\")}\n </Button>\n </form>\n <div className=\"flex flex-wrap gap-2\">\n {languages.length > 0 && (\n <Select value={language} onValueChange={(v) => filter({ language: String(v ?? \"\") })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"pollEditor.language\")}>\n <SelectValue>{(v: string) => (v ? (languages.find((l) => l.value === v)?.label ?? v) : t(\"pollManager.allLanguages\"))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{t(\"pollManager.allLanguages\")}</SelectItem>\n {languages.map((l) => (\n <SelectItem key={l.value} value={l.value}>\n {l.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <Select value={state ?? \"\"} onValueChange={(v) => filter({ state: (String(v ?? \"\") || undefined) as PollState | undefined })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"pollManager.state\")}>\n <SelectValue>{(v: string) => (v ? t(`pollManager.${v as PollState}`) : t(\"pollManager.allStates\"))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{t(\"pollManager.allStates\")}</SelectItem>\n {STATES.map((s) => (\n <SelectItem key={s} value={s}>\n {t(`pollManager.${s}`)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"flex flex-wrap items-end gap-3\">\n <div className=\"grid gap-1\">\n <Label htmlFor={`${ids}-month`}>{t(\"pollManager.month\")}</Label>\n <Input id={`${ids}-month`} className=\"h-8 w-44\" type=\"month\" value={month} onChange={(e) => filter({ month: e.target.value, date: \"\" })} />\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor={`${ids}-date`}>{t(\"pollManager.date\")}</Label>\n <Input id={`${ids}-date`} className=\"h-8 w-44\" type=\"date\" value={date} onChange={(e) => filter({ date: e.target.value, month: \"\" })} />\n </div>\n {(month || date || query || language || state) && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => loc.go(\"\")}>\n {t(\"pollManager.clear\")}\n </Button>\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(\"pollManager.loadFailed\")} retry={list.reload} />\n ) : !list.items.length ? (\n <p className=\"text-muted-foreground\">{t(\"pollManager.empty\")}</p>\n ) : (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\" aria-busy={list.loading}>\n {list.items.map((poll) => {\n const s = pollState(poll);\n return (\n <li key={poll.id} className=\"flex flex-wrap items-start gap-3 p-3\" data-ckui=\"poll-row\" data-state={s}>\n <div className=\"grid min-w-0 flex-1 gap-1\">\n <button type=\"button\" className=\"text-left font-medium break-words hover:underline\" onClick={() => loc.go(poll.id)}>\n {poll.question}\n </button>\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted-foreground\">\n <Badge variant={s === \"live\" ? \"default\" : s === \"inactive\" ? \"outline\" : \"secondary\"}>{t(`pollManager.${s}`)}</Badge>\n <span>{poll.language}</span>\n {poll.kind === \"free_text\" && <span>{t(\"pollEditor.freeText\")}</span>}\n <span>{t(s === \"scheduled\" ? \"pollManager.goesLive\" : \"pollManager.wentLive\", { date: formatDate(m, poll.live_at) })}</span>\n {poll.closes_at && <span>{t(\"poll.closes\", { date: formatDate(m, poll.closes_at) })}</span>}\n <span>{poll.kind === \"free_text\" ? m.plural(\"poll.answers\", poll.answer_count ?? 0) : m.plural(\"poll.votes\", poll.total_votes)}</span>\n </div>\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" variant=\"outline\" onClick={() => loc.go(poll.id)}>\n {t(\"comments.edit\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" disabled={del.pending} onClick={() => void remove(poll)}>\n {t(\"comments.delete\")}\n </Button>\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"],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import { ReactNode } from 'react';
2
+ import { Post } from '../../client/generated/wire.js';
3
+ import { FlowClientOptions, StaffAccess } from '../../react/admin.js';
4
+ import { AdminFlowProps, AdminRouteProps } from './shell.js';
5
+ type Choice = string | {
6
+ value: string;
7
+ label: string;
8
+ };
9
+ /** What PostManager hands its body editor. */
10
+ export interface PostEditorProps {
11
+ /** The body as HTML, its images at URLs that show them now. */
12
+ value: string;
13
+ onChange: (html: string) => void;
14
+ /** Uploads an image to the post and resolves with a URL that shows it; absent until the post exists. */
15
+ uploadImage?: (file: File) => Promise<string>;
16
+ disabled?: boolean;
17
+ placeholder?: string;
18
+ "aria-label"?: string;
19
+ }
20
+ export interface PostManagerProps extends AdminFlowProps, AdminRouteProps {
21
+ /** Languages to filter by and write in: codes, or codes with labels; the first is a new post's. */
22
+ languages?: readonly Choice[];
23
+ /**
24
+ * The body editor: `richTextEditor` from `@openrails/contentkit-ui/editor`,
25
+ * or the host's own. Without it, the body is a plain HTML field.
26
+ */
27
+ renderEditor?: (props: PostEditorProps) => ReactNode;
28
+ /** A link to a published post's page. */
29
+ postHref?: (post: Post) => string | undefined;
30
+ pageSize?: number;
31
+ }
32
+ /** Whether the caller may manage posts (root:posts:write). */
33
+ export declare function usePostManagerAccess(o?: FlowClientOptions): StaffAccess<"posts">;
34
+ export type PostStatus = "draft" | "scheduled" | "published" | "held" | "rejected";
35
+ /** A post's status as the staff list names it. */
36
+ export declare function postStatus(p: Post, now?: number): PostStatus;
37
+ /**
38
+ * Staff: every post, by language, draft state and text, deleted ones on
39
+ * their own to restore; create and edit title, slug, language, excerpt,
40
+ * schedule, cover and body with inline images, publish or keep a draft, and
41
+ * delete. The list is at basePath, a new post at basePath/new and each post
42
+ * at basePath/{id}, filters in the query.
43
+ */
44
+ export declare function PostManager({ client: own, baseUrl, auth, guard, appearance, className, language, messages, ...p }: PostManagerProps): import('react').JSX.Element;
45
+ export {};