feat(editorial): validate publications and preserve partial event updates
CI / check (push) Successful in 57s
CI / deploy (push) Successful in 18s
CI / publish-container (push) Successful in 1m20s

This commit is contained in:
Simo committed 2026-09-11 00:36:55 +02:00
1 parent 76f0420d64
commit db4acbb46e
14 files changed
+613 -16

No files matched your search

+53 -3
View File
@@ -1,8 +1,11 @@
"use client";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { createEvent, updateEvent } from "@/actions/events";
import { ArticlePreview } from "@/components/admin/article-preview";
import { PublicationPreflight } from "@/components/admin/publication-preflight";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { DateTimePicker } from "@/components/ui/date-time-picker";
@@ -17,6 +20,7 @@ import {
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { useServerAction } from "@/hooks/use-server-action";
import { publicationIssues } from "@/lib/publication-preflight";
import type {
CreateEventInput,
UpdateEventInput,
@@ -49,6 +53,8 @@ interface EventFormValues {
export function EventForm({ eventTypes, defaultValues }: EventFormProps) {
const t = useTranslations("pages.admin.events");
const tAction = useTranslations("pages.admin.actions");
const [preview, setPreview] = useState(false);
const { run, isPending } = useServerAction();
const isEditing = !!defaultValues?.id;
@@ -63,7 +69,16 @@ export function EventForm({ eventTypes, defaultValues }: EventFormProps) {
},
});
const values = form.watch();
const issues = publicationIssues({ kind: "event", ...values });
function onSubmit(data: EventFormValues) {
if (
data.status === "published" &&
publicationIssues({ kind: "event", ...data }).some(
(issue) => issue.severity === "error",
)
)
return;
if (isEditing && defaultValues?.id) {
run(
() =>
@@ -152,7 +167,7 @@ export function EventForm({ eventTypes, defaultValues }: EventFormProps) {
<DateTimePicker
value={form.watch("endsAt")}
onChange={(d) =>
form.setValue("endsAt", d, { shouldDirty: true })
form.setValue("endsAt", d ?? null, { shouldDirty: true })
}
placeholder={t("formSelectEnd")}
/>
@@ -214,8 +229,43 @@ export function EventForm({ eventTypes, defaultValues }: EventFormProps) {
/>
</div>
<div className="flex justify-end">
<Button type="submit" disabled={isPending}>
<PublicationPreflight
issues={issues}
paths={[
"/events",
...(defaultValues?.id ? [`/events/${defaultValues.id}`] : []),
]}
/>
<ArticlePreview
title={tAction("preview")}
data={
preview
? {
title: values.title,
image: values.image ?? "",
summary: values.description,
body: "",
}
: null
}
onClose={() => setPreview(false)}
/>
<div className="flex justify-end gap-2">
<Button
type="button"
variant="outline"
onClick={() => setPreview(true)}
>
{tAction("preview")}
</Button>
<Button
type="submit"
disabled={
isPending ||
(values.status === "published" &&
issues.some((issue) => issue.severity === "error"))
}
>
{isPending
? t("formSaving")
: isEditing