@spaceinvoices/react-ui 0.4.8 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/cli/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/common/autocomplete.tsx +69 -6
  4. package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +124 -285
  5. package/src/components/advance-invoices/list/list-table.tsx +10 -3
  6. package/src/components/advance-invoices/list/locales/de.ts +2 -0
  7. package/src/components/advance-invoices/list/locales/en.ts +1 -0
  8. package/src/components/advance-invoices/list/locales/es.ts +1 -0
  9. package/src/components/advance-invoices/list/locales/fr.ts +1 -0
  10. package/src/components/advance-invoices/list/locales/hr.ts +1 -0
  11. package/src/components/advance-invoices/list/locales/it.ts +1 -0
  12. package/src/components/advance-invoices/list/locales/nl.ts +1 -0
  13. package/src/components/advance-invoices/list/locales/pl.ts +1 -0
  14. package/src/components/advance-invoices/list/locales/pt.ts +1 -0
  15. package/src/components/advance-invoices/list/locales/sl.ts +1 -0
  16. package/src/components/advance-invoices/list/use-advance-invoice-download.ts +1 -12
  17. package/src/components/credit-notes/create/create-credit-note-form.tsx +116 -238
  18. package/src/components/credit-notes/list/list-table.tsx +6 -3
  19. package/src/components/credit-notes/list/use-credit-note-download.ts +1 -12
  20. package/src/components/customers/customer-autocomplete.tsx +64 -11
  21. package/src/components/customers/customer-list-table/customer-list-table.tsx +3 -2
  22. package/src/components/dashboard/collection-rate-card/collection-rate-card.tsx +9 -1
  23. package/src/components/dashboard/collection-rate-card/locales/bg.ts +3 -0
  24. package/src/components/dashboard/collection-rate-card/locales/cs.ts +3 -0
  25. package/src/components/dashboard/collection-rate-card/locales/et.ts +3 -0
  26. package/src/components/dashboard/collection-rate-card/locales/fi.ts +3 -0
  27. package/src/components/dashboard/collection-rate-card/locales/is.ts +3 -0
  28. package/src/components/dashboard/collection-rate-card/locales/nb.ts +3 -0
  29. package/src/components/dashboard/collection-rate-card/locales/sk.ts +3 -0
  30. package/src/components/dashboard/collection-rate-card/locales/sv.ts +3 -0
  31. package/src/components/dashboard/invoice-status-chart/invoice-status-chart.tsx +10 -2
  32. package/src/components/dashboard/invoice-status-chart/locales/bg.ts +10 -0
  33. package/src/components/dashboard/invoice-status-chart/locales/cs.ts +10 -0
  34. package/src/components/dashboard/invoice-status-chart/locales/de.ts +1 -0
  35. package/src/components/dashboard/invoice-status-chart/locales/es.ts +1 -0
  36. package/src/components/dashboard/invoice-status-chart/locales/et.ts +10 -0
  37. package/src/components/dashboard/invoice-status-chart/locales/fi.ts +10 -0
  38. package/src/components/dashboard/invoice-status-chart/locales/fr.ts +1 -0
  39. package/src/components/dashboard/invoice-status-chart/locales/hr.ts +1 -0
  40. package/src/components/dashboard/invoice-status-chart/locales/is.ts +10 -0
  41. package/src/components/dashboard/invoice-status-chart/locales/it.ts +1 -0
  42. package/src/components/dashboard/invoice-status-chart/locales/nb.ts +10 -0
  43. package/src/components/dashboard/invoice-status-chart/locales/nl.ts +1 -0
  44. package/src/components/dashboard/invoice-status-chart/locales/pl.ts +1 -0
  45. package/src/components/dashboard/invoice-status-chart/locales/pt.ts +1 -0
  46. package/src/components/dashboard/invoice-status-chart/locales/sk.ts +10 -0
  47. package/src/components/dashboard/invoice-status-chart/locales/sl.ts +1 -0
  48. package/src/components/dashboard/invoice-status-chart/locales/sv.ts +10 -0
  49. package/src/components/dashboard/payment-methods-chart/locales/bg.ts +12 -0
  50. package/src/components/dashboard/payment-methods-chart/locales/cs.ts +12 -0
  51. package/src/components/dashboard/payment-methods-chart/locales/et.ts +12 -0
  52. package/src/components/dashboard/payment-methods-chart/locales/fi.ts +12 -0
  53. package/src/components/dashboard/payment-methods-chart/locales/is.ts +12 -0
  54. package/src/components/dashboard/payment-methods-chart/locales/nb.ts +12 -0
  55. package/src/components/dashboard/payment-methods-chart/locales/sk.ts +12 -0
  56. package/src/components/dashboard/payment-methods-chart/locales/sv.ts +12 -0
  57. package/src/components/dashboard/payment-methods-chart/payment-methods-chart.tsx +9 -1
  58. package/src/components/dashboard/payment-trend-chart/locales/bg.ts +6 -0
  59. package/src/components/dashboard/payment-trend-chart/locales/cs.ts +6 -0
  60. package/src/components/dashboard/payment-trend-chart/locales/de.ts +1 -0
  61. package/src/components/dashboard/payment-trend-chart/locales/es.ts +1 -0
  62. package/src/components/dashboard/payment-trend-chart/locales/et.ts +6 -0
  63. package/src/components/dashboard/payment-trend-chart/locales/fi.ts +6 -0
  64. package/src/components/dashboard/payment-trend-chart/locales/fr.ts +1 -0
  65. package/src/components/dashboard/payment-trend-chart/locales/hr.ts +1 -0
  66. package/src/components/dashboard/payment-trend-chart/locales/is.ts +6 -0
  67. package/src/components/dashboard/payment-trend-chart/locales/it.ts +1 -0
  68. package/src/components/dashboard/payment-trend-chart/locales/nb.ts +6 -0
  69. package/src/components/dashboard/payment-trend-chart/locales/nl.ts +1 -0
  70. package/src/components/dashboard/payment-trend-chart/locales/pl.ts +1 -0
  71. package/src/components/dashboard/payment-trend-chart/locales/pt.ts +1 -0
  72. package/src/components/dashboard/payment-trend-chart/locales/sk.ts +6 -0
  73. package/src/components/dashboard/payment-trend-chart/locales/sl.ts +1 -0
  74. package/src/components/dashboard/payment-trend-chart/locales/sv.ts +6 -0
  75. package/src/components/dashboard/payment-trend-chart/payment-trend-chart.tsx +15 -8
  76. package/src/components/dashboard/revenue-trend-chart/locales/bg.ts +6 -0
  77. package/src/components/dashboard/revenue-trend-chart/locales/cs.ts +6 -0
  78. package/src/components/dashboard/revenue-trend-chart/locales/de.ts +1 -0
  79. package/src/components/dashboard/revenue-trend-chart/locales/es.ts +1 -0
  80. package/src/components/dashboard/revenue-trend-chart/locales/et.ts +6 -0
  81. package/src/components/dashboard/revenue-trend-chart/locales/fi.ts +6 -0
  82. package/src/components/dashboard/revenue-trend-chart/locales/fr.ts +1 -0
  83. package/src/components/dashboard/revenue-trend-chart/locales/hr.ts +1 -0
  84. package/src/components/dashboard/revenue-trend-chart/locales/is.ts +6 -0
  85. package/src/components/dashboard/revenue-trend-chart/locales/it.ts +1 -0
  86. package/src/components/dashboard/revenue-trend-chart/locales/nb.ts +6 -0
  87. package/src/components/dashboard/revenue-trend-chart/locales/nl.ts +1 -0
  88. package/src/components/dashboard/revenue-trend-chart/locales/pl.ts +1 -0
  89. package/src/components/dashboard/revenue-trend-chart/locales/pt.ts +1 -0
  90. package/src/components/dashboard/revenue-trend-chart/locales/sk.ts +6 -0
  91. package/src/components/dashboard/revenue-trend-chart/locales/sl.ts +1 -0
  92. package/src/components/dashboard/revenue-trend-chart/locales/sv.ts +6 -0
  93. package/src/components/dashboard/revenue-trend-chart/revenue-trend-chart.tsx +15 -8
  94. package/src/components/dashboard/tax-collected-card/locales.ts +110 -0
  95. package/src/components/dashboard/tax-collected-card/tax-collected-card.tsx +8 -2
  96. package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +4 -4
  97. package/src/components/dashboard/top-customers-chart/locales/bg.ts +7 -0
  98. package/src/components/dashboard/top-customers-chart/locales/cs.ts +7 -0
  99. package/src/components/dashboard/top-customers-chart/locales/de.ts +2 -0
  100. package/src/components/dashboard/top-customers-chart/locales/es.ts +2 -0
  101. package/src/components/dashboard/top-customers-chart/locales/et.ts +7 -0
  102. package/src/components/dashboard/top-customers-chart/locales/fi.ts +7 -0
  103. package/src/components/dashboard/top-customers-chart/locales/fr.ts +2 -0
  104. package/src/components/dashboard/top-customers-chart/locales/hr.ts +2 -0
  105. package/src/components/dashboard/top-customers-chart/locales/is.ts +7 -0
  106. package/src/components/dashboard/top-customers-chart/locales/it.ts +2 -0
  107. package/src/components/dashboard/top-customers-chart/locales/nb.ts +7 -0
  108. package/src/components/dashboard/top-customers-chart/locales/nl.ts +2 -0
  109. package/src/components/dashboard/top-customers-chart/locales/pl.ts +2 -0
  110. package/src/components/dashboard/top-customers-chart/locales/pt.ts +2 -0
  111. package/src/components/dashboard/top-customers-chart/locales/sk.ts +7 -0
  112. package/src/components/dashboard/top-customers-chart/locales/sl.ts +2 -0
  113. package/src/components/dashboard/top-customers-chart/locales/sv.ts +7 -0
  114. package/src/components/dashboard/top-customers-chart/top-customers-chart.tsx +23 -12
  115. package/src/components/delivery-notes/create/create-delivery-note-form.tsx +33 -20
  116. package/src/components/delivery-notes/list/list-table.tsx +22 -13
  117. package/src/components/delivery-notes/list/locales/de.ts +2 -0
  118. package/src/components/delivery-notes/list/locales/en.ts +1 -0
  119. package/src/components/delivery-notes/list/locales/es.ts +1 -0
  120. package/src/components/delivery-notes/list/locales/fr.ts +1 -0
  121. package/src/components/delivery-notes/list/locales/hr.ts +1 -0
  122. package/src/components/delivery-notes/list/locales/it.ts +1 -0
  123. package/src/components/delivery-notes/list/locales/nl.ts +1 -0
  124. package/src/components/delivery-notes/list/locales/pl.ts +1 -0
  125. package/src/components/delivery-notes/list/locales/pt.ts +1 -0
  126. package/src/components/delivery-notes/list/locales/sl.ts +1 -0
  127. package/src/components/delivery-notes/list/use-delivery-note-download.ts +1 -12
  128. package/src/components/documents/create/document-add-item-form.tsx +28 -16
  129. package/src/components/documents/create/document-add-item-tax-rate-field.tsx +12 -2
  130. package/src/components/documents/create/document-items-section.tsx +70 -39
  131. package/src/components/documents/create/document-recipient-section.tsx +10 -1
  132. package/src/components/documents/create/live-preview.tsx +113 -15
  133. package/src/components/documents/create/prepare-document-submission.ts +35 -16
  134. package/src/components/documents/create/use-document-customer-form.ts +14 -3
  135. package/src/components/documents/documents.hooks.ts +7 -2
  136. package/src/components/documents/shared/document-preview-display.tsx +136 -67
  137. package/src/components/documents/shared/scaled-document-preview.tsx +45 -5
  138. package/src/components/documents/view/document-actions-bar.tsx +284 -182
  139. package/src/components/documents/view/document-activities-list.tsx +3 -0
  140. package/src/components/documents/view/document-payments-list.tsx +3 -0
  141. package/src/components/documents/view/locales/de.ts +8 -0
  142. package/src/components/documents/view/locales/es.ts +8 -0
  143. package/src/components/documents/view/locales/fr.ts +8 -0
  144. package/src/components/documents/view/locales/hr.ts +8 -0
  145. package/src/components/documents/view/locales/it.ts +8 -0
  146. package/src/components/documents/view/locales/nl.ts +8 -0
  147. package/src/components/documents/view/locales/pl.ts +8 -0
  148. package/src/components/documents/view/locales/pt.ts +8 -0
  149. package/src/components/documents/view/locales/sl.ts +8 -0
  150. package/src/components/documents/view/use-document-download.ts +14 -25
  151. package/src/components/entities/create-entity-form.tsx +101 -16
  152. package/src/components/entities/fina-settings-form/fina-operator-required-dialog.tsx +3 -3
  153. package/src/components/entities/fina-settings-form/fina-settings-form.tsx +78 -124
  154. package/src/components/entities/fina-settings-form/sections/certificate-settings-section.tsx +8 -1
  155. package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +14 -2
  156. package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +7 -2
  157. package/src/components/entities/furs-settings-form/furs-settings-form.tsx +56 -130
  158. package/src/components/entities/furs-settings-form/sections/certificate-settings-section.tsx +8 -1
  159. package/src/components/entities/furs-settings-form/sections/enable-fiscalization-section.tsx +1 -0
  160. package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +15 -2
  161. package/src/components/entities/furs-settings-form/sections/premises-management-section.tsx +20 -3
  162. package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +38 -12
  163. package/src/components/entities/settings/eslog-settings-form.tsx +13 -1
  164. package/src/components/entities/settings/pdf-template-selector/demo-invoice-data.ts +3 -22
  165. package/src/components/entities/shared/fiscalization-step-flow.ts +77 -0
  166. package/src/components/entities/shared/fiscalization-step-tabs.tsx +71 -0
  167. package/src/components/estimates/create/create-estimate-form.tsx +34 -21
  168. package/src/components/estimates/list/list-table.tsx +23 -14
  169. package/src/components/estimates/list/locales/de.ts +2 -0
  170. package/src/components/estimates/list/locales/en.ts +1 -0
  171. package/src/components/estimates/list/locales/es.ts +1 -0
  172. package/src/components/estimates/list/locales/fr.ts +1 -0
  173. package/src/components/estimates/list/locales/hr.ts +1 -0
  174. package/src/components/estimates/list/locales/it.ts +1 -0
  175. package/src/components/estimates/list/locales/nl.ts +1 -0
  176. package/src/components/estimates/list/locales/pl.ts +1 -0
  177. package/src/components/estimates/list/locales/pt.ts +1 -0
  178. package/src/components/estimates/list/locales/sl.ts +1 -0
  179. package/src/components/estimates/list/use-estimate-download.ts +1 -12
  180. package/src/components/export/document-export-form.tsx +33 -7
  181. package/src/components/export/sales-per-item-export-form.tsx +23 -7
  182. package/src/components/invoices/create/create-invoice-form.tsx +295 -329
  183. package/src/components/invoices/create/prepare-invoice-submission.ts +0 -8
  184. package/src/components/invoices/list/list-table.tsx +7 -4
  185. package/src/components/invoices/list/use-invoice-download.ts +1 -11
  186. package/src/components/invoices/send-email-dialog/locales/de.ts +2 -0
  187. package/src/components/invoices/send-email-dialog/locales/es.ts +2 -0
  188. package/src/components/invoices/send-email-dialog/locales/fr.ts +2 -0
  189. package/src/components/invoices/send-email-dialog/locales/hr.ts +2 -0
  190. package/src/components/invoices/send-email-dialog/locales/it.ts +2 -0
  191. package/src/components/invoices/send-email-dialog/locales/nl.ts +2 -0
  192. package/src/components/invoices/send-email-dialog/locales/pl.ts +2 -0
  193. package/src/components/invoices/send-email-dialog/locales/pt.ts +2 -0
  194. package/src/components/invoices/send-email-dialog/locales/sl.ts +2 -0
  195. package/src/components/invoices/send-email-dialog/send-email-dialog.tsx +77 -8
  196. package/src/components/invoices/view/eslog-info-display.tsx +17 -1
  197. package/src/components/invoices/view/fiscalization-status-card.tsx +7 -3
  198. package/src/components/items/item-combobox.tsx +26 -6
  199. package/src/components/items/item-list-table/item-list-table.tsx +5 -2
  200. package/src/components/payments/list/list-table.tsx +14 -4
  201. package/src/components/recurring-invoices/list/list-table.tsx +7 -4
  202. package/src/components/request-logs/locales.ts +412 -0
  203. package/src/components/request-logs/request-log-detail.tsx +37 -21
  204. package/src/components/request-logs/request-log-list-table.tsx +57 -11
  205. package/src/components/table/data-table.tsx +5 -2
  206. package/src/components/table/date-cell.tsx +3 -1
  207. package/src/components/table/filter-bar.tsx +14 -2
  208. package/src/components/table/hooks/use-table-query.ts +1 -1
  209. package/src/components/table/locales.ts +1116 -0
  210. package/src/components/table/search-input.tsx +12 -3
  211. package/src/components/table/selection-toolbar.tsx +23 -6
  212. package/src/components/table/table-empty-state.tsx +43 -3
  213. package/src/components/table/table-no-results.tsx +3 -3
  214. package/src/components/table/table-pagination.tsx +4 -3
  215. package/src/components/table/types.ts +1 -0
  216. package/src/components/tax-reports/index.ts +1 -0
  217. package/src/components/tax-reports/kir-export-form.tsx +46 -8
  218. package/src/components/tax-reports/slovenia-tax-profile-step.tsx +191 -0
  219. package/src/components/tax-reports/slovenia-yearly-export-form.tsx +509 -0
  220. package/src/components/tax-reports/slovenia-yearly-review-step.tsx +253 -0
  221. package/src/components/tax-reports/slovenia-yearly-summary.tsx +19 -0
  222. package/src/components/taxes/tax-list-table/tax-list-table.tsx +3 -2
  223. package/src/components/ui/sticky-form-footer.tsx +7 -1
  224. package/src/components/webhook-logs/index.ts +6 -0
  225. package/src/components/webhook-logs/locales.ts +392 -0
  226. package/src/components/webhook-logs/webhook-delivery-detail.tsx +255 -0
  227. package/src/components/webhook-logs/webhook-delivery-list-table.tsx +278 -0
  228. package/src/components/wl-subscription/index.ts +1 -0
  229. package/src/components/wl-subscription/locked-feature.tsx +1 -0
  230. package/src/components/wl-subscription/paywall.tsx +193 -0
  231. package/src/components/wl-subscription/upgrade-modal.tsx +93 -29
  232. package/src/generate-schemas.ts +10 -5
  233. package/src/generated/schemas/customer.ts +2 -0
  234. package/src/generated/schemas/entity.ts +34 -0
  235. package/src/generated/schemas/me.ts +20 -1
  236. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +40 -34
  237. package/src/generated/schemas/rendercreditnotepreview_body.ts +42 -36
  238. package/src/generated/schemas/renderdeliverynotepreview_body.ts +23 -13
  239. package/src/generated/schemas/renderestimatepreview_body.ts +23 -13
  240. package/src/generated/schemas/renderinvoicepreview_body.ts +40 -34
  241. package/src/generated/schemas/sendemail_body.ts +44 -0
  242. package/src/generated/schemas/startpdfexport_body.ts +91 -1
  243. package/src/generated/schemas/webhook.ts +10 -0
  244. package/src/hooks/use-duplicate-document.ts +51 -13
  245. package/src/hooks/use-eslog-validation.ts +59 -0
  246. package/src/hooks/use-premise-selection.ts +186 -0
  247. package/src/lib/browser-cookies.ts +4 -4
  248. package/src/lib/date-fns-locale.ts +48 -0
  249. package/src/lib/fiscalization-options.ts +81 -0
  250. package/src/lib/locale.ts +38 -0
  251. package/src/lib/template-variables.tsx +1 -1
  252. package/src/lib/translation.ts +14 -3
  253. package/src/providers/entities-context.tsx +1 -0
  254. package/src/providers/entities-provider.tsx +102 -3
  255. package/src/providers/form-footer-context.tsx +37 -4
  256. package/src/providers/sdk-provider.tsx +7 -2
  257. package/src/providers/white-label-provider.tsx +4 -1
  258. package/src/providers/wl-subscription-provider.tsx +90 -3
@@ -5,6 +5,7 @@
5
5
  import { PlusIcon, SeparatorHorizontal } from "lucide-react";
6
6
  import type { MutableRefObject } from "react";
7
7
  import type { UseFormGetValues, UseFormSetValue, UseFormWatch } from "react-hook-form";
8
+ import { useFieldArray } from "react-hook-form";
8
9
  import { Button } from "@/ui/components/ui/button";
9
10
  import DocumentAddItemForm from "./document-add-item-form";
10
11
  import type { AnyControl } from "./form-types";
@@ -12,6 +13,14 @@ import type { AnyControl } from "./form-types";
12
13
  /** Map of item index to gross price mode */
13
14
  export type PriceModesMap = Record<number, boolean>;
14
15
 
16
+ function reindexPriceModes(priceModes: PriceModesMap, nextLength: number): PriceModesMap {
17
+ const reindexed: PriceModesMap = {};
18
+ for (let index = 0; index < nextLength; index += 1) {
19
+ reindexed[index] = priceModes[index] ?? false;
20
+ }
21
+ return reindexed;
22
+ }
23
+
15
24
  type DocumentItemsSectionProps = {
16
25
  control: AnyControl;
17
26
 
@@ -34,6 +43,8 @@ type DocumentItemsSectionProps = {
34
43
  priceModesRef?: MutableRefObject<PriceModesMap>;
35
44
  /** Initial price modes (from duplicated document) */
36
45
  initialPriceModes?: PriceModesMap;
46
+ /** Called when item ordering or price mode changes outside normal field edits. */
47
+ onItemsStateChange?: () => void;
37
48
  };
38
49
 
39
50
  export function DocumentItemsSection({
@@ -50,66 +61,85 @@ export function DocumentItemsSection({
50
61
  maxTaxesPerItem,
51
62
  priceModesRef,
52
63
  initialPriceModes = {},
64
+ onItemsStateChange,
53
65
  }: DocumentItemsSectionProps) {
66
+ const { fields, append, remove, move } = useFieldArray({
67
+ control: control as any,
68
+ name: "items",
69
+ });
70
+
71
+ const syncPriceModes = (updater: (current: PriceModesMap) => PriceModesMap) => {
72
+ if (!priceModesRef) return;
73
+ priceModesRef.current = updater(priceModesRef.current ?? {});
74
+ };
75
+
54
76
  const addItem = () => {
55
- const currentItems = getValues("items") || [];
56
- setValue("items", [
57
- ...currentItems,
58
- {
59
- name: "",
60
- description: "",
61
- quantity: 1,
62
- price: undefined,
63
- taxes: [],
64
- },
65
- ]);
77
+ append({
78
+ name: "",
79
+ description: "",
80
+ quantity: 1,
81
+ price: undefined,
82
+ taxes: [],
83
+ });
84
+ syncPriceModes((current) => ({ ...current, [fields.length]: false }));
85
+ onItemsStateChange?.();
66
86
  };
67
87
 
68
88
  const addSeparator = () => {
69
- const currentItems = getValues("items") || [];
70
- setValue("items", [
71
- ...currentItems,
72
- {
73
- type: "separator",
74
- name: "",
75
- description: "",
76
- },
77
- ]);
89
+ append({
90
+ type: "separator",
91
+ name: "",
92
+ description: "",
93
+ });
94
+ syncPriceModes((current) => ({ ...current, [fields.length]: false }));
95
+ onItemsStateChange?.();
78
96
  };
79
97
 
80
98
  const removeItem = (index: number) => {
81
- const currentItems = getValues("items") || [];
82
- setValue(
83
- "items",
84
- currentItems.filter((_: unknown, i: number) => i !== index),
85
- );
99
+ remove(index);
100
+ syncPriceModes((current) => {
101
+ const next: PriceModesMap = {};
102
+ let nextIndex = 0;
103
+ for (let currentIndex = 0; currentIndex < fields.length; currentIndex += 1) {
104
+ if (currentIndex === index) continue;
105
+ next[nextIndex] = current[currentIndex] ?? false;
106
+ nextIndex += 1;
107
+ }
108
+ return next;
109
+ });
110
+ onItemsStateChange?.();
86
111
  };
87
112
 
88
113
  const moveItemUp = (index: number) => {
89
114
  if (index === 0) return;
90
- const items = getValues("items");
91
- const newItems = [...items];
92
- [newItems[index], newItems[index - 1]] = [newItems[index - 1], newItems[index]];
93
- setValue("items", newItems);
115
+ move(index, index - 1);
116
+ syncPriceModes((current) => {
117
+ const next = reindexPriceModes(current, fields.length);
118
+ [next[index], next[index - 1]] = [next[index - 1], next[index]];
119
+ return next;
120
+ });
121
+ onItemsStateChange?.();
94
122
  };
95
123
 
96
124
  const moveItemDown = (index: number) => {
97
- const items = getValues("items");
98
- if (index === items.length - 1) return;
99
- const newItems = [...items];
100
- [newItems[index], newItems[index + 1]] = [newItems[index + 1], newItems[index]];
101
- setValue("items", newItems);
125
+ if (index === fields.length - 1) return;
126
+ move(index, index + 1);
127
+ syncPriceModes((current) => {
128
+ const next = reindexPriceModes(current, fields.length);
129
+ [next[index], next[index + 1]] = [next[index + 1], next[index]];
130
+ return next;
131
+ });
132
+ onItemsStateChange?.();
102
133
  };
103
134
 
104
- const items = watch("items") || [];
135
+ const items = watch("items") || fields;
105
136
 
106
137
  return (
107
138
  <div className="flex flex-col gap-4">
108
139
  <h2 className="font-bold text-xl">{t("Items")}</h2>
109
140
 
110
- {items.map((_: unknown, index: number) => (
111
- // biome-ignore lint/suspicious/noArrayIndexKey: index is stable
112
- <div key={index}>
141
+ {fields.map((field, index: number) => (
142
+ <div key={field.id}>
113
143
  <DocumentAddItemForm
114
144
  form={{ control, watch, setValue, getValues } as any}
115
145
  index={index}
@@ -127,11 +157,12 @@ export function DocumentItemsSection({
127
157
  taxesDisabled={taxesDisabled}
128
158
  taxesDisabledMessage={taxesDisabledMessage}
129
159
  maxTaxesPerItem={maxTaxesPerItem}
130
- initialIsGrossPrice={initialPriceModes[index] ?? false}
160
+ initialIsGrossPrice={priceModesRef?.current[index] ?? initialPriceModes[index] ?? false}
131
161
  onPriceModeChange={(isGross) => {
132
162
  if (priceModesRef) {
133
163
  priceModesRef.current[index] = isGross;
134
164
  }
165
+ onItemsStateChange?.();
135
166
  }}
136
167
  />
137
168
  </div>
@@ -47,6 +47,10 @@ export function DocumentRecipientSection({
47
47
  control: control as any,
48
48
  name: "customer.is_end_consumer" as any,
49
49
  });
50
+ const customerNameController = useController({
51
+ control: control as any,
52
+ name: "customer.name" as any,
53
+ });
50
54
 
51
55
  useEffect(() => {
52
56
  if (showCustomerForm && shouldFocusName) {
@@ -80,16 +84,21 @@ export function DocumentRecipientSection({
80
84
  <CustomerAutocomplete
81
85
  entityId={entityId}
82
86
  value={selectedCustomerId}
87
+ committedDisplayName={customerNameController.field.value ?? initialCustomerName}
83
88
  onValueChange={onCustomerSelect}
89
+ onCommitInlineName={(nextName) => customerNameController.field.onChange(nextName)}
84
90
  onClear={onCustomerClear}
85
91
  placeholder={t("Search or create customer...")}
86
92
  initialDisplayName={initialCustomerName}
93
+ inputTestId="document-customer-input"
94
+ inputRef={nameInputRef}
95
+ commitOnBlurMode={showCustomerForm ? "update-inline" : "create"}
87
96
  />
88
97
  </div>
89
98
 
90
99
  {showCustomerForm && (
91
100
  <>
92
- <FormInput control={control} name="customer.address" placeholder={t("Address")} label="" ref={nameInputRef} />
101
+ <FormInput control={control} name="customer.address" placeholder={t("Address")} label="" />
93
102
 
94
103
  <FormInput control={control} name="customer.address_2" placeholder={t("Address 2")} label="" />
95
104
 
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import type { CreateInvoiceRequest } from "@spaceinvoices/js-sdk";
4
+ import { Loader2 } from "lucide-react";
4
5
  import { useCallback, useEffect, useRef, useState } from "react";
5
6
  import { cn } from "@/ui/lib/utils";
6
7
  import { useEntities } from "@/ui/providers/entities-context";
@@ -11,6 +12,14 @@ import { useA4Scaling } from "../shared/use-a4-scaling";
11
12
  import type { DocumentTypes } from "../types";
12
13
  import { filterUnresolvedTaxes } from "./prepare-preview-data";
13
14
 
15
+ const LIVE_PREVIEW_TIMING_EVENT = "si:live-preview-timing";
16
+ const LIVE_PREVIEW_DEBOUNCE_MS = 300;
17
+
18
+ function emitLivePreviewDebug(detail: Record<string, unknown>) {
19
+ if (!import.meta.env.DEV || typeof window === "undefined") return;
20
+ window.dispatchEvent(new CustomEvent(LIVE_PREVIEW_TIMING_EVENT, { detail }));
21
+ }
22
+
14
23
  export type PdfTemplateId = "modern" | "classic" | "condensed" | "minimal" | "fashion";
15
24
 
16
25
  type LiveInvoicePreviewProps = {
@@ -30,6 +39,8 @@ type LiveInvoicePreviewProps = {
30
39
  documentTypeLabel?: string;
31
40
  /** Document type to determine which render endpoint to use */
32
41
  documentType?: DocumentTypes;
42
+ /** Skip debounce for the first non-empty preview request. */
43
+ eagerFirstPreview?: boolean;
33
44
  /** QR settings overrides for preview (before saving) */
34
45
  qrOverrides?: {
35
46
  upn_qr_enabled?: boolean;
@@ -45,7 +56,7 @@ type LiveInvoicePreviewProps = {
45
56
  * Uses debouncing to avoid excessive API calls and shows loading states.
46
57
  *
47
58
  * Features:
48
- * - Debounced API requests (500ms delay after user stops typing)
59
+ * - Debounced API requests (300ms delay after user stops typing)
49
60
  * - Loading skeleton while fetching preview
50
61
  * - Error handling with fallback display
51
62
  * - Fully styled HTML with scoped CSS (prevents style leakage)
@@ -60,16 +71,20 @@ export function LiveInvoicePreview({
60
71
  t: tProp,
61
72
  documentTypeLabel: _documentTypeLabel,
62
73
  documentType = "invoice",
74
+ eagerFirstPreview = false,
63
75
  qrOverrides,
64
76
  }: LiveInvoicePreviewProps) {
65
77
  const t = tProp ?? ((key: string) => key);
66
78
  const [previewHtml, setPreviewHtml] = useState<string>("");
67
79
  const [isLoading, setIsLoading] = useState(false);
80
+ const [isRefreshPending, setIsRefreshPending] = useState(false);
68
81
  const [error, setError] = useState<string | null>(null);
69
82
  const { activeEntity } = useEntities();
70
83
  const { sdk } = useSDK();
71
84
  const abortControllerRef = useRef<AbortController | null>(null);
72
85
  const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
86
+ const lastRequestKeyRef = useRef<string | null>(null);
87
+ const hasDispatchedFirstPreviewRef = useRef(false);
73
88
 
74
89
  const { containerRef, contentRef, scale: dynamicScale, contentHeight, A4_WIDTH_PX } = useA4Scaling(previewHtml);
75
90
  const scale = fixedScale ?? dynamicScale;
@@ -82,6 +97,14 @@ export function LiveInvoicePreview({
82
97
  // Don't fetch if no items exist (name can be empty for preview)
83
98
  if (!invoiceData.items || invoiceData.items.length === 0) {
84
99
  setPreviewHtml("");
100
+ lastRequestKeyRef.current = null;
101
+ setIsRefreshPending(false);
102
+ emitLivePreviewDebug({
103
+ stage: "skipped",
104
+ reason: "no_items",
105
+ documentType,
106
+ itemCount: 0,
107
+ });
85
108
  return;
86
109
  }
87
110
 
@@ -95,12 +118,14 @@ export function LiveInvoicePreview({
95
118
  abortControllerRef.current = abortController;
96
119
 
97
120
  setIsLoading(true);
121
+ setIsRefreshPending(false);
98
122
  setError(null);
99
123
 
100
124
  try {
101
125
  if (!sdk || !activeEntity?.id) {
102
126
  throw new Error("Authentication required");
103
127
  }
128
+ const startedAt = performance.now();
104
129
 
105
130
  // Prepare preview data with active entity as issuer (if not already set)
106
131
  // Exclude 'number' as it's auto-generated by the render endpoint
@@ -122,6 +147,30 @@ export function LiveInvoicePreview({
122
147
  ...invoiceData.issuer,
123
148
  },
124
149
  };
150
+ const requestKey = JSON.stringify({
151
+ documentType,
152
+ template,
153
+ qrOverrides,
154
+ entityId: activeEntity.id,
155
+ previewData,
156
+ });
157
+ if (lastRequestKeyRef.current === requestKey) {
158
+ setIsLoading(false);
159
+ setIsRefreshPending(false);
160
+ emitLivePreviewDebug({
161
+ stage: "deduped",
162
+ documentType,
163
+ itemCount: previewData.items?.length ?? 0,
164
+ });
165
+ return;
166
+ }
167
+ lastRequestKeyRef.current = requestKey;
168
+ emitLivePreviewDebug({
169
+ stage: "request_started",
170
+ documentType,
171
+ itemCount: previewData.items?.length ?? 0,
172
+ template,
173
+ });
125
174
 
126
175
  // Call the render API using the appropriate SDK method for the document type
127
176
  // Don't send locale — let entity locale drive formatting (decimal separators, date format)
@@ -135,7 +184,7 @@ export function LiveInvoicePreview({
135
184
  if (qrOverrides?.epc_qr_enabled !== undefined) {
136
185
  renderParams.epc_qr_enabled = qrOverrides.epc_qr_enabled ? "true" : "false";
137
186
  }
138
- const requestOpts = { entity_id: activeEntity.id };
187
+ const requestOpts = { entity_id: activeEntity.id, signal: abortController.signal };
139
188
  let html: string;
140
189
  switch (documentType) {
141
190
  case "estimate":
@@ -157,19 +206,40 @@ export function LiveInvoicePreview({
157
206
 
158
207
  setPreviewHtml(html);
159
208
  setError(null);
209
+ emitLivePreviewDebug({
210
+ stage: "request_succeeded",
211
+ documentType,
212
+ itemCount: previewData.items?.length ?? 0,
213
+ elapsedMs: Number((performance.now() - startedAt).toFixed(1)),
214
+ });
160
215
  } catch (err) {
161
216
  // Ignore abort errors (they're expected when user keeps typing)
162
217
  if (err instanceof Error && err.name === "AbortError") {
218
+ emitLivePreviewDebug({
219
+ stage: "request_aborted",
220
+ documentType,
221
+ });
163
222
  return;
164
223
  }
165
224
 
166
225
  // Ignore 422 validation errors - expected while user is still filling the form
167
226
  if (err instanceof Error && "status" in err && (err as any).status === 422) {
227
+ emitLivePreviewDebug({
228
+ stage: "request_validation_error",
229
+ documentType,
230
+ status: 422,
231
+ });
168
232
  return;
169
233
  }
170
234
 
171
235
  setError(err instanceof Error ? err.message : "Failed to generate preview");
172
236
  setPreviewHtml("");
237
+ lastRequestKeyRef.current = null;
238
+ emitLivePreviewDebug({
239
+ stage: "request_failed",
240
+ documentType,
241
+ message: err instanceof Error ? err.message : "Failed to generate preview",
242
+ });
173
243
  } finally {
174
244
  // Only set loading to false if this request wasn't aborted
175
245
  if (!abortController.signal.aborted) {
@@ -190,6 +260,7 @@ export function LiveInvoicePreview({
190
260
  template,
191
261
  sdk,
192
262
  documentType,
263
+ qrOverrides,
193
264
  qrOverrides?.upn_qr_enabled,
194
265
  qrOverrides?.upn_qr_display_mode,
195
266
  qrOverrides?.epc_qr_enabled,
@@ -198,7 +269,7 @@ export function LiveInvoicePreview({
198
269
 
199
270
  /**
200
271
  * Debounced preview fetch
201
- * Waits 500ms after user stops typing before fetching
272
+ * Waits briefly after user stops typing before fetching
202
273
  */
203
274
  useEffect(() => {
204
275
  // Clear previous timeout
@@ -206,18 +277,31 @@ export function LiveInvoicePreview({
206
277
  clearTimeout(debounceTimeoutRef.current);
207
278
  }
208
279
 
209
- // Set new timeout
210
- debounceTimeoutRef.current = setTimeout(() => {
280
+ const hasItems = !!data.items && data.items.length > 0;
281
+ const shouldFetchImmediately = eagerFirstPreview && hasItems && !hasDispatchedFirstPreviewRef.current;
282
+
283
+ if (shouldFetchImmediately) {
284
+ setIsRefreshPending(false);
285
+ hasDispatchedFirstPreviewRef.current = true;
211
286
  fetchPreview(data);
212
- }, 500);
287
+ } else {
288
+ setIsRefreshPending(hasItems);
289
+ // Set new timeout
290
+ debounceTimeoutRef.current = setTimeout(() => {
291
+ setIsRefreshPending(false);
292
+ hasDispatchedFirstPreviewRef.current = true;
293
+ fetchPreview(data);
294
+ }, LIVE_PREVIEW_DEBOUNCE_MS);
295
+ }
213
296
 
214
297
  // Cleanup
215
298
  return () => {
216
299
  if (debounceTimeoutRef.current) {
217
300
  clearTimeout(debounceTimeoutRef.current);
218
301
  }
302
+ setIsRefreshPending(false);
219
303
  };
220
- }, [data, fetchPreview]);
304
+ }, [data, eagerFirstPreview, fetchPreview]);
221
305
 
222
306
  /**
223
307
  * Cleanup on unmount
@@ -232,10 +316,13 @@ export function LiveInvoicePreview({
232
316
  if (debounceTimeoutRef.current) {
233
317
  clearTimeout(debounceTimeoutRef.current);
234
318
  }
319
+ setIsRefreshPending(false);
320
+ lastRequestKeyRef.current = null;
235
321
  };
236
322
  }, []);
237
323
 
238
324
  const showSkeleton = (!previewHtml && !error) || (isLoading && !previewHtml);
325
+ const showRefreshBadge = !!previewHtml && (isRefreshPending || isLoading);
239
326
 
240
327
  return (
241
328
  <div ref={containerRef} className={cn("relative", className)}>
@@ -254,14 +341,25 @@ export function LiveInvoicePreview({
254
341
 
255
342
  {/* Preview - Scoped HTML injection with A4 scaling */}
256
343
  {previewHtml && !error && (
257
- <div className={cn(isLoading && "opacity-50 transition-opacity duration-200")}>
258
- <ScaledDocumentPreview
259
- htmlContent={previewHtml}
260
- scale={scale}
261
- contentHeight={contentHeight}
262
- A4_WIDTH_PX={A4_WIDTH_PX}
263
- contentRef={contentRef}
264
- />
344
+ <div className="relative">
345
+ {showRefreshBadge && (
346
+ <div className="absolute top-4 right-4 z-20">
347
+ <div className="inline-flex items-center gap-2 rounded-full border bg-background/95 px-3 py-1.5 text-muted-foreground text-xs shadow-sm">
348
+ <Loader2 className={cn("size-3.5", (isRefreshPending || isLoading) && "animate-spin")} />
349
+ <span>{isLoading ? t("Updating preview...") : t("Refreshing preview...")}</span>
350
+ </div>
351
+ </div>
352
+ )}
353
+ {isLoading && <div className="absolute inset-0 z-10 rounded-lg bg-background/35 backdrop-blur-[1.5px]" />}
354
+ <div className={cn(isLoading && "opacity-75 transition-opacity duration-200")}>
355
+ <ScaledDocumentPreview
356
+ htmlContent={previewHtml}
357
+ scale={scale}
358
+ contentHeight={contentHeight}
359
+ A4_WIDTH_PX={A4_WIDTH_PX}
360
+ contentRef={contentRef}
361
+ />
362
+ </div>
265
363
  </div>
266
364
  )}
267
365
  </div>
@@ -73,39 +73,47 @@ export function prepareDocumentSubmission<T extends BaseDocumentValues>(
73
73
  values: T,
74
74
  options: PrepareDocumentOptions,
75
75
  ): any {
76
+ const nextValues: any = {
77
+ ...values,
78
+ customer: values.customer ? { ...values.customer } : values.customer,
79
+ items: values.items
80
+ ? values.items.map((item: any) => ({ ...item, taxes: item?.taxes ? [...item.taxes] : item?.taxes }))
81
+ : values.items,
82
+ };
83
+
76
84
  // Document numbers are always auto-generated by the server
77
85
  // Remove number from payload (even if form provides a preview value)
78
86
 
79
87
  // Handle customer logic
80
- if (values.customer_id && values.customer) {
88
+ if (nextValues.customer_id && nextValues.customer) {
81
89
  // If customer form was not shown, remove customer data (keep only customer_id)
82
90
  if (options.wasCustomerFormShown === false) {
83
- delete values.customer;
91
+ delete nextValues.customer;
84
92
  } else {
85
93
  // Existing customer loaded - check if data was actually modified
86
94
  const customerChanged =
87
- options.originalCustomer && JSON.stringify(values.customer) !== JSON.stringify(options.originalCustomer);
95
+ options.originalCustomer && JSON.stringify(nextValues.customer) !== JSON.stringify(options.originalCustomer);
88
96
 
89
97
  if (!customerChanged) {
90
98
  // No changes - send only customer_id
91
- delete values.customer;
99
+ delete nextValues.customer;
92
100
  } else {
93
101
  // Changes detected - clean null/empty values and send with save_customer flag
94
102
  const cleanedCustomer: any = { save_customer: true };
95
- for (const [key, value] of Object.entries(values.customer)) {
103
+ for (const [key, value] of Object.entries(nextValues.customer)) {
96
104
  if (key !== "save_customer" && value !== "" && value !== null && value !== undefined) {
97
105
  cleanedCustomer[key] = value;
98
106
  }
99
107
  }
100
- values.customer = cleanedCustomer;
108
+ nextValues.customer = cleanedCustomer;
101
109
  }
102
110
  }
103
- } else if (values.customer) {
111
+ } else if (nextValues.customer) {
104
112
  // New inline customer - clean null/empty values and add save flag
105
113
  const cleanedCustomer: any = { save_customer: true };
106
114
  let hasAnyValue = false;
107
115
 
108
- for (const [key, value] of Object.entries(values.customer)) {
116
+ for (const [key, value] of Object.entries(nextValues.customer)) {
109
117
  if (key !== "save_customer" && value !== "" && value !== null && value !== undefined) {
110
118
  cleanedCustomer[key] = value;
111
119
  hasAnyValue = true;
@@ -113,21 +121,21 @@ export function prepareDocumentSubmission<T extends BaseDocumentValues>(
113
121
  }
114
122
 
115
123
  if (!hasAnyValue) {
116
- delete values.customer;
124
+ delete nextValues.customer;
117
125
  } else {
118
- values.customer = cleanedCustomer;
126
+ nextValues.customer = cleanedCustomer;
119
127
  }
120
128
  }
121
129
 
122
130
  // Clean up customer_id if empty
123
- if (!values.customer_id) {
124
- delete values.customer_id;
131
+ if (!nextValues.customer_id) {
132
+ delete nextValues.customer_id;
125
133
  }
126
134
 
127
135
  // Clean up taxes and handle gross price transformation
128
- if (values.items) {
136
+ if (nextValues.items) {
129
137
  const priceModes = options.priceModes ?? {};
130
- values.items = values.items.map((item: any, index: number) => {
138
+ nextValues.items = nextValues.items.map((item: any, index: number) => {
131
139
  // Separator items — pass through with only type, name, description
132
140
  if (item.type === "separator") {
133
141
  return {
@@ -162,7 +170,7 @@ export function prepareDocumentSubmission<T extends BaseDocumentValues>(
162
170
 
163
171
  // Build payload with date conversions
164
172
  // Destructure to exclude fields we handle explicitly (number is always server-generated)
165
- const { number: _number, note, payment_terms, reference, signature, ...restValues } = values as any;
173
+ const { number: _number, note, payment_terms, reference, signature, ...restValues } = nextValues as any;
166
174
  const payload: any = {
167
175
  ...restValues,
168
176
  ...(note?.trim() && { note: note.trim() }),
@@ -170,7 +178,7 @@ export function prepareDocumentSubmission<T extends BaseDocumentValues>(
170
178
  // Advance invoices don't have payment terms - they are documents requesting payment
171
179
  ...(options.documentType !== "advance_invoice" && payment_terms?.trim() && { payment_terms: payment_terms.trim() }),
172
180
  ...(signature?.trim() && { signature: signature.trim() }),
173
- date: values.date ? new Date(values.date) : undefined,
181
+ date: nextValues.date ? new Date(nextValues.date) : undefined,
174
182
  };
175
183
 
176
184
  // Add secondary date field based on document type
@@ -181,6 +189,17 @@ export function prepareDocumentSubmission<T extends BaseDocumentValues>(
181
189
  }
182
190
  // Credit notes don't have a secondary date field
183
191
 
192
+ // Add service date fields for invoices and credit notes
193
+ if (options.documentType === "invoice" || options.documentType === "credit_note") {
194
+ const v = nextValues as any;
195
+ if (v.date_service) {
196
+ payload.date_service = new Date(v.date_service);
197
+ }
198
+ if (v.date_service_to) {
199
+ payload.date_service_to = new Date(v.date_service_to);
200
+ }
201
+ }
202
+
184
203
  // Handle markAsPaid for invoices and credit notes
185
204
  if (
186
205
  options.documentType !== "estimate" &&
@@ -1,4 +1,4 @@
1
- import { useState } from "react";
1
+ import { useEffect, useState } from "react";
2
2
  import type { FieldValues, Path, PathValue, UseFormReturn } from "react-hook-form";
3
3
 
4
4
  /**
@@ -67,9 +67,20 @@ export function useDocumentCustomerForm<TForm extends DocumentFormWithCustomer>(
67
67
  const [shouldFocusName, setShouldFocusName] = useState(false);
68
68
  const [selectedCustomerId, setSelectedCustomerId] = useState<string | undefined>(initialCustomerId);
69
69
 
70
+ useEffect(() => {
71
+ const defaultCustomerId = form.formState.defaultValues?.customer_id as string | undefined;
72
+ const defaultCustomer = form.formState.defaultValues?.customer as CustomerData | undefined;
73
+ const hasDefaultCustomer = !!(defaultCustomerId || defaultCustomer?.name);
74
+
75
+ setOriginalCustomer(hasDefaultCustomer && defaultCustomer ? defaultCustomer : null);
76
+ setSelectedCustomerId(defaultCustomerId);
77
+ setShowCustomerForm(hasDefaultCustomer);
78
+ setShouldFocusName(false);
79
+ }, [form.formState.defaultValues]);
80
+
70
81
  // Type-safe setValue that works with the generic form type
71
82
  const setValue = <K extends Path<TForm>>(name: K, value: PathValue<TForm, K>) => {
72
- form.setValue(name, value);
83
+ form.setValue(name, value, { shouldDirty: true, shouldTouch: true });
73
84
  };
74
85
 
75
86
  const handleCustomerSelect = (customerId: string, customer: CustomerData) => {
@@ -158,7 +169,7 @@ export function useDocumentCustomerForm<TForm extends DocumentFormWithCustomer>(
158
169
  shouldFocusName,
159
170
  selectedCustomerId,
160
171
  /** Initial customer name from form defaults (for duplication display) */
161
- initialCustomerName: initialCustomer?.name ?? undefined,
172
+ initialCustomerName: (form.formState.defaultValues?.customer as CustomerData | undefined)?.name ?? undefined,
162
173
  handleCustomerSelect,
163
174
  handleCustomerClear,
164
175
  };
@@ -26,6 +26,8 @@ type FinalizeDocumentOptions = {
26
26
  type FinalizeDocumentVariables = {
27
27
  documentId: string;
28
28
  documentType: DocumentType;
29
+ furs?: { business_premise_name: string; electronic_device_name: string } | { skip: true };
30
+ fina?: { business_premise_name: string; electronic_device_name: string; payment_type?: string };
29
31
  };
30
32
 
31
33
  /**
@@ -37,8 +39,11 @@ export function useFinalizeDocument(options: FinalizeDocumentOptions) {
37
39
  const queryClient = useQueryClient();
38
40
 
39
41
  return useMutation({
40
- mutationFn: async ({ documentId, documentType }: FinalizeDocumentVariables) => {
41
- return sdk.documents.finalizeDocument(documentId, { type: documentType });
42
+ mutationFn: async ({ documentId, documentType, furs, fina }: FinalizeDocumentVariables) => {
43
+ const body: Record<string, unknown> = {};
44
+ if (furs) body.furs = furs;
45
+ if (fina) body.fina = fina;
46
+ return sdk.documents.finalizeDocument(documentId, body, { type: documentType });
42
47
  },
43
48
  onSuccess: (data, variables) => {
44
49
  // Invalidate list cache