From 44cfea22ca495a711201f5fc1e4221e8112b6bb6 Mon Sep 17 00:00:00 2001 From: BOHA Date: Tue, 16 Jun 2026 20:17:23 +0200 Subject: [PATCH] feat(documents): shared CustomFieldsPrintPicker + detail query types Co-Authored-By: Claude Opus 4.8 (1M context) --- .../document/CustomFieldsPrintPicker.tsx | 60 +++++++++++++++++++ src/admin/lib/queries/invoices.ts | 1 + src/admin/lib/queries/issued-orders.ts | 1 + src/admin/lib/queries/offers.ts | 1 + 4 files changed, 63 insertions(+) create mode 100644 src/admin/components/document/CustomFieldsPrintPicker.tsx diff --git a/src/admin/components/document/CustomFieldsPrintPicker.tsx b/src/admin/components/document/CustomFieldsPrintPicker.tsx new file mode 100644 index 0000000..fef4aeb --- /dev/null +++ b/src/admin/components/document/CustomFieldsPrintPicker.tsx @@ -0,0 +1,60 @@ +import { FormControlLabel, Checkbox, Box, Typography } from "@mui/material"; +import type { CompanySettingsCustomField } from "../../lib/queries/settings"; + +interface Props { + /** Company custom field definitions, in positional order (index = print key). */ + fields: CompanySettingsCustomField[]; + /** Currently selected positional indices. */ + selected: number[]; + /** Read-only (document not editable / locked by another user). */ + disabled?: boolean; + onChange: (next: number[]) => void; +} + +/** + * Per-document picker: choose which COMPANY custom fields print on this + * document's PDF. Selection is positional (matches the PDF `custom_` keys). + * Renders nothing when the company has defined no printable custom fields. + */ +export default function CustomFieldsPrintPicker({ + fields, + selected, + disabled = false, + onChange, +}: Props) { + const printable = fields.filter((f) => (f.value || "").trim()); + if (printable.length === 0) return null; + + const toggle = (idx: number, checked: boolean) => { + const set = new Set(selected); + if (checked) set.add(idx); + else set.delete(idx); + onChange([...set].sort((a, b) => a - b)); + }; + + return ( + + + VlastnĂ­ pole na PDF + + {fields.map((f, idx) => { + if (!(f.value || "").trim()) return null; + const label = (f.name || "").trim() ? `${f.name}: ${f.value}` : f.value; + return ( + toggle(idx, e.target.checked)} + /> + } + label={{label}} + /> + ); + })} + + ); +} diff --git a/src/admin/lib/queries/invoices.ts b/src/admin/lib/queries/invoices.ts index 1591b7d..bf448fc 100644 --- a/src/admin/lib/queries/invoices.ts +++ b/src/admin/lib/queries/invoices.ts @@ -85,6 +85,7 @@ export interface InvoiceDetail { bank_account_id?: number | null; items?: InvoiceItem[]; sections?: InvoiceSection[]; + selected_custom_fields?: number[]; subtotal: number; vat_amount: number; total: number; diff --git a/src/admin/lib/queries/issued-orders.ts b/src/admin/lib/queries/issued-orders.ts index 586c197..37f7e48 100644 --- a/src/admin/lib/queries/issued-orders.ts +++ b/src/admin/lib/queries/issued-orders.ts @@ -60,6 +60,7 @@ export interface IssuedOrderDetail extends IssuedOrder { sections: IssuedOrderSection[]; supplier: Record | null; valid_transitions: string[]; + selected_custom_fields: number[]; /** Fresh edit lock held by ANOTHER user (same shape as offers), else null. */ locked_by: { user_id: number; diff --git a/src/admin/lib/queries/offers.ts b/src/admin/lib/queries/offers.ts index 2934602..11b15f0 100644 --- a/src/admin/lib/queries/offers.ts +++ b/src/admin/lib/queries/offers.ts @@ -184,6 +184,7 @@ export interface OfferDetailData { locked_by: OfferLockInfo | null; /** Legal next statuses, computed server-side (same contract as issued orders). */ valid_transitions: string[]; + selected_custom_fields: number[]; } export const offerDetailOptions = (id: string | undefined) =>