diff --git a/src/app/clothes/categories/page.tsx b/src/app/clothes/categories/page.tsx index ef4225e..44ca8c5 100644 --- a/src/app/clothes/categories/page.tsx +++ b/src/app/clothes/categories/page.tsx @@ -5,6 +5,7 @@ import ClothesShell from "@/components/clothes/ClothesShell"; import IconButton, { AddChildIcon, EditIcon, + MetaTagsIcon, TechnicalDataIcon, TrashIcon, VariantIcon, @@ -57,6 +58,9 @@ export default function CategoriesPage() { const [techTarget, setTechTarget] = useState(null); const [draftFields, setDraftFields] = useState([]); + const [seoTarget, setSeoTarget] = useState(null); + const [draftSeoDescription, setDraftSeoDescription] = useState(""); + const parentOptions = useMemo(() => flattenCategories(tree), [tree]); const reload = useCallback(async () => { @@ -146,6 +150,16 @@ export default function CategoriesPage() { setDraftFields([]); } + function openSeo(node: CategoryNode) { + setSeoTarget(node); + setDraftSeoDescription(node.seoDescription ?? ""); + } + + function closeSeo() { + setSeoTarget(null); + setDraftSeoDescription(""); + } + function addTechnicalField() { setDraftFields((prev) => [...prev, createTechnicalField()]); } @@ -316,6 +330,26 @@ export default function CategoriesPage() { } } + async function handleSeoSubmit(event: FormEvent) { + event.preventDefault(); + if (!seoTarget) return; + setSaving(true); + setError(null); + try { + const nextTree = await updateCategory(seoTarget.id, { + seoDescription: draftSeoDescription.trim(), + }); + setTree(nextTree); + closeSeo(); + } catch (err) { + setError( + err instanceof ApiError ? err.message : "ذخیره توضیحات SEO ناموفق بود.", + ); + } finally { + setSaving(false); + } + } + return ( <> openCreate(id)} onVariants={openVariants} onTechnical={openTechnical} + onSeo={openSeo} onDelete={handleDelete} delay={80 + i * 60} /> @@ -665,6 +700,32 @@ export default function CategoriesPage() { + افزودن فیلد متنی + + +

+ این متن در صفحه دسته در فروشگاه نمایش داده می‌شود و برای SEO استفاده + می‌شود. +

+
+ +