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

@@ -5,8 +5,11 @@ import { useAuth } from "../context/AuthContext";
import Forbidden from "../components/Forbidden";
import FormField from "../components/FormField";
import ConfirmModal from "../components/ConfirmModal";
import { companySettingsOptions } from "../lib/queries/settings";
import { bankAccountsOptions } from "../lib/queries/common";
import {
companySettingsOptions,
type CompanySettingsData,
} from "../lib/queries/settings";
import { bankAccountsOptions, type BankAccount } from "../lib/queries/common";
import { motion } from "framer-motion";
import apiFetch from "../utils/api";
@@ -47,17 +50,6 @@ interface CompanyForm {
vat_id: string;
}
interface BankAccount {
id: number;
account_name: string;
bank_name: string;
account_number: string;
iban: string;
bic: string;
currency: string;
is_default: boolean;
}
interface BankForm {
account_name: string;
bank_name: string;
@@ -196,49 +188,49 @@ export default function CompanySettings({
);
const bankAccountsList: BankAccount[] = Array.isArray(bankAccountsData)
? (bankAccountsData as unknown as BankAccount[])
? bankAccountsData
: [];
// Populate form state when settings data arrives
useEffect(() => {
if (!settingsData) return;
const d = settingsData as Record<string, unknown>;
setForm({
company_name: (d.company_name as string) || "",
street: (d.street as string) || "",
city: (d.city as string) || "",
postal_code: (d.postal_code as string) || "",
country: (d.country as string) || "",
company_id: (d.company_id as string) || "",
vat_id: (d.vat_id as string) || "",
company_name: settingsData.company_name || "",
street: settingsData.street || "",
city: settingsData.city || "",
postal_code: settingsData.postal_code || "",
country: settingsData.country || "",
company_id: settingsData.company_id || "",
vat_id: settingsData.vat_id || "",
});
const cf: CustomField[] =
Array.isArray(d.custom_fields) && d.custom_fields.length > 0
? (d.custom_fields as CustomField[]).map((f, i) => ({
Array.isArray(settingsData.custom_fields) &&
settingsData.custom_fields.length > 0
? settingsData.custom_fields.map((f, i) => ({
...f,
showLabel: f.showLabel !== false,
_key: f._key || `cf-${Date.now()}-${i}`,
_key: (f as CustomField)._key || `cf-${Date.now()}-${i}`,
}))
: [];
setCustomFields(cf);
if (
Array.isArray(d.supplier_field_order) &&
d.supplier_field_order.length > 0
Array.isArray(settingsData.supplier_field_order) &&
settingsData.supplier_field_order.length > 0
) {
setFieldOrder(d.supplier_field_order as string[]);
setFieldOrder(settingsData.supplier_field_order);
} else {
setFieldOrder([...DEFAULT_FIELD_ORDER]);
}
if (
Array.isArray(d.available_currencies) &&
d.available_currencies.length > 0
Array.isArray(settingsData.available_currencies) &&
settingsData.available_currencies.length > 0
) {
setAvailableCurrencies(d.available_currencies as string[]);
setAvailableCurrencies(settingsData.available_currencies);
}
if (d.has_logo) {
if (settingsData.has_logo) {
fetchLogo("light");
}
if (d.has_logo_dark) {
if (settingsData.has_logo_dark) {
fetchLogo("dark");
}
}, [settingsData, fetchLogo]);
@@ -277,6 +269,7 @@ export default function CompanySettings({
alert.success(result.message);
resetBankForm();
queryClient.invalidateQueries({ queryKey: ["bank-accounts"] });
queryClient.invalidateQueries({ queryKey: ["invoices"] });
} else {
alert.error(result.error || "Chyba při ukládání");
}
@@ -305,6 +298,7 @@ export default function CompanySettings({
alert.success(result.message);
if (editingBank === bankDeleteConfirm.id) resetBankForm();
queryClient.invalidateQueries({ queryKey: ["bank-accounts"] });
queryClient.invalidateQueries({ queryKey: ["invoices"] });
} else {
alert.error(result.error || "Chyba při mazání");
}
@@ -355,6 +349,9 @@ export default function CompanySettings({
if (result.success) {
alert.success(result.message || "Nastavení bylo uloženo");
queryClient.invalidateQueries({ queryKey: ["company-settings"] });
queryClient.invalidateQueries({ queryKey: ["offers"] });
queryClient.invalidateQueries({ queryKey: ["orders"] });
queryClient.invalidateQueries({ queryKey: ["invoices"] });
} else {
alert.error(result.error || "Nepodařilo se uložit nastavení");
}
@@ -390,6 +387,9 @@ export default function CompanySettings({
if (result.success) {
alert.success(result.message || "Logo bylo nahráno");
queryClient.invalidateQueries({ queryKey: ["company-settings"] });
queryClient.invalidateQueries({ queryKey: ["offers"] });
queryClient.invalidateQueries({ queryKey: ["orders"] });
queryClient.invalidateQueries({ queryKey: ["invoices"] });
fetchLogo(variant);
} else {
alert.error(result.error || "Nepodařilo se nahrát logo");
@@ -406,7 +406,7 @@ export default function CompanySettings({
setForm((prev) => ({ ...prev, [field]: value }));
};
if (!embedded && !hasPermission("settings.manage")) return <Forbidden />;
if (!embedded && !hasPermission("settings.company")) return <Forbidden />;
if (settingsLoading) {
return (