diff --git a/src/admin/pages/OffersTemplates.tsx b/src/admin/pages/OffersTemplates.tsx
index 4f91c14..107fcca 100644
--- a/src/admin/pages/OffersTemplates.tsx
+++ b/src/admin/pages/OffersTemplates.tsx
@@ -1,11 +1,11 @@
-import { useState, useRef, type ReactNode } from "react";
+import { useState, useRef } from "react";
import { useQuery } from "@tanstack/react-query";
+import { motion } from "framer-motion";
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+import IconButton from "@mui/material/IconButton";
import { useAlert } from "../context/AlertContext";
import { useAuth } from "../context/AuthContext";
-import { motion } from "framer-motion";
-import ConfirmModal from "../components/ConfirmModal";
-import FormModal from "../components/FormModal";
-import FormField from "../components/FormField";
import Forbidden from "../components/Forbidden";
import RichEditor from "../components/RichEditor";
import {
@@ -16,8 +16,24 @@ import {
type ScopeSection,
} from "../lib/queries/offers";
import { useApiMutation } from "../lib/queries/mutations";
-
import apiFetch from "../utils/api";
+import {
+ Button,
+ Card,
+ DataTable,
+ Modal,
+ ConfirmDialog,
+ Field,
+ TextField,
+ StatusChip,
+ PageHeader,
+ EmptyState,
+ LoadingState,
+ Tabs,
+ TabPanel,
+ type DataColumn,
+ type TabDef,
+} from "../ui";
const API_BASE = "/api/admin";
@@ -33,45 +49,130 @@ interface ScopeForm {
sections: ScopeSection[];
}
+const PlusIcon = (
+
+);
+const EditIcon = (
+
+);
+const DeleteIcon = (
+
+);
+const UpIcon = (
+
+);
+const DownIcon = (
+
+);
+const RemoveIcon = (
+
+);
+
export default function OffersTemplates() {
const { hasPermission } = useAuth();
const [activeTab, setActiveTab] = useState<"items" | "scopes">("items");
if (!hasPermission("settings.templates")) return ;
+ const tabs: TabDef[] = [
+ { value: "items", label: "Šablony položek" },
+ { value: "scopes", label: "Šablony rozsahu" },
+ ];
+
return (
-
+
-
-
Šablony
-
- Šablony položek a rozsahu projektu
-
-
+
-
-
-
-
+
+ setActiveTab(v as "items" | "scopes")}
+ tabs={tabs}
+ />
+
- {activeTab === "items" ? : }
-
+
+
+
+
+
+
+
);
}
@@ -169,183 +270,158 @@ function ItemTemplatesTab() {
};
if (isPending) {
- return (
-
- );
+ return ;
}
- return (
- <>
-
-
-
- Šablony položek ({templates.length})
-
-
-
-
- {templates.length === 0 ? (
-
-
Zatím žádné šablony položek.
-
- ) : (
-
-
-
-
- | Název |
- Popis |
- Cena |
- Kategorie |
- Akce |
-
-
-
- {templates.map((t) => (
-
- | {t.name} |
-
- {t.description || "—"}
- |
- {Number(t.default_price).toFixed(2)} |
-
- {t.category || "—"}
- |
-
-
-
-
-
- |
-
- ))}
-
-
-
- )}
-
-
+ {EditIcon}
+
+ setDeleteConfirm({ show: true, template: t })}
+ aria-label="Smazat"
+ title="Smazat"
+ >
+ {DeleteIcon}
+
+
+ ),
+ },
+ ];
+
+ return (
+
+
+
+ Šablony položek ({templates.length})
+
+
+
+
+
+
+ columns={columns}
+ rows={templates}
+ rowKey={(t) => t.id}
+ empty={}
+ />
+
{/* Item Template Modal */}
- setShowModal(false)}
onSubmit={handleSubmit}
title={editingTemplate ? "Upravit šablonu" : "Nová šablona položky"}
- submitLabel={editingTemplate ? "Uložit" : "Vytvořit"}
+ submitText={editingTemplate ? "Uložit" : "Vytvořit"}
loading={saving}
+ maxWidth="md"
>
-
-
+
+
+
+ setForm((p) => ({ ...p, category: e.target.value }))
+ }
+ />
+
+
+
- setDeleteConfirm({ show: false, template: null })}
onConfirm={handleDelete}
@@ -353,10 +429,10 @@ function ItemTemplatesTab() {
message={`Opravdu chcete smazat šablonu "${deleteConfirm.template?.name}"?`}
confirmText="Smazat"
cancelText="Zrušit"
- type="danger"
+ confirmVariant="danger"
loading={deleting}
/>
- >
+
);
}
@@ -533,268 +609,256 @@ function ScopeTemplatesTab() {
}
};
- return (
- <>
-
-
-
- Šablony rozsahu ({templates.length})
-
- ;
+ }
+
+ const columns: DataColumn[] = [
+ {
+ key: "name",
+ header: "Název",
+ width: "80%",
+ bold: true,
+ render: (t) => t.name,
+ },
+ {
+ key: "actions",
+ header: "Akce",
+ width: "20%",
+ align: "right",
+ render: (t) => (
+
+ openEdit(t)}
+ aria-label="Upravit"
+ title="Upravit"
>
-
- Přidat
-
-
-
- {templates.length === 0 ? (
-
-
Zatím žádné šablony rozsahu.
-
- ) : (
-
-
-
-
- | Název |
- Akce |
-
-
-
- {templates.map((t) => (
-
- | {t.name} |
-
-
-
-
-
- |
-
- ))}
-
-
-
- )}
-
-
+ {EditIcon}
+
+ setDeleteConfirm({ show: true, template: t })}
+ aria-label="Smazat"
+ title="Smazat"
+ >
+ {DeleteIcon}
+
+
+ ),
+ },
+ ];
+
+ return (
+
+
+
+ Šablony rozsahu ({templates.length})
+
+
+
+
+
+
+ columns={columns}
+ rows={templates}
+ rowKey={(t) => t.id}
+ empty={}
+ />
+
{/* Scope Template Modal (large) */}
- setShowModal(false)}
onSubmit={handleSubmit}
title={
editingTemplate ? "Upravit šablonu rozsahu" : "Nová šablona rozsahu"
}
- submitLabel={editingTemplate ? "Uložit" : "Vytvořit"}
- size="lg"
+ submitText={editingTemplate ? "Uložit" : "Vytvořit"}
+ maxWidth="lg"
loading={saving}
>
-
-
- setForm((p) => ({ ...p, name: e.target.value }))}
- className="admin-form-input"
- />
-
+
+ setForm((p) => ({ ...p, name: e.target.value }))}
+ />
+
-
-
-
- {form.sections.map((section, index) => (
-
-
-
{index + 1}.
-
- {section.title ||
- section.title_cz ||
- `Sekce ${index + 1}`}
-
-
-
-
- {form.sections.length > 1 && (
-
- )}
-
-
-
-
- ))}
-
-
-
-
-
-
-
+
+
+ {index + 1}.
+
+
+ {section.title || section.title_cz || `Sekce ${index + 1}`}
+
+ moveSection(index, -1)}
+ disabled={index === 0}
+ title="Posunout nahoru"
+ aria-label="Posunout nahoru"
+ >
+ {UpIcon}
+
+ moveSection(index, 1)}
+ disabled={index === form.sections.length - 1}
+ title="Posunout dolů"
+ aria-label="Posunout dolů"
+ >
+ {DownIcon}
+
+ {form.sections.length > 1 && (
+ removeSection(index)}
+ title="Odebrat"
+ aria-label="Odebrat"
+ >
+ {RemoveIcon}
+
+ )}
+
-
+
+
+
+
+ Název sekce
+
+
+
+ updateSection(index, "title", e.target.value)
+ }
+ placeholder="Název sekce (anglicky)"
+ />
+
+
+
+
+
+ Název sekce
+
+
+
+ updateSection(index, "title_cz", e.target.value)
+ }
+ placeholder="Název sekce (česky)"
+ />
+
+
+
+
+ updateSection(index, "content", val)}
+ placeholder="Obsah sekce..."
+ minHeight="150px"
+ />
+
+
+ ))}
+
+
+
+
+
+
+
+ setDeleteConfirm({ show: false, template: null })}
onConfirm={handleDelete}
@@ -802,9 +866,9 @@ function ScopeTemplatesTab() {
message={`Opravdu chcete smazat šablonu "${deleteConfirm.template?.name}"?`}
confirmText="Smazat"
cancelText="Zrušit"
- type="danger"
+ confirmVariant="danger"
loading={deleting}
/>
- >
+
);
}