"use client"; import React, { useEffect, useState } from "react"; import { FolderTree, Edit3, Trash2, Plus, ChevronRight, ChevronDown, } from "lucide-react"; import { getTree, getFlat, updateCategory, createCategory, deleteCategory } from "@/lib/categories.api"; /* ================= TYPES ================= */ interface Category { id: string; name: string; slug: string; parentId?: string | null; children?: Category[]; } /* ================= API ================= */ /* ================= UTIL ================= */ function slugify(text: string) { return text .toLowerCase() .trim() .replace(/\s+/g, "-") .replace(/[^a-z0-9-]/g, ""); } /* ================= PAGE ================= */ export default function CategoriesPage() { const [tree, setTree] = useState([]); const [flat, setFlat] = useState([]); const [loading, setLoading] = useState(false); const [modalOpen, setModalOpen] = useState(false); const [form, setForm] = useState({ id: null as string | null, name: "", slug: "", parentId: null as string | null, }); /* ================= LOAD ================= */ async function load() { setLoading(true); try { const [t, f] = await Promise.all([getTree(), getFlat()]); setTree(t); setFlat(f); } finally { setLoading(false); } } useEffect(() => { load(); }, []); /* ================= CRUD ================= */ async function save() { const payload = { name: form.name, slug: form.slug || slugify(form.name), parentId: form.parentId, }; if (form.id) { await updateCategory(form.id, payload); } else { await createCategory(payload); } closeModal(); load(); } async function remove(id: string) { if (!confirm("Delete this category?")) return; await deleteCategory(id); load(); } /* ================= MODAL ================= */ function openCreate(parentId?: string) { setForm({ id: null, name: "", slug: "", parentId: parentId || null, }); setModalOpen(true); } function openEdit(cat: Category) { setForm({ id: cat.id, name: cat.name, slug: cat.slug, parentId: cat.parentId || null, }); setModalOpen(true); } function closeModal() { setModalOpen(false); setForm({ id: null, name: "", slug: "", parentId: null }); } /* ================= TREE ================= */ function TreeNode({ node, level = 0, }: { node: Category; level?: number; }) { const [open, setOpen] = useState(true); return (
{/* NODE */}
openEdit(node)} className="text-sm font-medium cursor-pointer hover:text-blue-600" > {node.name}
{/* ACTIONS */}
{/* CHILDREN */} {open && node.children?.map((child) => ( ))}
); } /* ================= UI ================= */ return (
{/* HEADER */}

Category Manager

{/* TREE */}
{loading ? (

Loading...

) : ( tree.map((node) => ( )) )}
{/* MODAL */} {modalOpen && (

{form.id ? "Edit Category" : "Create Category"}

setForm({ ...form, name: e.target.value, slug: slugify(e.target.value), }) } /> setForm({ ...form, slug: e.target.value }) } />
)}
); }