refactor(admin): organize user actions and improve content editing UX
CI / check (push) Successful in 1m32s
CI / deploy (push) Successful in 1m11s
CI / e2e (push) Successful in 21s

This commit is contained in:
Simo committed 2026-09-05 18:56:56 +02:00
1 parent e17346ab9f
commit c6b919c01d
10 files changed
+688 -458

No files matched your search

+57
View File
@@ -0,0 +1,57 @@
"use client";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
function escapeHtml(value: string) {
return value.replace(
/[&<>"']/g,
(c) =>
({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[
c
] ?? c,
);
}
export interface ArticlePreviewData {
title: string;
image: string;
summary: string;
body: string;
}
export function ArticlePreview({
data,
onClose,
}: {
data: ArticlePreviewData | null;
onClose: () => void;
}) {
const document = data
? `<!doctype html><html><head><meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https: http: data:; style-src 'unsafe-inline'"><meta name="viewport" content="width=device-width, initial-scale=1"><style>:root{color-scheme:light;background:Canvas;color:CanvasText}body{font:16px/1.6 system-ui;padding:20px;margin:auto;max-width:760px;overflow-wrap:anywhere}img{max-width:100%;height:auto}h1{line-height:1.2}pre{white-space:pre-wrap}</style></head><body><h1>${escapeHtml(data.title)}</h1>${data.image ? `<img src="${escapeHtml(data.image)}" alt="">` : ""}<p>${escapeHtml(data.summary)}</p>${data.body}</body></html>`
: "";
return (
<Dialog
open={!!data}
onOpenChange={(open) => {
if (!open) onClose();
}}
>
<DialogContent className="sm:max-w-4xl h-[85dvh] flex flex-col min-h-0">
<DialogHeader>
<DialogTitle>Article preview</DialogTitle>
</DialogHeader>
<p className="text-xs text-muted-foreground">
Content preview. The published page applies the site theme.
</p>
<iframe
title="Article content preview"
sandbox=""
srcDoc={document}
className="min-h-0 flex-1 w-full bg-background rounded border"
/>
</DialogContent>
</Dialog>
);
}