feat: comprehensive CMS improvements
CI / runtime-diagnostics (push) Skipped
CI / release (push) Skipped
CI / check (push) Failing after 0s
CI / deploy (push) Skipped

- 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

+36
View File
@@ -18,6 +18,8 @@ export function ArticleForm({
image?: string;
shortStory?: string;
fullStory?: string;
status?: string;
publishAt?: string;
};
}) {
const t = useTranslations("pages.admin.articles.form");
@@ -26,6 +28,7 @@ export function ArticleForm({
const [slugTouched, setSlugTouched] = useState(Boolean(defaultValues?.slug));
const [image, setImage] = useState(defaultValues?.image ?? "");
const [imageError, setImageError] = useState(false);
const [status, setStatus] = useState(defaultValues?.status ?? "published");
const suggestedSlug = useMemo(() => slugify(title), [title]);
@@ -132,6 +135,39 @@ export function ArticleForm({
/>
</div>
<div className="grid gap-4 md:grid-cols-2">
<label className="block">
<span className="text-xs font-medium text-[var(--admin-text-muted)]">
{t("status")}
</span>
<select
name="status"
value={status}
onChange={(e) => setStatus(e.target.value)}
className="input input-bordered mt-1 w-full"
>
<option value="draft">{t("statusDraft")}</option>
<option value="scheduled">{t("statusScheduled")}</option>
<option value="published">{t("statusPublished")}</option>
</select>
</label>
{status === "scheduled" ? (
<label className="block">
<span className="text-xs font-medium text-[var(--admin-text-muted)]">
{t("publishAt")}
</span>
<input
type="datetime-local"
name="publishAt"
defaultValue={defaultValues?.publishAt ?? ""}
required
className="input input-bordered mt-1 w-full"
/>
</label>
) : null}
</div>
<button type="submit" className="btn btn-primary justify-self-start">
{t("save")}
</button>