175 lines
5.2 KiB
TypeScript
175 lines
5.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useTransition } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { toast } from "sonner";
|
|
import { Loader2, RotateCw, Save } from "lucide-react";
|
|
import {
|
|
updateAnnouncement,
|
|
retryAnnouncementEmails,
|
|
} from "@/actions/announcements";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
import { MarkdownContent } from "@/components/markdown-content";
|
|
|
|
export function EditNewsForm({
|
|
announcement,
|
|
}: {
|
|
announcement: {
|
|
id: string;
|
|
revision: number;
|
|
title: string;
|
|
content: string;
|
|
};
|
|
}) {
|
|
const router = useRouter();
|
|
const [pending, startTransition] = useTransition();
|
|
const [content, setContent] = useState(announcement.content);
|
|
const [sendEmail, setSendEmail] = useState(false);
|
|
function submit(formData: FormData) {
|
|
formData.set("id", announcement.id);
|
|
formData.set("revision", String(announcement.revision));
|
|
formData.set("sendEmail", sendEmail ? "on" : "off");
|
|
startTransition(async () => {
|
|
try {
|
|
const result = await updateAnnouncement(formData);
|
|
if (!("success" in result)) {
|
|
toast.error(result.error);
|
|
return;
|
|
}
|
|
toast.success(
|
|
result.queued
|
|
? `Mitteilung gespeichert. ${result.queued} E-Mails sind zum Versand vorgemerkt.`
|
|
: result.changed
|
|
? "Mitteilung aktualisiert."
|
|
: "Keine Änderungen vorhanden.",
|
|
);
|
|
setSendEmail(false);
|
|
router.refresh();
|
|
} catch {
|
|
toast.error(
|
|
"Mitteilung konnte nicht gespeichert werden. Bitte versuche es erneut.",
|
|
);
|
|
}
|
|
});
|
|
}
|
|
return (
|
|
<form action={submit} className="grid gap-5">
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="edit-title">Titel</Label>
|
|
<Input
|
|
id="edit-title"
|
|
name="title"
|
|
defaultValue={announcement.title}
|
|
minLength={3}
|
|
maxLength={160}
|
|
required
|
|
disabled={pending}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="edit-content">Text</Label>
|
|
<Textarea
|
|
id="edit-content"
|
|
name="content"
|
|
rows={10}
|
|
value={content}
|
|
onChange={(event) => setContent(event.target.value)}
|
|
minLength={3}
|
|
maxLength={20_000}
|
|
required
|
|
disabled={pending}
|
|
/>
|
|
<p className="text-xs text-[var(--ink-muted)]">
|
|
Markdown ist aktiv. HTML wird nicht interpretiert.
|
|
</p>
|
|
</div>
|
|
<details className="rounded-lg border border-[var(--hairline)] bg-[var(--surface-2)] p-4">
|
|
<summary className="cursor-pointer text-sm font-medium">
|
|
Vorschau ansehen
|
|
</summary>
|
|
<div className="mt-4 text-sm">
|
|
<MarkdownContent content={content} />
|
|
</div>
|
|
</details>
|
|
<div className="rounded-lg border border-[var(--hairline)] bg-[var(--surface-2)] p-4">
|
|
<div className="flex items-start gap-2">
|
|
<Checkbox
|
|
id="correction-email"
|
|
checked={sendEmail}
|
|
disabled={pending}
|
|
onCheckedChange={(checked) => setSendEmail(checked === true)}
|
|
/>
|
|
<Label
|
|
htmlFor="correction-email"
|
|
className="cursor-pointer leading-5"
|
|
>
|
|
Aktuelle Fassung auch per E-Mail senden
|
|
</Label>
|
|
</div>
|
|
<p className="mt-2 text-xs leading-5 text-[var(--ink-soft)]">
|
|
Bei einer Textänderung erscheint der Beitrag für alle wieder als
|
|
ungelesen. Jede Fassung wird je Person höchstens einmal zum Versand
|
|
vorgemerkt.
|
|
</p>
|
|
</div>
|
|
<Button type="submit" disabled={pending} className="justify-self-end">
|
|
{pending ? (
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
) : (
|
|
<Save className="h-4 w-4" />
|
|
)}
|
|
Änderungen speichern
|
|
</Button>
|
|
</form>
|
|
);
|
|
}
|
|
|
|
export function RetryNewsEmails({
|
|
id,
|
|
revision,
|
|
failed,
|
|
}: {
|
|
id: string;
|
|
revision: number;
|
|
failed: number;
|
|
}) {
|
|
const [pending, startTransition] = useTransition();
|
|
const router = useRouter();
|
|
return (
|
|
<Button
|
|
variant="outline"
|
|
disabled={pending || failed === 0}
|
|
onClick={() =>
|
|
startTransition(async () => {
|
|
try {
|
|
const result = await retryAnnouncementEmails(id, revision);
|
|
if (!("success" in result)) {
|
|
toast.error(result.error);
|
|
return;
|
|
}
|
|
toast.success(
|
|
result.queued
|
|
? `${result.queued} fehlgeschlagene E-Mails werden erneut versucht.`
|
|
: "Keine fehlgeschlagenen Sendungen vorhanden.",
|
|
);
|
|
router.refresh();
|
|
} catch {
|
|
toast.error("Versand konnte nicht gestartet werden.");
|
|
}
|
|
})
|
|
}
|
|
>
|
|
{pending ? (
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
) : (
|
|
<RotateCw className="h-4 w-4" />
|
|
)}
|
|
Fehlgeschlagene erneut versuchen
|
|
</Button>
|
|
);
|
|
}
|