feat(documents): shared CustomFieldsPrintPicker + detail query types
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
60
src/admin/components/document/CustomFieldsPrintPicker.tsx
Normal file
60
src/admin/components/document/CustomFieldsPrintPicker.tsx
Normal file
@@ -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_<i>` 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 (
|
||||
<Box>
|
||||
<Typography variant="subtitle2" sx={{ mb: 0.5 }}>
|
||||
Vlastní pole na PDF
|
||||
</Typography>
|
||||
{fields.map((f, idx) => {
|
||||
if (!(f.value || "").trim()) return null;
|
||||
const label = (f.name || "").trim() ? `${f.name}: ${f.value}` : f.value;
|
||||
return (
|
||||
<FormControlLabel
|
||||
key={idx}
|
||||
control={
|
||||
<Checkbox
|
||||
size="small"
|
||||
checked={selected.includes(idx)}
|
||||
disabled={disabled}
|
||||
onChange={(e) => toggle(idx, e.target.checked)}
|
||||
/>
|
||||
}
|
||||
label={<Typography variant="body2">{label}</Typography>}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -60,6 +60,7 @@ export interface IssuedOrderDetail extends IssuedOrder {
|
||||
sections: IssuedOrderSection[];
|
||||
supplier: Record<string, unknown> | 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;
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
Reference in New Issue
Block a user