feat(plan): add Sprava kategorii management modal and button
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
145
src/admin/components/PlanCategoriesModal.tsx
Normal file
145
src/admin/components/PlanCategoriesModal.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import { useState } from "react";
|
||||
import FormModal from "./FormModal";
|
||||
import { useApiMutation } from "../lib/queries/mutations";
|
||||
import { useAlert } from "../context/AlertContext";
|
||||
import { PlanCategory } from "../lib/queries/plan";
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
categories: PlanCategory[];
|
||||
}
|
||||
|
||||
const DEFAULT_NEW_COLOR = "#2563eb";
|
||||
|
||||
export default function PlanCategoriesModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
categories,
|
||||
}: Props) {
|
||||
const alert = useAlert();
|
||||
const [newLabel, setNewLabel] = useState("");
|
||||
const [newColor, setNewColor] = useState(DEFAULT_NEW_COLOR);
|
||||
|
||||
const createCat = useApiMutation<
|
||||
{ label: string; color: string },
|
||||
PlanCategory
|
||||
>({
|
||||
url: "/api/admin/plan/categories",
|
||||
method: "POST",
|
||||
invalidate: ["plan", "dashboard", "audit-log"],
|
||||
});
|
||||
const updateCat = useApiMutation<
|
||||
{ id: number; label?: string; color?: string; is_active?: boolean },
|
||||
PlanCategory
|
||||
>({
|
||||
url: (v) => `/api/admin/plan/categories/${v.id}`,
|
||||
method: "PATCH",
|
||||
invalidate: ["plan", "dashboard", "audit-log"],
|
||||
});
|
||||
|
||||
const handleAdd = () => {
|
||||
const label = newLabel.trim();
|
||||
if (!label) {
|
||||
alert.error("Zadejte název kategorie");
|
||||
return;
|
||||
}
|
||||
createCat.mutate(
|
||||
{ label, color: newColor },
|
||||
{
|
||||
onSuccess: () => {
|
||||
setNewLabel("");
|
||||
setNewColor(DEFAULT_NEW_COLOR);
|
||||
},
|
||||
onError: (e) => alert.error(e.message),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const patch = (
|
||||
id: number,
|
||||
body: { label?: string; color?: string; is_active?: boolean },
|
||||
) => {
|
||||
updateCat.mutate(
|
||||
{ id, ...body },
|
||||
{ onError: (e) => alert.error(e.message) },
|
||||
);
|
||||
};
|
||||
|
||||
const busy = createCat.isPending || updateCat.isPending;
|
||||
|
||||
return (
|
||||
<FormModal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
title="Správa kategorií"
|
||||
hideFooter
|
||||
size="md"
|
||||
>
|
||||
<div className="plan-cat-manager">
|
||||
{categories.map((c) => (
|
||||
<div
|
||||
key={c.id}
|
||||
className={`plan-cat-row${c.is_active ? "" : " plan-cat-row--inactive"}`}
|
||||
>
|
||||
<input
|
||||
type="color"
|
||||
className="plan-cat-color"
|
||||
value={c.color}
|
||||
disabled={busy}
|
||||
onChange={(e) => patch(c.id, { color: e.target.value })}
|
||||
aria-label={`Barva – ${c.label}`}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
className="admin-form-input"
|
||||
defaultValue={c.label}
|
||||
disabled={busy}
|
||||
onBlur={(e) => {
|
||||
const v = e.target.value.trim();
|
||||
if (v && v !== c.label) patch(c.id, { label: v });
|
||||
}}
|
||||
aria-label={`Název – ${c.label}`}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="admin-btn admin-btn-secondary admin-btn-sm"
|
||||
disabled={busy}
|
||||
onClick={() => patch(c.id, { is_active: !c.is_active })}
|
||||
>
|
||||
{c.is_active ? "Skrýt" : "Obnovit"}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="plan-cat-row plan-cat-row--new">
|
||||
<input
|
||||
type="color"
|
||||
className="plan-cat-color"
|
||||
value={newColor}
|
||||
disabled={busy}
|
||||
onChange={(e) => setNewColor(e.target.value)}
|
||||
aria-label="Barva nové kategorie"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
className="admin-form-input"
|
||||
placeholder="Nová kategorie…"
|
||||
value={newLabel}
|
||||
disabled={busy}
|
||||
onChange={(e) => setNewLabel(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && handleAdd()}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="admin-btn admin-btn-primary admin-btn-sm"
|
||||
disabled={busy}
|
||||
onClick={handleAdd}
|
||||
>
|
||||
Přidat
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</FormModal>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user