diff --git a/src/admin/pages/WarehouseCategories.tsx b/src/admin/pages/WarehouseCategories.tsx new file mode 100644 index 0000000..ffde9d7 --- /dev/null +++ b/src/admin/pages/WarehouseCategories.tsx @@ -0,0 +1,395 @@ +import { useState } from "react"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useAlert } from "../context/AlertContext"; +import { useAuth } from "../context/AuthContext"; +import Forbidden from "../components/Forbidden"; +import { motion, AnimatePresence } from "framer-motion"; +import ConfirmModal from "../components/ConfirmModal"; +import useModalLock from "../hooks/useModalLock"; + +import apiFetch from "../utils/api"; +import FormField from "../components/FormField"; +import { + warehouseCategoryListOptions, + type WarehouseCategory, +} from "../lib/queries/warehouse"; + +const API_BASE = "/api/admin/warehouse/categories"; + +interface CategoryForm { + name: string; + description: string; + sort_order: number; +} + +export default function WarehouseCategories() { + const alert = useAlert(); + const { hasPermission } = useAuth(); + const queryClient = useQueryClient(); + + const { data: categories = [], isPending } = useQuery( + warehouseCategoryListOptions(), + ); + + const [showModal, setShowModal] = useState(false); + const [editingCategory, setEditingCategory] = + useState(null); + const [form, setForm] = useState({ + name: "", + description: "", + sort_order: 0, + }); + + const [errors, setErrors] = useState>({}); + const [deleteConfirm, setDeleteConfirm] = useState<{ + show: boolean; + category: WarehouseCategory | null; + }>({ show: false, category: null }); + + useModalLock(showModal); + + if (!hasPermission("warehouse.manage")) return ; + + const openCreateModal = () => { + setEditingCategory(null); + setForm({ + name: "", + description: "", + sort_order: 0, + }); + setErrors({}); + setShowModal(true); + }; + + const openEditModal = (category: WarehouseCategory) => { + setEditingCategory(category); + setForm({ + name: category.name, + description: category.description || "", + sort_order: category.sort_order, + }); + setErrors({}); + setShowModal(true); + }; + + const handleSubmit = async () => { + const newErrors: Record = {}; + if (!form.name.trim()) newErrors.name = "Zadejte název kategorie"; + setErrors(newErrors); + if (Object.keys(newErrors).length > 0) return; + + try { + const url = editingCategory + ? `${API_BASE}/${editingCategory.id}` + : API_BASE; + const method = editingCategory ? "PUT" : "POST"; + + const response = await apiFetch(url, { + method, + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(form), + }); + + const result = await response.json(); + + if (result.success) { + setShowModal(false); + queryClient.invalidateQueries({ + queryKey: ["warehouse", "categories"], + }); + queryClient.invalidateQueries({ queryKey: ["warehouse"] }); + alert.success(result.message); + } else { + alert.error(result.error); + } + } catch { + alert.error("Chyba připojení"); + } + }; + + const handleDelete = async () => { + if (!deleteConfirm.category) return; + + try { + const response = await apiFetch( + `${API_BASE}/${deleteConfirm.category.id}`, + { + method: "DELETE", + }, + ); + + const result = await response.json(); + + if (result.success) { + setDeleteConfirm({ show: false, category: null }); + queryClient.invalidateQueries({ + queryKey: ["warehouse", "categories"], + }); + queryClient.invalidateQueries({ queryKey: ["warehouse"] }); + alert.success(result.message); + } else { + alert.error(result.error); + } + } catch { + alert.error("Chyba připojení"); + } + }; + + if (isPending) { + return ( +
+
+
+ ); + } + + return ( +
+ +
+

Kategorie skladu

+
+
+ +
+
+ + +
+ {categories.length === 0 && ( +
+
+ + + +
+

Zatím nejsou žádné kategorie.

+ +
+ )} + {categories.length > 0 && ( +
+ + + + + + + + + + + {categories.map((category) => ( + + + + + + + ))} + +
NázevPopisPořadíAkce
{category.name}{category.description || "—"}{category.sort_order} +
+ + +
+
+
+ )} +
+
+ + {/* Add/Edit Modal */} + + {showModal && ( + +
setShowModal(false)} + /> + +
+

+ {editingCategory ? "Upravit kategorii" : "Přidat kategorii"} +

+
+ +
+
+ + { + setForm({ ...form, name: e.target.value }); + setErrors((prev) => ({ ...prev, name: "" })); + }} + className="admin-form-input" + placeholder="Název kategorie" + aria-invalid={!!errors.name} + /> + + + +