feat(documents): per-document custom-field print picker on offer/issued-order/invoice detail
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ import RichEditor from "../components/RichEditor";
|
||||
import SectionsEditor, {
|
||||
type DocumentSection,
|
||||
} from "../components/document/SectionsEditor";
|
||||
import CustomFieldsPrintPicker from "../components/document/CustomFieldsPrintPicker";
|
||||
import {
|
||||
DndContext,
|
||||
closestCenter,
|
||||
@@ -611,6 +612,9 @@ export default function InvoiceDetail() {
|
||||
// Rich-text CZ/EN "Obsah" sections (printed after the items on the PDF —
|
||||
// shared editor with offers/issued orders).
|
||||
const [sections, setSections] = useState<DocumentSection[]>([]);
|
||||
const [selectedCustomFields, setSelectedCustomFields] = useState<number[]>(
|
||||
[],
|
||||
);
|
||||
const [items, setItems] = useState<InvoiceItem[]>(() => [
|
||||
{
|
||||
_key: "inv-1",
|
||||
@@ -821,11 +825,15 @@ export default function InvoiceDetail() {
|
||||
: [];
|
||||
setSections(mappedSections);
|
||||
|
||||
const mappedCustomFields = inv.selected_custom_fields ?? [];
|
||||
setSelectedCustomFields(mappedCustomFields);
|
||||
|
||||
// Capture initial snapshot for dirty-checking
|
||||
initialSnapshotRef.current = JSON.stringify({
|
||||
form: formData,
|
||||
items: mappedItems,
|
||||
sections: mappedSections,
|
||||
selectedCustomFields: mappedCustomFields,
|
||||
});
|
||||
|
||||
setDataReady(true);
|
||||
@@ -923,15 +931,21 @@ export default function InvoiceDetail() {
|
||||
// Edit mode: captured inside the sync effect from raw query data.
|
||||
// Create mode: captured on the first render after sync effects populate the form.
|
||||
if (dataReady && !initialSnapshotRef.current) {
|
||||
initialSnapshotRef.current = JSON.stringify({ form, items, sections });
|
||||
initialSnapshotRef.current = JSON.stringify({
|
||||
form,
|
||||
items,
|
||||
sections,
|
||||
selectedCustomFields,
|
||||
});
|
||||
}
|
||||
|
||||
const isDirty = useMemo(() => {
|
||||
if (!initialSnapshotRef.current) return false;
|
||||
return (
|
||||
JSON.stringify({ form, items, sections }) !== initialSnapshotRef.current
|
||||
JSON.stringify({ form, items, sections, selectedCustomFields }) !==
|
||||
initialSnapshotRef.current
|
||||
);
|
||||
}, [form, items, sections]);
|
||||
}, [form, items, sections, selectedCustomFields]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDirty) return;
|
||||
@@ -1095,6 +1109,7 @@ export default function InvoiceDetail() {
|
||||
content: s.content,
|
||||
position: i,
|
||||
})),
|
||||
selected_custom_fields: selectedCustomFields,
|
||||
};
|
||||
// Only set status when a target is given (create-as-draft / create-as-live
|
||||
// / finalize). Editing a live invoice sends no status — the backend keeps
|
||||
@@ -1107,7 +1122,12 @@ export default function InvoiceDetail() {
|
||||
|
||||
const data = await saveMutation.mutateAsync(payload);
|
||||
alert.success(isEdit ? "Faktura byla uložena" : "Faktura byla vytvořena");
|
||||
initialSnapshotRef.current = JSON.stringify({ form, items, sections });
|
||||
initialSnapshotRef.current = JSON.stringify({
|
||||
form,
|
||||
items,
|
||||
sections,
|
||||
selectedCustomFields,
|
||||
});
|
||||
if (!isEdit) {
|
||||
navigate(`/invoices/${data.invoice_id}`);
|
||||
}
|
||||
@@ -2209,6 +2229,15 @@ export default function InvoiceDetail() {
|
||||
]}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<CustomFieldsPrintPicker
|
||||
fields={companySettings?.custom_fields ?? []}
|
||||
selected={selectedCustomFields}
|
||||
disabled={false}
|
||||
onChange={setSelectedCustomFields}
|
||||
/>
|
||||
</Box>
|
||||
</Card>
|
||||
|
||||
{/* Items */}
|
||||
|
||||
Reference in New Issue
Block a user