feat: comprehensive CMS improvements

- Fix DOMPurify SSR crash (use isomorphic-dompurify)
- Fix SanitizedHtml to sanitize by default
- Add auth guards to studio/catalog maintenance pages
- Add update/edit to vouchers CRUD
- Add update/edit to rare-values CRUD
- Add approve workflow to applications page
- Add edit form to guilds detail page
- Add SEO metadata to all public pages (21 pages)
- Fix mobile nav accessibility (focus trap, aria attributes)
- Fix missing labels and table accessibility
- Add dynamic imports for heavy client components (6 components)
- Fix silent error swallowing (40+ locations)
- Add content scheduling for articles (publishAt, status)
- Wire up 12 missing webhook notification triggers
- Add global search to admin panel
- Add bulk actions to admin users table
- Fix JSON formatting and a11y issues
This commit is contained in:
openhands committed 2026-09-03 16:00:32 +02:00
1 parent b810b16672
commit 30c95b1a5c
78 files changed
+2252 -134

No files matched your search

+4 -4
View File
@@ -74,10 +74,10 @@ export default async function ModBansPage() {
<table>
<thead>
<tr>
<th>{t("colUserId")}</th>
<th>{t("colType")}</th>
<th>{t("colReason")}</th>
<th>{t("colExpires")}</th>
<th scope="col">{t("colUserId")}</th>
<th scope="col">{t("colType")}</th>
<th scope="col">{t("colReason")}</th>
<th scope="col">{t("colExpires")}</th>
</tr>
</thead>
<tbody>
+11 -3
View File
@@ -15,6 +15,7 @@ import { requireModPermission } from "@/lib/admin/guard";
import { calcPagination, parseListParams } from "@/lib/admin-helpers";
import { db, SupportTickets, User } from "@/lib/db";
import { PERMS } from "@/lib/permissions";
import { logServerError } from "@/lib/server-log";
const SORT_COLS = {
id: SupportTickets.id,
@@ -55,7 +56,8 @@ export default async function ModCfhListPage({
.where(like(User.username, `%${q}%`))
.limit(50);
userIds = users.map((u) => u.id);
} catch {
} catch (error) {
logServerError("cfh.user_search_failed", error, { query: q });
userIds = [];
}
@@ -91,7 +93,10 @@ export default async function ModCfhListPage({
.from(SupportTickets)
.where(where)
.then((rows) => rows[0]?.total ?? 0)
.catch(() => 0);
.catch((error) => {
logServerError("cfh.count_failed", error);
return 0;
});
const pagination = calcPagination(total, parsed.page, parsed.perPage);
const tickets = await db
@@ -101,7 +106,10 @@ export default async function ModCfhListPage({
.orderBy(finalOrder)
.offset(pagination.offset)
.limit(pagination.perPage)
.catch(() => []);
.catch((error) => {
logServerError("cfh.query_failed", error);
return [];
});
const userIds = [
...new Set([
+9 -2
View File
@@ -8,6 +8,7 @@ import { calcPagination, parseListParams } from "@/lib/admin-helpers";
import { db, User } from "@/lib/db";
import { getAvatarUrl } from "@/lib/imager";
import { PERMS } from "@/lib/permissions";
import { logServerError } from "@/lib/server-log";
import type { DataTableColumn } from "@/types/common";
type ModUserRow = {
@@ -139,7 +140,10 @@ export default async function ModUsersPage({
.from(User)
.where(where)
.then((rows) => rows[0]?.total ?? 0)
.catch(() => 0),
.catch((error) => {
logServerError("users.count_failed", error);
return 0;
}),
db
.select({
id: User.id,
@@ -155,7 +159,10 @@ export default async function ModUsersPage({
.orderBy(finalOrder)
.offset((parsed.page - 1) * parsed.perPage)
.limit(parsed.perPage)
.catch(() => [] as ModUserRow[]),
.catch((error) => {
logServerError("users.query_failed", error);
return [] as ModUserRow[];
}),
]);
const total = totals;