refactor(admin): organize user actions and improve content editing UX
This commit is contained in:
1 parent
e17346ab9f
commit
c6b919c01d
10 files changed
+688
-458
No files matched your search
@@ -1,8 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { useFormStatus } from "react-dom";
|
||||
import { slugify } from "@/lib/format";
|
||||
import { ArticlePreview, type ArticlePreviewData } from "./article-preview";
|
||||
import { MediaPicker } from "./media-picker";
|
||||
import { RichText } from "./rich-text";
|
||||
|
||||
@@ -30,10 +32,13 @@ export function ArticleForm({
|
||||
const [imageError, setImageError] = useState(false);
|
||||
const [status, setStatus] = useState(defaultValues?.status ?? "published");
|
||||
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
const [preview, setPreview] = useState<ArticlePreviewData | null>(null);
|
||||
const suggestedSlug = useMemo(() => slugify(title), [title]);
|
||||
|
||||
return (
|
||||
<form
|
||||
ref={formRef}
|
||||
action={action}
|
||||
className="admin-card"
|
||||
style={{ display: "grid", gap: "1rem" }}
|
||||
@@ -76,6 +81,7 @@ export function ArticleForm({
|
||||
</span>
|
||||
<div className="mt-1 flex gap-2 items-start">
|
||||
<input
|
||||
aria-label={t("image")}
|
||||
name="image"
|
||||
value={image}
|
||||
onChange={(e) => {
|
||||
@@ -83,7 +89,7 @@ export function ArticleForm({
|
||||
setImageError(false);
|
||||
}}
|
||||
placeholder={t("imagePlaceholder")}
|
||||
className="input input-bordered flex-1"
|
||||
className="input input-bordered min-w-0 flex-1"
|
||||
/>
|
||||
<MediaPicker
|
||||
onSelect={(url) => {
|
||||
@@ -168,9 +174,40 @@ export function ArticleForm({
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<button type="submit" className="btn btn-primary justify-self-start">
|
||||
{t("save")}
|
||||
</button>
|
||||
<div className="sticky bottom-0 flex flex-wrap gap-2 border-t border-[var(--admin-border)] bg-[var(--admin-surface)] py-3">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline"
|
||||
onClick={() => {
|
||||
if (!formRef.current) return;
|
||||
const values = new FormData(formRef.current);
|
||||
setPreview({
|
||||
title: String(values.get("title") ?? ""),
|
||||
image: String(values.get("image") ?? ""),
|
||||
summary: String(values.get("shortStory") ?? ""),
|
||||
body: String(values.get("fullStory") ?? ""),
|
||||
});
|
||||
}}
|
||||
>
|
||||
Preview
|
||||
</button>
|
||||
<ArticleSaveButton label={t("save")} />
|
||||
</div>
|
||||
<ArticlePreview data={preview} onClose={() => setPreview(null)} />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function ArticleSaveButton({ label }: { label: string }) {
|
||||
const { pending } = useFormStatus();
|
||||
return (
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary"
|
||||
disabled={pending}
|
||||
aria-busy={pending}
|
||||
>
|
||||
{pending ? "Saving…" : label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user