v1.6.5: fix attendance unique constraint, schema sync, seed script

- Change attendance idx_attendance_user_date from unique to index (allow multiple shifts per day)
- Reset migrations to single baseline init migration
- Add seed script with admin user (admin/admin)
- Update CLAUDE.md with migration workflow documentation
- Various frontend fixes (queries, pages, hooks)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-05-17 16:21:34 +02:00
parent 1db5060c42
commit cd6d3daf43
75 changed files with 1748 additions and 1169 deletions

View File

@@ -7,7 +7,11 @@ import ConfirmModal from "../components/ConfirmModal";
import FormField from "../components/FormField";
import Forbidden from "../components/Forbidden";
import useModalLock from "../hooks/useModalLock";
import { offerCustomersOptions } from "../lib/queries/offers";
import {
offerCustomersOptions,
type Customer,
type CustomField,
} from "../lib/queries/offers";
import { Skeleton } from "boneyard-js/react";
import OffersCustomersFixture from "../fixtures/OffersCustomersFixture";
@@ -31,27 +35,6 @@ const CUSTOMER_FIELD_LABELS: Record<string, string> = {
vat_id: "DIČ",
};
interface Customer {
id: number;
name: string;
street?: string;
city?: string;
postal_code?: string;
country?: string;
company_id?: string;
vat_id?: string;
quotation_count: number;
custom_fields?: CustomField[];
customer_field_order?: string[];
}
interface CustomField {
name: string;
value: string;
showLabel: boolean;
_key?: string;
}
interface CustomerForm {
name: string;
street: string;
@@ -66,9 +49,7 @@ export default function OffersCustomers() {
const alert = useAlert();
const { hasPermission } = useAuth();
const queryClient = useQueryClient();
const { data: customers = [], isPending } = useQuery(
offerCustomersOptions(),
) as { data: Customer[]; isPending: boolean };
const { data: customers = [], isPending } = useQuery(offerCustomersOptions());
const [search, setSearch] = useState("");
const [showModal, setShowModal] = useState(false);
@@ -232,6 +213,7 @@ export default function OffersCustomers() {
: "Zákazník byl vytvořen"),
);
queryClient.invalidateQueries({ queryKey: ["offer-customers"] });
queryClient.invalidateQueries({ queryKey: ["offers"] });
} else {
alert.error(result.error || "Nepodařilo se uložit zákazníka");
}
@@ -260,6 +242,7 @@ export default function OffersCustomers() {
setDeleteConfirm({ show: false, customer: null });
alert.success(result.message || "Zákazník byl smazán");
queryClient.invalidateQueries({ queryKey: ["offer-customers"] });
queryClient.invalidateQueries({ queryKey: ["offers"] });
} else {
alert.error(result.error || "Nepodařilo se smazat zákazníka");
}
@@ -270,7 +253,7 @@ export default function OffersCustomers() {
}
};
if (!hasPermission("offers.view")) return <Forbidden />;
if (!hasPermission("customers.view")) return <Forbidden />;
const filteredCustomers = search
? customers.filter(
@@ -300,7 +283,7 @@ export default function OffersCustomers() {
<h1 className="admin-page-title">Zákazníci</h1>
<p className="admin-page-subtitle">Správa zákazníků pro nabídky</p>
</div>
{hasPermission("offers.create") && (
{hasPermission("customers.create") && (
<button
onClick={openCreateModal}
className="admin-btn admin-btn-primary"
@@ -345,7 +328,7 @@ export default function OffersCustomers() {
? "Žádní zákazníci odpovídající hledání."
: "Zatím nejsou žádní zákazníci."}
</p>
{!search && hasPermission("offers.create") && (
{!search && hasPermission("customers.create") && (
<button
onClick={openCreateModal}
className="admin-btn admin-btn-primary"
@@ -398,7 +381,7 @@ export default function OffersCustomers() {
</td>
<td>
<div className="admin-table-actions">
{hasPermission("offers.edit") && (
{hasPermission("customers.edit") && (
<button
onClick={() => openEditModal(customer)}
className="admin-btn-icon"
@@ -418,7 +401,7 @@ export default function OffersCustomers() {
</svg>
</button>
)}
{hasPermission("offers.delete") && (
{hasPermission("customers.delete") && (
<button
onClick={() =>
setDeleteConfirm({ show: true, customer })