@openrails/contentkit-ui 0.70.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.
- package/README.md +98 -10
- package/dist/client/content/bans.d.ts +2 -0
- package/dist/client/content/bans.js +24 -12
- package/dist/client/content/bans.js.map +1 -1
- package/dist/client/content/comments.d.ts +4 -2
- package/dist/client/content/comments.js.map +1 -1
- package/dist/client/content/polls.d.ts +10 -3
- package/dist/client/content/polls.js.map +1 -1
- package/dist/client/content/posts.d.ts +4 -4
- package/dist/client/content/posts.js.map +1 -1
- package/dist/client/generated/error-codes.js +2 -2
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +146 -120
- package/dist/client/generated/routes.js +245 -117
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +5 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +2 -2
- package/dist/client/taxonomy.d.ts +6 -2
- package/dist/client/taxonomy.js +7 -0
- package/dist/client/taxonomy.js.map +1 -1
- package/dist/components/admin/poll-manager.d.ts +28 -0
- package/dist/components/admin/poll-manager.js +297 -0
- package/dist/components/admin/poll-manager.js.map +1 -0
- package/dist/components/admin/post-manager.d.ts +45 -0
- package/dist/components/admin/post-manager.js +602 -0
- package/dist/components/admin/post-manager.js.map +1 -0
- package/dist/components/admin/shell.d.ts +73 -0
- package/dist/components/admin/shell.js +148 -0
- package/dist/components/admin/shell.js.map +1 -0
- package/dist/components/admin/taxonomy-manager.d.ts +54 -0
- package/dist/components/admin/taxonomy-manager.js +976 -0
- package/dist/components/admin/taxonomy-manager.js.map +1 -0
- package/dist/components/admin/user-comment-standing.d.ts +25 -0
- package/dist/components/admin/user-comment-standing.js +229 -0
- package/dist/components/admin/user-comment-standing.js.map +1 -0
- package/dist/components/social/bans.d.ts +5 -2
- package/dist/components/social/bans.js +132 -134
- package/dist/components/social/bans.js.map +1 -1
- package/dist/components/social/index.d.ts +1 -1
- package/dist/components/social/moderation.d.ts +11 -13
- package/dist/components/social/moderation.js +299 -218
- package/dist/components/social/moderation.js.map +1 -1
- package/dist/components/social/parts.d.ts +3 -2
- package/dist/components/social/parts.js +74 -48
- package/dist/components/social/parts.js.map +1 -1
- package/dist/components/social/poll-editor.d.ts +1 -1
- package/dist/components/social/poll-editor.js +1 -0
- package/dist/components/social/poll-editor.js.map +1 -1
- package/dist/editor/index.d.ts +1 -0
- package/dist/editor/rich-text-editor.d.ts +13 -0
- package/dist/editor/rich-text-editor.js +228 -0
- package/dist/editor/rich-text-editor.js.map +1 -0
- package/dist/editor.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +13 -8
- package/dist/locales/admin/de.d.ts +2 -0
- package/dist/locales/admin/de.js +176 -0
- package/dist/locales/admin/de.js.map +1 -0
- package/dist/locales/admin/en.d.ts +171 -0
- package/dist/locales/admin/en.js +176 -0
- package/dist/locales/admin/en.js.map +1 -0
- package/dist/locales/admin/es.d.ts +2 -0
- package/dist/locales/admin/es.js +176 -0
- package/dist/locales/admin/es.js.map +1 -0
- package/dist/locales/admin/ja.d.ts +2 -0
- package/dist/locales/admin/ja.js +176 -0
- package/dist/locales/admin/ja.js.map +1 -0
- package/dist/locales/admin/ko.d.ts +2 -0
- package/dist/locales/admin/ko.js +176 -0
- package/dist/locales/admin/ko.js.map +1 -0
- package/dist/locales/admin/zh.d.ts +2 -0
- package/dist/locales/admin/zh.js +176 -0
- package/dist/locales/admin/zh.js.map +1 -0
- package/dist/locales/de.js +5 -3
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/en.d.ts +176 -0
- package/dist/locales/en.js +5 -3
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +5 -3
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +5 -3
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +5 -3
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/social/de.js +8 -1
- package/dist/locales/social/de.js.map +1 -1
- package/dist/locales/social/en.d.ts +7 -0
- package/dist/locales/social/en.js +8 -1
- package/dist/locales/social/en.js.map +1 -1
- package/dist/locales/social/es.js +8 -1
- package/dist/locales/social/es.js.map +1 -1
- package/dist/locales/social/ja.js +8 -1
- package/dist/locales/social/ja.js.map +1 -1
- package/dist/locales/social/ko.js +8 -1
- package/dist/locales/social/ko.js.map +1 -1
- package/dist/locales/social/zh.js +8 -1
- package/dist/locales/social/zh.js.map +1 -1
- package/dist/locales/zh.js +5 -3
- package/dist/locales/zh.js.map +1 -1
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +23 -19
- package/dist/provider.js.map +1 -1
- package/dist/react/admin.d.ts +55 -0
- package/dist/react/admin.js +97 -0
- package/dist/react/admin.js.map +1 -0
- package/dist/react/comments.d.ts +18 -2
- package/dist/react/comments.js +24 -8
- package/dist/react/comments.js.map +1 -1
- package/dist/react/context.d.ts +7 -1
- package/dist/react/context.js +6 -1
- package/dist/react/context.js.map +1 -1
- package/dist/react/index.d.ts +4 -3
- package/dist/react/polls.d.ts +2 -2
- package/dist/react/polls.js +88 -87
- package/dist/react/polls.js.map +1 -1
- package/dist/react/posts.d.ts +3 -3
- package/dist/react/posts.js +123 -122
- package/dist/react/posts.js.map +1 -1
- package/dist/react/resources.js +3 -0
- package/dist/react/resources.js.map +1 -1
- package/dist/react/use-resource.d.ts +16 -0
- package/dist/react/use-resource.js +20 -1
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react.js +21 -20
- package/dist/styles.css +1 -1
- package/package.json +37 -2
|
@@ -4,94 +4,95 @@ import { useContentKitClient as n } from "../../react/context.js";
|
|
|
4
4
|
import { Alert as r, AlertDescription as i } from "../../ui/alert.js";
|
|
5
5
|
import { Button as a } from "../../ui/button.js";
|
|
6
6
|
import { Dialog as o, DialogContent as s, DialogDescription as c, DialogFooter as l, DialogHeader as u, DialogTitle as d } from "../../ui/dialog.js";
|
|
7
|
-
import {
|
|
8
|
-
import { Badge as
|
|
9
|
-
import { Input as
|
|
10
|
-
import { Label as
|
|
11
|
-
import { Textarea as
|
|
12
|
-
import { ConfirmDialog as
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
7
|
+
import { keyOf as f, useContentScope as p, usePaged as m } from "../../react/use-resource.js";
|
|
8
|
+
import { Badge as h } from "../../ui/badge.js";
|
|
9
|
+
import { Input as g } from "../../ui/input.js";
|
|
10
|
+
import { Label as _ } from "../../ui/label.js";
|
|
11
|
+
import { Textarea as v } from "../../ui/textarea.js";
|
|
12
|
+
import { ConfirmDialog as y, ErrorLine as b, UserAvatar as x, UserName as S, formatDate as C, useAction as w } from "./parts.js";
|
|
13
|
+
import { LoadFailed as T, Pager as E } from "../admin/shell.js";
|
|
14
|
+
import { useEffect as D, useId as O, useState as k } from "react";
|
|
15
|
+
import { jsx as A, jsxs as j } from "react/jsx-runtime";
|
|
16
|
+
import { cn as M } from "cn";
|
|
16
17
|
//#region src/components/social/bans.tsx
|
|
17
|
-
function
|
|
18
|
+
function N({ ban: t, className: n }) {
|
|
18
19
|
let a = e(), { t: o } = a;
|
|
19
|
-
return /* @__PURE__ */
|
|
20
|
+
return /* @__PURE__ */ A(r, {
|
|
20
21
|
variant: "destructive",
|
|
21
22
|
className: n,
|
|
22
23
|
"data-ckui": "ban-notice",
|
|
23
|
-
children: /* @__PURE__ */
|
|
24
|
+
children: /* @__PURE__ */ j(i, {
|
|
24
25
|
className: "grid gap-1",
|
|
25
26
|
children: [
|
|
26
|
-
/* @__PURE__ */
|
|
27
|
+
/* @__PURE__ */ A("p", {
|
|
27
28
|
className: "font-medium",
|
|
28
29
|
children: o(t.scope === "global" ? "bans.globalNotice" : "bans.ownerNotice")
|
|
29
30
|
}),
|
|
30
|
-
t.reason && /* @__PURE__ */
|
|
31
|
-
/* @__PURE__ */
|
|
31
|
+
t.reason && /* @__PURE__ */ A("p", { children: o("bans.reason", { reason: t.reason }) }),
|
|
32
|
+
/* @__PURE__ */ A("p", { children: t.until ? o("bans.until", { date: C(a, t.until) }) : o("bans.untilLifted") })
|
|
32
33
|
]
|
|
33
34
|
})
|
|
34
35
|
});
|
|
35
36
|
}
|
|
36
|
-
var
|
|
37
|
-
day:
|
|
38
|
-
week: 7 *
|
|
39
|
-
month: 30 *
|
|
37
|
+
var P = 864e5, F = {
|
|
38
|
+
day: P,
|
|
39
|
+
week: 7 * P,
|
|
40
|
+
month: 30 * P,
|
|
40
41
|
forever: 0,
|
|
41
42
|
custom: 0
|
|
42
43
|
};
|
|
43
|
-
function
|
|
44
|
+
function I(e) {
|
|
44
45
|
let t = (e) => String(e).padStart(2, "0");
|
|
45
46
|
return `${e.getFullYear()}-${t(e.getMonth() + 1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`;
|
|
46
47
|
}
|
|
47
|
-
function
|
|
48
|
-
let { t: x } = e(),
|
|
49
|
-
|
|
50
|
-
t && (
|
|
48
|
+
function L({ open: t, onOpenChange: r, user: i, scopes: f, ban: p, onSaved: m, client: h, onError: y }) {
|
|
49
|
+
let { t: x } = e(), S = n(h), C = O(), T = p ? p.scope === "global" ? "global" : "owner" : f[0] ?? "owner", [E, M] = k(T), [N, L] = k(p ? p.until ? "custom" : "forever" : "week"), [R, z] = k(p?.until ? I(new Date(p.until)) : I(new Date(Date.now() + 7 * P))), [B, V] = k(p?.reason ?? ""), { run: H, pending: U, error: W, setError: G } = w("ban.save", y);
|
|
50
|
+
D(() => {
|
|
51
|
+
t && (M(T), L(p ? p.until ? "custom" : "forever" : "week"), z(p?.until ? I(new Date(p.until)) : I(new Date(Date.now() + 7 * P))), V(p?.reason ?? ""), G(null));
|
|
51
52
|
}, [t]);
|
|
52
53
|
let K = i.name ?? i.id, q = async () => {
|
|
53
|
-
let e =
|
|
54
|
+
let e = N === "forever" ? void 0 : N === "custom" ? new Date(R) : new Date(Date.now() + F[N]), t = await H(() => S.bans.ban(E, i.id, {
|
|
54
55
|
reason: B.trim() || void 0,
|
|
55
56
|
until: e?.toISOString()
|
|
56
57
|
}));
|
|
57
|
-
t && (
|
|
58
|
+
t && (m?.(t), r(!1));
|
|
58
59
|
};
|
|
59
|
-
return /* @__PURE__ */
|
|
60
|
+
return /* @__PURE__ */ A(o, {
|
|
60
61
|
open: t,
|
|
61
62
|
onOpenChange: r,
|
|
62
|
-
children: /* @__PURE__ */
|
|
63
|
+
children: /* @__PURE__ */ j(s, {
|
|
63
64
|
"data-ckui": "comment-ban-dialog",
|
|
64
|
-
children: [/* @__PURE__ */
|
|
65
|
+
children: [/* @__PURE__ */ j(u, { children: [/* @__PURE__ */ A(d, { children: x("bans.title", { name: K }) }), /* @__PURE__ */ A(c, { children: x(E === "global" ? "bans.globalHelp" : "bans.ownerHelp") })] }), /* @__PURE__ */ j("form", {
|
|
65
66
|
className: "grid gap-4",
|
|
66
67
|
onSubmit: (e) => {
|
|
67
68
|
e.preventDefault(), q();
|
|
68
69
|
},
|
|
69
70
|
children: [
|
|
70
|
-
f.length > 1 && !p && /* @__PURE__ */
|
|
71
|
+
f.length > 1 && !p && /* @__PURE__ */ j("fieldset", {
|
|
71
72
|
className: "grid gap-2",
|
|
72
|
-
children: [/* @__PURE__ */
|
|
73
|
+
children: [/* @__PURE__ */ A("legend", {
|
|
73
74
|
className: "mb-1 text-sm font-medium",
|
|
74
75
|
children: x("bans.scope")
|
|
75
|
-
}), /* @__PURE__ */
|
|
76
|
+
}), /* @__PURE__ */ A("div", {
|
|
76
77
|
className: "flex flex-wrap gap-2",
|
|
77
|
-
children: f.map((e) => /* @__PURE__ */
|
|
78
|
+
children: f.map((e) => /* @__PURE__ */ A(a, {
|
|
78
79
|
type: "button",
|
|
79
80
|
size: "sm",
|
|
80
|
-
variant:
|
|
81
|
-
"aria-pressed":
|
|
82
|
-
onClick: () =>
|
|
81
|
+
variant: E === e ? "default" : "outline",
|
|
82
|
+
"aria-pressed": E === e,
|
|
83
|
+
onClick: () => M(e),
|
|
83
84
|
children: x(e === "global" ? "bans.scopeGlobal" : "bans.scopeOwner")
|
|
84
85
|
}, e))
|
|
85
86
|
})]
|
|
86
87
|
}),
|
|
87
|
-
/* @__PURE__ */
|
|
88
|
+
/* @__PURE__ */ j("fieldset", {
|
|
88
89
|
className: "grid gap-2",
|
|
89
90
|
children: [
|
|
90
|
-
/* @__PURE__ */
|
|
91
|
+
/* @__PURE__ */ A("legend", {
|
|
91
92
|
className: "mb-1 text-sm font-medium",
|
|
92
93
|
children: x("bans.duration")
|
|
93
94
|
}),
|
|
94
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ A("div", {
|
|
95
96
|
className: "flex flex-wrap gap-2",
|
|
96
97
|
children: [
|
|
97
98
|
"day",
|
|
@@ -99,38 +100,38 @@ function N({ open: t, onOpenChange: r, user: i, scopes: f, ban: p, onSaved: _, c
|
|
|
99
100
|
"month",
|
|
100
101
|
"forever",
|
|
101
102
|
"custom"
|
|
102
|
-
].map((e) => /* @__PURE__ */
|
|
103
|
+
].map((e) => /* @__PURE__ */ A(a, {
|
|
103
104
|
type: "button",
|
|
104
105
|
size: "sm",
|
|
105
|
-
variant:
|
|
106
|
-
"aria-pressed":
|
|
106
|
+
variant: N === e ? "default" : "outline",
|
|
107
|
+
"aria-pressed": N === e,
|
|
107
108
|
onClick: () => L(e),
|
|
108
109
|
children: x(`bans.${e}`)
|
|
109
110
|
}, e))
|
|
110
111
|
}),
|
|
111
|
-
|
|
112
|
+
N === "custom" && /* @__PURE__ */ j("div", {
|
|
112
113
|
className: "grid gap-1.5",
|
|
113
|
-
children: [/* @__PURE__ */
|
|
114
|
-
htmlFor: `${
|
|
114
|
+
children: [/* @__PURE__ */ A(_, {
|
|
115
|
+
htmlFor: `${C}-until`,
|
|
115
116
|
children: x("bans.endsAt")
|
|
116
|
-
}), /* @__PURE__ */
|
|
117
|
-
id: `${
|
|
117
|
+
}), /* @__PURE__ */ A(g, {
|
|
118
|
+
id: `${C}-until`,
|
|
118
119
|
type: "datetime-local",
|
|
119
120
|
required: !0,
|
|
120
121
|
value: R,
|
|
121
|
-
min:
|
|
122
|
+
min: I(/* @__PURE__ */ new Date()),
|
|
122
123
|
onChange: (e) => z(e.target.value)
|
|
123
124
|
})]
|
|
124
125
|
})
|
|
125
126
|
]
|
|
126
127
|
}),
|
|
127
|
-
/* @__PURE__ */
|
|
128
|
+
/* @__PURE__ */ j("div", {
|
|
128
129
|
className: "grid gap-1.5",
|
|
129
|
-
children: [/* @__PURE__ */
|
|
130
|
-
htmlFor: `${
|
|
130
|
+
children: [/* @__PURE__ */ A(_, {
|
|
131
|
+
htmlFor: `${C}-reason`,
|
|
131
132
|
children: x("bans.reasonLabel")
|
|
132
|
-
}), /* @__PURE__ */
|
|
133
|
-
id: `${
|
|
133
|
+
}), /* @__PURE__ */ A(v, {
|
|
134
|
+
id: `${C}-reason`,
|
|
134
135
|
maxLength: 500,
|
|
135
136
|
rows: 3,
|
|
136
137
|
placeholder: x("bans.reasonPlaceholder"),
|
|
@@ -138,13 +139,13 @@ function N({ open: t, onOpenChange: r, user: i, scopes: f, ban: p, onSaved: _, c
|
|
|
138
139
|
onChange: (e) => V(e.target.value)
|
|
139
140
|
})]
|
|
140
141
|
}),
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
-
/* @__PURE__ */
|
|
142
|
+
/* @__PURE__ */ A(b, { error: W }),
|
|
143
|
+
/* @__PURE__ */ j(l, { children: [/* @__PURE__ */ A(a, {
|
|
143
144
|
type: "button",
|
|
144
145
|
variant: "outline",
|
|
145
146
|
onClick: () => r(!1),
|
|
146
147
|
children: x("common.cancel")
|
|
147
|
-
}), /* @__PURE__ */
|
|
148
|
+
}), /* @__PURE__ */ A(a, {
|
|
148
149
|
type: "submit",
|
|
149
150
|
variant: "destructive",
|
|
150
151
|
disabled: U,
|
|
@@ -155,146 +156,143 @@ function N({ open: t, onOpenChange: r, user: i, scopes: f, ban: p, onSaved: _, c
|
|
|
155
156
|
})
|
|
156
157
|
});
|
|
157
158
|
}
|
|
158
|
-
function
|
|
159
|
-
let
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
159
|
+
function R({ scope: n = "global", pageSize: r = 25, page: i, onPageChange: o, userHref: s, client: c, onError: l, className: u, appearance: d }) {
|
|
160
|
+
let v = e(), { t: D } = v, { client: N, store: P, scope: F } = p(c), [I, R] = k(1), z = i ?? I, B = (e) => o ? o(e) : R(e), V = m(P, f("bans-page", F, n, r), {
|
|
161
|
+
type: "bans",
|
|
162
|
+
scope: n
|
|
163
|
+
}, z, r, async (e, t) => (await N.bans.list(n, e, t)).map((e) => ({
|
|
164
|
+
...e,
|
|
165
|
+
id: e.user_id
|
|
166
|
+
}))), H = O(), [U, W] = k(null), [G, K] = k(null), [q, J] = k(""), Y = w("ban.lift", l);
|
|
167
|
+
return /* @__PURE__ */ j(t, {
|
|
168
|
+
appearance: d,
|
|
169
|
+
className: M("grid gap-4 text-sm", u),
|
|
167
170
|
"data-ckui": "comment-bans",
|
|
168
171
|
children: [
|
|
169
|
-
/* @__PURE__ */
|
|
172
|
+
/* @__PURE__ */ j("form", {
|
|
170
173
|
className: "flex flex-wrap items-end gap-2",
|
|
171
174
|
onSubmit: (e) => {
|
|
172
|
-
e.preventDefault(),
|
|
175
|
+
e.preventDefault(), q.trim() && W({ user: { id: q.trim() } });
|
|
173
176
|
},
|
|
174
|
-
children: [/* @__PURE__ */
|
|
177
|
+
children: [/* @__PURE__ */ j("div", {
|
|
175
178
|
className: "grid min-w-48 flex-1 gap-1.5",
|
|
176
|
-
children: [/* @__PURE__ */
|
|
177
|
-
htmlFor: `${
|
|
178
|
-
children:
|
|
179
|
-
}), /* @__PURE__ */
|
|
180
|
-
id: `${
|
|
181
|
-
value:
|
|
182
|
-
placeholder:
|
|
183
|
-
onChange: (e) =>
|
|
179
|
+
children: [/* @__PURE__ */ A(_, {
|
|
180
|
+
htmlFor: `${H}-user`,
|
|
181
|
+
children: D("bans.user")
|
|
182
|
+
}), /* @__PURE__ */ A(g, {
|
|
183
|
+
id: `${H}-user`,
|
|
184
|
+
value: q,
|
|
185
|
+
placeholder: D("bans.userPlaceholder"),
|
|
186
|
+
onChange: (e) => J(e.target.value)
|
|
184
187
|
})]
|
|
185
|
-
}), /* @__PURE__ */
|
|
188
|
+
}), /* @__PURE__ */ A(a, {
|
|
186
189
|
type: "submit",
|
|
187
190
|
variant: "outline",
|
|
188
|
-
disabled: !
|
|
189
|
-
children:
|
|
191
|
+
disabled: !q.trim(),
|
|
192
|
+
children: D("bans.add")
|
|
190
193
|
})]
|
|
191
194
|
}),
|
|
192
|
-
/* @__PURE__ */
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
onClick: g.reload,
|
|
199
|
-
children: d("common.retry")
|
|
200
|
-
})]
|
|
201
|
-
}) : !g.loading && !g.items.length ? /* @__PURE__ */ E("p", {
|
|
195
|
+
/* @__PURE__ */ A(b, { error: Y.error }),
|
|
196
|
+
V.error && !V.items.length ? /* @__PURE__ */ A(T, {
|
|
197
|
+
error: V.error,
|
|
198
|
+
message: D("bans.loadFailed"),
|
|
199
|
+
retry: V.reload
|
|
200
|
+
}) : !V.loading && !V.items.length ? /* @__PURE__ */ A("p", {
|
|
202
201
|
className: "text-muted-foreground",
|
|
203
|
-
children:
|
|
204
|
-
}) : /* @__PURE__ */
|
|
202
|
+
children: D("bans.empty")
|
|
203
|
+
}) : /* @__PURE__ */ A("ul", {
|
|
205
204
|
className: "grid divide-y divide-border rounded-lg border border-border",
|
|
206
|
-
"aria-busy":
|
|
207
|
-
children:
|
|
205
|
+
"aria-busy": V.loading,
|
|
206
|
+
children: V.items.map((e) => {
|
|
208
207
|
let t = e.user?.username ?? e.user_id;
|
|
209
|
-
return /* @__PURE__ */
|
|
208
|
+
return /* @__PURE__ */ j("li", {
|
|
210
209
|
className: "flex flex-wrap items-start gap-3 p-3",
|
|
211
210
|
"data-ckui": "comment-ban",
|
|
212
211
|
children: [
|
|
213
|
-
/* @__PURE__ */
|
|
212
|
+
/* @__PURE__ */ A(x, {
|
|
214
213
|
user: e.user,
|
|
215
214
|
name: t
|
|
216
215
|
}),
|
|
217
|
-
/* @__PURE__ */
|
|
216
|
+
/* @__PURE__ */ j("div", {
|
|
218
217
|
className: "grid min-w-0 flex-1 gap-0.5",
|
|
219
218
|
children: [
|
|
220
|
-
/* @__PURE__ */
|
|
219
|
+
/* @__PURE__ */ j("div", {
|
|
221
220
|
className: "flex flex-wrap items-center gap-2",
|
|
222
|
-
children: [/* @__PURE__ */
|
|
221
|
+
children: [/* @__PURE__ */ A(S, {
|
|
223
222
|
user: e.user,
|
|
224
223
|
name: t,
|
|
225
|
-
href:
|
|
226
|
-
}), /* @__PURE__ */
|
|
224
|
+
href: s
|
|
225
|
+
}), /* @__PURE__ */ A(h, {
|
|
227
226
|
variant: e.expired ? "outline" : "destructive",
|
|
228
|
-
children:
|
|
227
|
+
children: D(e.expired ? "bans.expired" : "bans.active")
|
|
229
228
|
})]
|
|
230
229
|
}),
|
|
231
|
-
e.reason && /* @__PURE__ */
|
|
230
|
+
e.reason && /* @__PURE__ */ A("p", {
|
|
232
231
|
className: "break-words",
|
|
233
|
-
children:
|
|
232
|
+
children: D("bans.reason", { reason: e.reason })
|
|
234
233
|
}),
|
|
235
|
-
/* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ j("p", {
|
|
236
235
|
className: "text-muted-foreground",
|
|
237
236
|
children: [
|
|
238
|
-
e.until ?
|
|
237
|
+
e.until ? D("bans.until", { date: C(v, e.until) }) : D("bans.untilLifted"),
|
|
239
238
|
" · ",
|
|
240
|
-
|
|
239
|
+
D("bans.bannedAt", { date: C(v, e.banned_at, !1) })
|
|
241
240
|
]
|
|
242
241
|
})
|
|
243
242
|
]
|
|
244
243
|
}),
|
|
245
|
-
/* @__PURE__ */
|
|
244
|
+
/* @__PURE__ */ j("div", {
|
|
246
245
|
className: "flex gap-2",
|
|
247
|
-
children: [/* @__PURE__ */
|
|
246
|
+
children: [/* @__PURE__ */ A(a, {
|
|
248
247
|
size: "sm",
|
|
249
248
|
variant: "outline",
|
|
250
|
-
onClick: () =>
|
|
249
|
+
onClick: () => W({
|
|
251
250
|
user: {
|
|
252
251
|
id: e.user_id,
|
|
253
252
|
name: t
|
|
254
253
|
},
|
|
255
254
|
ban: e
|
|
256
255
|
}),
|
|
257
|
-
children:
|
|
258
|
-
}), /* @__PURE__ */
|
|
256
|
+
children: D("bans.update")
|
|
257
|
+
}), /* @__PURE__ */ A(a, {
|
|
259
258
|
size: "sm",
|
|
260
259
|
variant: "destructive",
|
|
261
|
-
onClick: () =>
|
|
262
|
-
children:
|
|
260
|
+
onClick: () => K(e),
|
|
261
|
+
children: D("bans.lift")
|
|
263
262
|
})]
|
|
264
263
|
})
|
|
265
264
|
]
|
|
266
265
|
}, e.user_id);
|
|
267
266
|
})
|
|
268
267
|
}),
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
children: d("moderation.loadMore")
|
|
268
|
+
/* @__PURE__ */ A(E, {
|
|
269
|
+
page: z,
|
|
270
|
+
hasNext: V.hasNext,
|
|
271
|
+
onPage: B,
|
|
272
|
+
disabled: V.loading
|
|
275
273
|
}),
|
|
276
|
-
|
|
274
|
+
U && /* @__PURE__ */ A(L, {
|
|
277
275
|
open: !0,
|
|
278
|
-
onOpenChange: (e) => !e &&
|
|
279
|
-
user:
|
|
276
|
+
onOpenChange: (e) => !e && W(null),
|
|
277
|
+
user: U.user,
|
|
280
278
|
scopes: [n],
|
|
281
|
-
ban:
|
|
282
|
-
client:
|
|
283
|
-
onError:
|
|
284
|
-
onSaved: () =>
|
|
279
|
+
ban: U.ban,
|
|
280
|
+
client: N,
|
|
281
|
+
onError: l,
|
|
282
|
+
onSaved: () => J("")
|
|
285
283
|
}),
|
|
286
|
-
/* @__PURE__ */
|
|
287
|
-
open: !!
|
|
288
|
-
onOpenChange: (e) => !e &&
|
|
289
|
-
title:
|
|
290
|
-
description:
|
|
291
|
-
confirm:
|
|
292
|
-
onConfirm: () =>
|
|
284
|
+
/* @__PURE__ */ A(y, {
|
|
285
|
+
open: !!G,
|
|
286
|
+
onOpenChange: (e) => !e && K(null),
|
|
287
|
+
title: D("bans.liftTitle", { name: G?.user?.username ?? G?.user_id ?? "" }),
|
|
288
|
+
description: D("bans.liftBody"),
|
|
289
|
+
confirm: D("bans.lift"),
|
|
290
|
+
onConfirm: () => G && void Y.run(() => N.bans.lift(n, G.user_id))
|
|
293
291
|
})
|
|
294
292
|
]
|
|
295
293
|
});
|
|
296
294
|
}
|
|
297
295
|
//#endregion
|
|
298
|
-
export {
|
|
296
|
+
export { N as BanNotice, L as CommentBanDialog, R as CommentBans, I as localInput };
|
|
299
297
|
|
|
300
298
|
//# sourceMappingURL=bans.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bans.js","names":[],"sources":["../../../src/components/social/bans.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { BanScope } from \"../../client/content/types.js\";\nimport type { BanNotice as BanNoticeData, CommentBan, PublicUser } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useCommentBans } from \"../../react/comments.js\";\nimport { useContentKitClient } from \"../../react/context.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Alert, AlertDescription } from \"#ckui/ui/alert\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from \"#ckui/ui/dialog\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Textarea } from \"#ckui/ui/textarea\";\nimport { ConfirmDialog, ErrorLine, formatDate, UserAvatar, UserName, useAction } from \"./parts.js\";\n\n/** What a banned user is told: where, why and until when (never who). */\nexport function BanNotice({ ban, className }: { ban: BanNoticeData; className?: string }) {\n const m = useMessages();\n const { t } = m;\n return (\n <Alert variant=\"destructive\" className={className} data-ckui=\"ban-notice\">\n <AlertDescription className=\"grid gap-1\">\n <p className=\"font-medium\">{t(ban.scope === \"global\" ? \"bans.globalNotice\" : \"bans.ownerNotice\")}</p>\n {ban.reason && <p>{t(\"bans.reason\", { reason: ban.reason })}</p>}\n <p>{ban.until ? t(\"bans.until\", { date: formatDate(m, ban.until) }) : t(\"bans.untilLifted\")}</p>\n </AlertDescription>\n </Alert>\n );\n}\n\nconst DAY = 86_400_000;\ntype Duration = \"day\" | \"week\" | \"month\" | \"forever\" | \"custom\";\nconst span: Record<Duration, number> = { day: DAY, week: 7 * DAY, month: 30 * DAY, forever: 0, custom: 0 };\n\n/** \"YYYY-MM-DDTHH:mm\" in local time, for a datetime-local input. */\nexport function localInput(d: Date): string {\n const p = (n: number) => String(n).padStart(2, \"0\");\n return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`;\n}\n\nexport interface CommentBanDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Who to ban: their id and the name to show. */\n user: { id: string; name?: string };\n /** The scopes the caller may ban in (CommentStanding.ban_scopes); the first is preselected. */\n scopes: readonly BanScope[];\n /** The current ban, when updating one. */\n ban?: CommentBan | null;\n onSaved?: (ban: CommentBan) => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n}\n\n/** Bans a user from commenting, or replaces their ban: scope, duration presets or an end date, and a reason they see. */\nexport function CommentBanDialog({ open, onOpenChange, user, scopes, ban, onSaved, client: own, onError }: CommentBanDialogProps) {\n const m = useMessages();\n const { t } = m;\n const client = useContentKitClient(own);\n const ids = useId();\n const initialScope: BanScope = ban ? (ban.scope === \"global\" ? \"global\" : \"owner\") : (scopes[0] ?? \"owner\");\n const [scope, setScope] = useState<BanScope>(initialScope);\n const [duration, setDuration] = useState<Duration>(ban ? (ban.until ? \"custom\" : \"forever\") : \"week\");\n const [until, setUntil] = useState(ban?.until ? localInput(new Date(ban.until)) : localInput(new Date(Date.now() + 7 * DAY)));\n const [reason, setReason] = useState(ban?.reason ?? \"\");\n const { run, pending, error, setError } = useAction(\"ban.save\", onError);\n useEffect(() => {\n if (!open) return;\n setScope(initialScope);\n setDuration(ban ? (ban.until ? \"custom\" : \"forever\") : \"week\");\n setUntil(ban?.until ? localInput(new Date(ban.until)) : localInput(new Date(Date.now() + 7 * DAY)));\n setReason(ban?.reason ?? \"\");\n setError(null);\n // Reset when it opens.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n const name = user.name ?? user.id;\n const save = async () => {\n const end = duration === \"forever\" ? undefined : duration === \"custom\" ? new Date(until) : new Date(Date.now() + span[duration]);\n const saved = await run(() => client.bans.ban(scope, user.id, { reason: reason.trim() || undefined, until: end?.toISOString() }));\n if (saved) {\n onSaved?.(saved);\n onOpenChange(false);\n }\n };\n const durations: Duration[] = [\"day\", \"week\", \"month\", \"forever\", \"custom\"];\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent data-ckui=\"comment-ban-dialog\">\n <DialogHeader>\n <DialogTitle>{t(\"bans.title\", { name })}</DialogTitle>\n <DialogDescription>{t(scope === \"global\" ? \"bans.globalHelp\" : \"bans.ownerHelp\")}</DialogDescription>\n </DialogHeader>\n <form\n className=\"grid gap-4\"\n onSubmit={(e) => {\n e.preventDefault();\n void save();\n }}\n >\n {scopes.length > 1 && !ban && (\n <fieldset className=\"grid gap-2\">\n <legend className=\"mb-1 text-sm font-medium\">{t(\"bans.scope\")}</legend>\n <div className=\"flex flex-wrap gap-2\">\n {scopes.map((s) => (\n <Button key={s} type=\"button\" size=\"sm\" variant={scope === s ? \"default\" : \"outline\"} aria-pressed={scope === s} onClick={() => setScope(s)}>\n {t(s === \"global\" ? \"bans.scopeGlobal\" : \"bans.scopeOwner\")}\n </Button>\n ))}\n </div>\n </fieldset>\n )}\n <fieldset className=\"grid gap-2\">\n <legend className=\"mb-1 text-sm font-medium\">{t(\"bans.duration\")}</legend>\n <div className=\"flex flex-wrap gap-2\">\n {durations.map((d) => (\n <Button key={d} type=\"button\" size=\"sm\" variant={duration === d ? \"default\" : \"outline\"} aria-pressed={duration === d} onClick={() => setDuration(d)}>\n {t(`bans.${d}`)}\n </Button>\n ))}\n </div>\n {duration === \"custom\" && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-until`}>{t(\"bans.endsAt\")}</Label>\n <Input id={`${ids}-until`} type=\"datetime-local\" required value={until} min={localInput(new Date())} onChange={(e) => setUntil(e.target.value)} />\n </div>\n )}\n </fieldset>\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-reason`}>{t(\"bans.reasonLabel\")}</Label>\n <Textarea id={`${ids}-reason`} maxLength={500} rows={3} placeholder={t(\"bans.reasonPlaceholder\")} value={reason} onChange={(e) => setReason(e.target.value)} />\n </div>\n <ErrorLine error={error} />\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n {t(\"common.cancel\")}\n </Button>\n <Button type=\"submit\" variant=\"destructive\" disabled={pending}>\n {pending ? t(\"common.saving\") : t(ban ? \"bans.update\" : \"bans.save\")}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n\nexport interface CommentBansProps {\n /** \"global\": the site's bans (CommentBan); \"owner\": the caller's own. Default \"global\". */\n scope?: BanScope;\n pageSize?: number;\n userHref?: (user: PublicUser) => string | undefined;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A scope's comment bans, newest first: who, why, until when; ban a user by id, update or lift a ban. */\nexport function CommentBans({ scope = \"global\", pageSize = 25, userHref, client, onError, className, appearance }: CommentBansProps) {\n const m = useMessages();\n const { t } = m;\n const bans = useCommentBans({ scope, pageSize, client });\n const ids = useId();\n const [editing, setEditing] = useState<{ user: { id: string; name?: string }; ban?: CommentBan } | null>(null);\n const [lifting, setLifting] = useState<CommentBan | null>(null);\n const [userId, setUserId] = useState(\"\");\n const lift = useAction(\"ban.lift\", onError);\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-4 text-sm\", className)} data-ckui=\"comment-bans\">\n <form\n className=\"flex flex-wrap items-end gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n if (userId.trim()) setEditing({ user: { id: userId.trim() } });\n }}\n >\n <div className=\"grid min-w-48 flex-1 gap-1.5\">\n <Label htmlFor={`${ids}-user`}>{t(\"bans.user\")}</Label>\n <Input id={`${ids}-user`} value={userId} placeholder={t(\"bans.userPlaceholder\")} onChange={(e) => setUserId(e.target.value)} />\n </div>\n <Button type=\"submit\" variant=\"outline\" disabled={!userId.trim()}>\n {t(\"bans.add\")}\n </Button>\n </form>\n <ErrorLine error={lift.error} />\n {bans.error && !bans.items.length ? (\n <div className=\"grid justify-items-start gap-2\">\n <ErrorLine>{t(\"bans.loadFailed\")}</ErrorLine>\n <Button size=\"sm\" variant=\"outline\" onClick={bans.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n ) : !bans.loading && !bans.items.length ? (\n <p className=\"text-muted-foreground\">{t(\"bans.empty\")}</p>\n ) : (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\" aria-busy={bans.loading}>\n {bans.items.map((b) => {\n const name = b.user?.username ?? b.user_id;\n return (\n <li key={b.user_id} className=\"flex flex-wrap items-start gap-3 p-3\" data-ckui=\"comment-ban\">\n <UserAvatar user={b.user} name={name} />\n <div className=\"grid min-w-0 flex-1 gap-0.5\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <UserName user={b.user} name={name} href={userHref} />\n <Badge variant={b.expired ? \"outline\" : \"destructive\"}>{t(b.expired ? \"bans.expired\" : \"bans.active\")}</Badge>\n </div>\n {b.reason && <p className=\"break-words\">{t(\"bans.reason\", { reason: b.reason })}</p>}\n <p className=\"text-muted-foreground\">\n {b.until ? t(\"bans.until\", { date: formatDate(m, b.until) }) : t(\"bans.untilLifted\")} · {t(\"bans.bannedAt\", { date: formatDate(m, b.banned_at, false) })}\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" variant=\"outline\" onClick={() => setEditing({ user: { id: b.user_id, name }, ban: b })}>\n {t(\"bans.update\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" onClick={() => setLifting(b)}>\n {t(\"bans.lift\")}\n </Button>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n {bans.hasMore && (\n <Button variant=\"outline\" className=\"justify-self-center\" disabled={bans.loadingMore} onClick={() => void bans.loadMore()}>\n {t(\"moderation.loadMore\")}\n </Button>\n )}\n {editing && (\n <CommentBanDialog\n open\n onOpenChange={(o) => !o && setEditing(null)}\n user={editing.user}\n scopes={[scope]}\n ban={editing.ban}\n client={client}\n onError={onError}\n onSaved={() => setUserId(\"\")}\n />\n )}\n <ConfirmDialog\n open={!!lifting}\n onOpenChange={(o) => !o && setLifting(null)}\n title={t(\"bans.liftTitle\", { name: lifting?.user?.username ?? lifting?.user_id ?? \"\" })}\n description={t(\"bans.liftBody\")}\n confirm={t(\"bans.lift\")}\n onConfirm={() => lifting && void lift.run(() => bans.lift(lifting.user_id))}\n />\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bans.js","names":[],"sources":["../../../src/components/social/bans.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { BanScope } from \"../../client/content/types.js\";\nimport type { BanNotice as BanNoticeData, CommentBan, PublicUser } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContentKitClient } from \"../../react/context.js\";\nimport { keyOf, useContentScope, usePaged } from \"../../react/use-resource.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Alert, AlertDescription } from \"#ckui/ui/alert\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from \"#ckui/ui/dialog\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Textarea } from \"#ckui/ui/textarea\";\nimport { LoadFailed, Pager } from \"../admin/shell.js\";\nimport { ConfirmDialog, ErrorLine, formatDate, UserAvatar, UserName, useAction } from \"./parts.js\";\n\n/** What a banned user is told: where, why and until when (never who). */\nexport function BanNotice({ ban, className }: { ban: BanNoticeData; className?: string }) {\n const m = useMessages();\n const { t } = m;\n return (\n <Alert variant=\"destructive\" className={className} data-ckui=\"ban-notice\">\n <AlertDescription className=\"grid gap-1\">\n <p className=\"font-medium\">{t(ban.scope === \"global\" ? \"bans.globalNotice\" : \"bans.ownerNotice\")}</p>\n {ban.reason && <p>{t(\"bans.reason\", { reason: ban.reason })}</p>}\n <p>{ban.until ? t(\"bans.until\", { date: formatDate(m, ban.until) }) : t(\"bans.untilLifted\")}</p>\n </AlertDescription>\n </Alert>\n );\n}\n\nconst DAY = 86_400_000;\ntype Duration = \"day\" | \"week\" | \"month\" | \"forever\" | \"custom\";\nconst span: Record<Duration, number> = { day: DAY, week: 7 * DAY, month: 30 * DAY, forever: 0, custom: 0 };\n\n/** \"YYYY-MM-DDTHH:mm\" in local time, for a datetime-local input. */\nexport function localInput(d: Date): string {\n const p = (n: number) => String(n).padStart(2, \"0\");\n return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`;\n}\n\nexport interface CommentBanDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Who to ban: their id and the name to show. */\n user: { id: string; name?: string };\n /** The scopes the caller may ban in (CommentStanding.ban_scopes); the first is preselected. */\n scopes: readonly BanScope[];\n /** The current ban, when updating one. */\n ban?: CommentBan | null;\n onSaved?: (ban: CommentBan) => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n}\n\n/** Bans a user from commenting, or replaces their ban: scope, duration presets or an end date, and a reason they see. */\nexport function CommentBanDialog({ open, onOpenChange, user, scopes, ban, onSaved, client: own, onError }: CommentBanDialogProps) {\n const m = useMessages();\n const { t } = m;\n const client = useContentKitClient(own);\n const ids = useId();\n const initialScope: BanScope = ban ? (ban.scope === \"global\" ? \"global\" : \"owner\") : (scopes[0] ?? \"owner\");\n const [scope, setScope] = useState<BanScope>(initialScope);\n const [duration, setDuration] = useState<Duration>(ban ? (ban.until ? \"custom\" : \"forever\") : \"week\");\n const [until, setUntil] = useState(ban?.until ? localInput(new Date(ban.until)) : localInput(new Date(Date.now() + 7 * DAY)));\n const [reason, setReason] = useState(ban?.reason ?? \"\");\n const { run, pending, error, setError } = useAction(\"ban.save\", onError);\n useEffect(() => {\n if (!open) return;\n setScope(initialScope);\n setDuration(ban ? (ban.until ? \"custom\" : \"forever\") : \"week\");\n setUntil(ban?.until ? localInput(new Date(ban.until)) : localInput(new Date(Date.now() + 7 * DAY)));\n setReason(ban?.reason ?? \"\");\n setError(null);\n // Reset when it opens.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n const name = user.name ?? user.id;\n const save = async () => {\n const end = duration === \"forever\" ? undefined : duration === \"custom\" ? new Date(until) : new Date(Date.now() + span[duration]);\n const saved = await run(() => client.bans.ban(scope, user.id, { reason: reason.trim() || undefined, until: end?.toISOString() }));\n if (saved) {\n onSaved?.(saved);\n onOpenChange(false);\n }\n };\n const durations: Duration[] = [\"day\", \"week\", \"month\", \"forever\", \"custom\"];\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent data-ckui=\"comment-ban-dialog\">\n <DialogHeader>\n <DialogTitle>{t(\"bans.title\", { name })}</DialogTitle>\n <DialogDescription>{t(scope === \"global\" ? \"bans.globalHelp\" : \"bans.ownerHelp\")}</DialogDescription>\n </DialogHeader>\n <form\n className=\"grid gap-4\"\n onSubmit={(e) => {\n e.preventDefault();\n void save();\n }}\n >\n {scopes.length > 1 && !ban && (\n <fieldset className=\"grid gap-2\">\n <legend className=\"mb-1 text-sm font-medium\">{t(\"bans.scope\")}</legend>\n <div className=\"flex flex-wrap gap-2\">\n {scopes.map((s) => (\n <Button key={s} type=\"button\" size=\"sm\" variant={scope === s ? \"default\" : \"outline\"} aria-pressed={scope === s} onClick={() => setScope(s)}>\n {t(s === \"global\" ? \"bans.scopeGlobal\" : \"bans.scopeOwner\")}\n </Button>\n ))}\n </div>\n </fieldset>\n )}\n <fieldset className=\"grid gap-2\">\n <legend className=\"mb-1 text-sm font-medium\">{t(\"bans.duration\")}</legend>\n <div className=\"flex flex-wrap gap-2\">\n {durations.map((d) => (\n <Button key={d} type=\"button\" size=\"sm\" variant={duration === d ? \"default\" : \"outline\"} aria-pressed={duration === d} onClick={() => setDuration(d)}>\n {t(`bans.${d}`)}\n </Button>\n ))}\n </div>\n {duration === \"custom\" && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-until`}>{t(\"bans.endsAt\")}</Label>\n <Input id={`${ids}-until`} type=\"datetime-local\" required value={until} min={localInput(new Date())} onChange={(e) => setUntil(e.target.value)} />\n </div>\n )}\n </fieldset>\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-reason`}>{t(\"bans.reasonLabel\")}</Label>\n <Textarea id={`${ids}-reason`} maxLength={500} rows={3} placeholder={t(\"bans.reasonPlaceholder\")} value={reason} onChange={(e) => setReason(e.target.value)} />\n </div>\n <ErrorLine error={error} />\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n {t(\"common.cancel\")}\n </Button>\n <Button type=\"submit\" variant=\"destructive\" disabled={pending}>\n {pending ? t(\"common.saving\") : t(ban ? \"bans.update\" : \"bans.save\")}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n\nexport interface CommentBansProps {\n /** \"global\": the site's bans (CommentBan); \"owner\": the caller's own. Default \"global\". */\n scope?: BanScope;\n pageSize?: number;\n /** The page shown (from 1), when the host keeps it, e.g. in the URL; with onPageChange. */\n page?: number;\n onPageChange?: (page: number) => void;\n userHref?: (user: PublicUser) => string | undefined;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A scope's comment bans, newest first, a page at a time: who, why, until when; ban a user by id, update or lift a ban. */\nexport function CommentBans({ scope = \"global\", pageSize = 25, page: shown, onPageChange, userHref, client: own, onError, className, appearance }: CommentBansProps) {\n const m = useMessages();\n const { t } = m;\n const { client, store, scope: readScope } = useContentScope(own);\n const [local, setLocal] = useState(1);\n const page = shown ?? local;\n const setPage = (n: number) => (onPageChange ? onPageChange(n) : setLocal(n));\n const bans = usePaged(store, keyOf(\"bans-page\", readScope, scope, pageSize), { type: \"bans\", scope }, page, pageSize, async (q, signal) =>\n (await client.bans.list(scope, q, signal)).map((b) => ({ ...b, id: b.user_id })),\n );\n const ids = useId();\n const [editing, setEditing] = useState<{ user: { id: string; name?: string }; ban?: CommentBan } | null>(null);\n const [lifting, setLifting] = useState<CommentBan | null>(null);\n const [userId, setUserId] = useState(\"\");\n const lift = useAction(\"ban.lift\", onError);\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-4 text-sm\", className)} data-ckui=\"comment-bans\">\n <form\n className=\"flex flex-wrap items-end gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n if (userId.trim()) setEditing({ user: { id: userId.trim() } });\n }}\n >\n <div className=\"grid min-w-48 flex-1 gap-1.5\">\n <Label htmlFor={`${ids}-user`}>{t(\"bans.user\")}</Label>\n <Input id={`${ids}-user`} value={userId} placeholder={t(\"bans.userPlaceholder\")} onChange={(e) => setUserId(e.target.value)} />\n </div>\n <Button type=\"submit\" variant=\"outline\" disabled={!userId.trim()}>\n {t(\"bans.add\")}\n </Button>\n </form>\n <ErrorLine error={lift.error} />\n {bans.error && !bans.items.length ? (\n <LoadFailed error={bans.error} message={t(\"bans.loadFailed\")} retry={bans.reload} />\n ) : !bans.loading && !bans.items.length ? (\n <p className=\"text-muted-foreground\">{t(\"bans.empty\")}</p>\n ) : (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\" aria-busy={bans.loading}>\n {bans.items.map((b) => {\n const name = b.user?.username ?? b.user_id;\n return (\n <li key={b.user_id} className=\"flex flex-wrap items-start gap-3 p-3\" data-ckui=\"comment-ban\">\n <UserAvatar user={b.user} name={name} />\n <div className=\"grid min-w-0 flex-1 gap-0.5\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <UserName user={b.user} name={name} href={userHref} />\n <Badge variant={b.expired ? \"outline\" : \"destructive\"}>{t(b.expired ? \"bans.expired\" : \"bans.active\")}</Badge>\n </div>\n {b.reason && <p className=\"break-words\">{t(\"bans.reason\", { reason: b.reason })}</p>}\n <p className=\"text-muted-foreground\">\n {b.until ? t(\"bans.until\", { date: formatDate(m, b.until) }) : t(\"bans.untilLifted\")} · {t(\"bans.bannedAt\", { date: formatDate(m, b.banned_at, false) })}\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" variant=\"outline\" onClick={() => setEditing({ user: { id: b.user_id, name }, ban: b })}>\n {t(\"bans.update\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" onClick={() => setLifting(b)}>\n {t(\"bans.lift\")}\n </Button>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n <Pager page={page} hasNext={bans.hasNext} onPage={setPage} disabled={bans.loading} />\n {editing && (\n <CommentBanDialog\n open\n onOpenChange={(o) => !o && setEditing(null)}\n user={editing.user}\n scopes={[scope]}\n ban={editing.ban}\n client={client}\n onError={onError}\n onSaved={() => setUserId(\"\")}\n />\n )}\n <ConfirmDialog\n open={!!lifting}\n onOpenChange={(o) => !o && setLifting(null)}\n title={t(\"bans.liftTitle\", { name: lifting?.user?.username ?? lifting?.user_id ?? \"\" })}\n description={t(\"bans.liftBody\")}\n confirm={t(\"bans.lift\")}\n onConfirm={() => lifting && void lift.run(() => client.bans.lift(scope, lifting.user_id))}\n />\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -3,4 +3,4 @@ export { BanNotice, CommentBanDialog, CommentBans, type CommentBanDialogProps, t
|
|
|
3
3
|
export { FavoriteButton, ReactionButtons, type FavoriteButtonProps, type ReactionButtonsProps } from './engagement.js';
|
|
4
4
|
export { Poll, type PollProps } from './poll.js';
|
|
5
5
|
export { PollEditor, type PollEditorProps } from './poll-editor.js';
|
|
6
|
-
export { CommentModeration, type CommentModerationProps, type ModerationSection } from './moderation.js';
|
|
6
|
+
export { CommentModeration, useCommentModerationAccess, type CommentModerationProps, type ModerationSection } from './moderation.js';
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { ContentKitClient } from '../../client/client.js';
|
|
2
1
|
import { PublicUser } from '../../client/generated/wire.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { FlowClientOptions, StaffAccess } from '../../react/admin.js';
|
|
3
|
+
import { AdminFlowProps, AdminRouteProps } from '../admin/shell.js';
|
|
5
4
|
export type ModerationSection = "comments" | "queue" | "bans";
|
|
6
|
-
export interface CommentModerationProps {
|
|
5
|
+
export interface CommentModerationProps extends AdminFlowProps, AdminRouteProps {
|
|
7
6
|
/** Content kinds offered as a filter of the comment list: codes, or codes with labels. */
|
|
8
7
|
contentKinds?: readonly (string | {
|
|
9
8
|
value: string;
|
|
@@ -15,18 +14,17 @@ export interface CommentModerationProps {
|
|
|
15
14
|
id: string;
|
|
16
15
|
}) => string | undefined;
|
|
17
16
|
userHref?: (user: PublicUser) => string | undefined;
|
|
18
|
-
/** The sections, in order
|
|
17
|
+
/** The sections, in order; the first is at basePath, the others at basePath/{section}. Default all. */
|
|
19
18
|
sections?: readonly ModerationSection[];
|
|
20
19
|
pageSize?: number;
|
|
21
|
-
client?: ContentKitClient;
|
|
22
|
-
onError?: ContentKitErrorHandler;
|
|
23
|
-
className?: string;
|
|
24
|
-
appearance?: ContentKitUiAppearance;
|
|
25
20
|
}
|
|
21
|
+
/** Which moderation sections the caller may use: every comment, the review queue, the site's bans. */
|
|
22
|
+
export declare function useCommentModerationAccess(o?: FlowClientOptions): StaffAccess<ModerationSection>;
|
|
26
23
|
/**
|
|
27
24
|
* Staff: every comment (deleted, held and rejected ones with their text) to
|
|
28
|
-
* delete, restore or ban its author
|
|
29
|
-
* posts to approve or reject; and the site's
|
|
30
|
-
*
|
|
25
|
+
* delete, restore or ban its author, by content kind or author; the review
|
|
26
|
+
* queue of held comments and posts to approve or reject; and the site's
|
|
27
|
+
* comment bans. Sections, filters and pages live in the URL under basePath;
|
|
28
|
+
* the caller sees only the sections its permissions allow.
|
|
31
29
|
*/
|
|
32
|
-
export declare function CommentModeration({
|
|
30
|
+
export declare function CommentModeration({ client: own, baseUrl, auth, guard, appearance, className, language, messages, sections, ...p }: CommentModerationProps): import('react').JSX.Element;
|