From 5140d23d73af5bbea3a667023cddeca27fd4aa28 Mon Sep 17 00:00:00 2001 From: BOHA Date: Sat, 6 Jun 2026 11:13:43 +0200 Subject: [PATCH] feat(projects-ui): add project create modal, type badge, empty-state copy Co-Authored-By: Claude Sonnet 4.6 --- src/admin/pages/Projects.tsx | 211 ++++++++++++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 2 deletions(-) diff --git a/src/admin/pages/Projects.tsx b/src/admin/pages/Projects.tsx index cdbd066..8816546 100644 --- a/src/admin/pages/Projects.tsx +++ b/src/admin/pages/Projects.tsx @@ -1,16 +1,24 @@ import { useState } from "react"; +import { useQuery } from "@tanstack/react-query"; import { useAlert } from "../context/AlertContext"; import { useAuth } from "../context/AuthContext"; import { Link } from "react-router-dom"; import Forbidden from "../components/Forbidden"; import { motion } from "framer-motion"; import ConfirmModal from "../components/ConfirmModal"; +import FormModal from "../components/FormModal"; +import FormField from "../components/FormField"; import { formatDate, czechPlural } from "../utils/formatters"; import SortIcon from "../components/SortIcon"; import useTableSort from "../hooks/useTableSort"; import { usePaginatedQuery } from "../hooks/usePaginatedQuery"; -import { projectListOptions } from "../lib/queries/projects"; +import { + projectListOptions, + projectNextNumberOptions, +} from "../lib/queries/projects"; +import { offerCustomersOptions } from "../lib/queries/offers"; +import { userListOptions } from "../lib/queries/users"; import { useApiMutation } from "../lib/queries/mutations"; import Pagination from "../components/Pagination"; @@ -73,6 +81,69 @@ export default function Projects() { }, }); + const [showCreate, setShowCreate] = useState(false); + const [createForm, setCreateForm] = useState({ + name: "", + customer_id: "", + responsible_user_id: "", + status: "aktivni", + start_date: "", + end_date: "", + notes: "", + }); + const [createErrors, setCreateErrors] = useState>({}); + const [creating, setCreating] = useState(false); + + const customersQuery = useQuery({ + ...offerCustomersOptions(), + enabled: showCreate, + }); + const usersQuery = useQuery({ ...userListOptions(), enabled: showCreate }); + const nextNumberQuery = useQuery({ + ...projectNextNumberOptions(), + enabled: showCreate, + }); + + const createMutation = useApiMutation({ + url: () => `${API_BASE}/projects`, + method: () => "POST", + invalidate: ["projects", "orders", "offers", "invoices", "attendance"], + onSuccess: () => { + setShowCreate(false); + alert.success("Projekt byl vytvořen"); + }, + }); + + const openCreate = () => { + setCreateForm({ + name: "", + customer_id: "", + responsible_user_id: "", + status: "aktivni", + start_date: "", + end_date: "", + notes: "", + }); + setCreateErrors({}); + setShowCreate(true); + }; + + const handleCreate = async () => { + const errs: Record = {}; + if (!createForm.name.trim()) errs.name = "Zadejte název projektu"; + setCreateErrors(errs); + if (Object.keys(errs).length > 0) return; + + setCreating(true); + try { + await createMutation.mutateAsync(createForm); + } catch (e) { + alert.error(e instanceof Error ? e.message : "Chyba připojení"); + } finally { + setCreating(false); + } + }; + const { items: projects, pagination, @@ -126,6 +197,27 @@ export default function Projects() { )}

+ {hasPermission("projects.create") && ( +
+ +
+ )} - Projekt se vytvoří automaticky při vytvoření objednávky. + Projekty vznikají z objednávek nebo je můžete vytvořit ručně + tlačítkem „Přidat projekt".

) : ( @@ -233,6 +326,7 @@ export default function Projects() { order={order} /> + Typ Objednávka Akce @@ -257,6 +351,11 @@ export default function Projects() { {formatDate(p.start_date)} {formatDate(p.end_date)} + + + {p.order_id ? "Z objednávky" : "Samostatný"} + + {p.order_id ? ( + + setShowCreate(false)} + onSubmit={handleCreate} + title="Přidat projekt" + submitLabel="Vytvořit" + loading={creating} + > +
+ + { + setCreateForm({ ...createForm, name: e.target.value }); + setCreateErrors((p) => ({ ...p, name: "" })); + }} + className="admin-form-input" + placeholder="Název projektu" + aria-invalid={!!createErrors.name} + /> + + +
+ + + + + + + +
+ +
+ + + setCreateForm({ ...createForm, start_date: e.target.value }) + } + className="admin-form-input" + /> + + + + setCreateForm({ ...createForm, end_date: e.target.value }) + } + className="admin-form-input" + /> + +
+ + +