@spaceinvoices/react-ui 0.1.1

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 (352) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +340 -0
  3. package/cli/dist/index.js +922 -0
  4. package/package.json +87 -0
  5. package/registry.json +600 -0
  6. package/spaceinvoices.schema.json +47 -0
  7. package/src/app.tsx +25 -0
  8. package/src/common/autocomplete.tsx +135 -0
  9. package/src/components/activities/activity-timeline.tsx +160 -0
  10. package/src/components/activities/index.ts +1 -0
  11. package/src/components/activities/locales/de.ts +30 -0
  12. package/src/components/activities/locales/sl.ts +30 -0
  13. package/src/components/advance-invoices/advance-invoices.hooks.ts +75 -0
  14. package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +702 -0
  15. package/src/components/advance-invoices/create/locales/de.ts +29 -0
  16. package/src/components/advance-invoices/create/locales/sl.ts +25 -0
  17. package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +74 -0
  18. package/src/components/advance-invoices/index.ts +5 -0
  19. package/src/components/advance-invoices/list/index.ts +3 -0
  20. package/src/components/advance-invoices/list/list-row-actions.tsx +119 -0
  21. package/src/components/advance-invoices/list/list-table.tsx +178 -0
  22. package/src/components/advance-invoices/list/locales/de.ts +32 -0
  23. package/src/components/advance-invoices/list/locales/sl.ts +32 -0
  24. package/src/components/advance-invoices/list/use-advance-invoice-download.ts +63 -0
  25. package/src/components/button-loader.tsx +11 -0
  26. package/src/components/combobox.tsx +96 -0
  27. package/src/components/company-registry/company-registry-autocomplete.tsx +151 -0
  28. package/src/components/company-registry/company-registry.hooks.ts +67 -0
  29. package/src/components/company-registry/index.ts +7 -0
  30. package/src/components/credit-notes/create/create-credit-note-form.tsx +332 -0
  31. package/src/components/credit-notes/create/index.ts +1 -0
  32. package/src/components/credit-notes/create/locales/de.ts +69 -0
  33. package/src/components/credit-notes/create/locales/sl.ts +67 -0
  34. package/src/components/credit-notes/credit-notes.hooks.ts +22 -0
  35. package/src/components/credit-notes/index.ts +10 -0
  36. package/src/components/credit-notes/list/index.ts +3 -0
  37. package/src/components/credit-notes/list/list-row-actions.tsx +116 -0
  38. package/src/components/credit-notes/list/list-table.tsx +183 -0
  39. package/src/components/credit-notes/list/locales/de.ts +33 -0
  40. package/src/components/credit-notes/list/locales/sl.ts +33 -0
  41. package/src/components/credit-notes/list/use-credit-note-download.ts +65 -0
  42. package/src/components/customers/create-customer-form/create-customer-form.tsx +134 -0
  43. package/src/components/customers/create-customer-form/locales/de.ts +20 -0
  44. package/src/components/customers/create-customer-form/locales/sl.ts +20 -0
  45. package/src/components/customers/customer-autocomplete.tsx +173 -0
  46. package/src/components/customers/customer-combobox.tsx +130 -0
  47. package/src/components/customers/customer-list-table/customer-list-row-actions.tsx +48 -0
  48. package/src/components/customers/customer-list-table/customer-list-table.tsx +124 -0
  49. package/src/components/customers/customer-list-table/index.ts +2 -0
  50. package/src/components/customers/customer-list-table/locales/de.ts +16 -0
  51. package/src/components/customers/customer-list-table/locales/sl.ts +16 -0
  52. package/src/components/customers/customers.hooks.test.ts +348 -0
  53. package/src/components/customers/customers.hooks.ts +57 -0
  54. package/src/components/customers/index.ts +5 -0
  55. package/src/components/dashboard/chart-empty-state.tsx +29 -0
  56. package/src/components/dashboard/collection-rate-card/collection-rate-card.tsx +80 -0
  57. package/src/components/dashboard/collection-rate-card/index.ts +4 -0
  58. package/src/components/dashboard/collection-rate-card/locales/sl.ts +3 -0
  59. package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +74 -0
  60. package/src/components/dashboard/index.ts +54 -0
  61. package/src/components/dashboard/invoice-status-chart/index.ts +4 -0
  62. package/src/components/dashboard/invoice-status-chart/invoice-status-chart.tsx +130 -0
  63. package/src/components/dashboard/invoice-status-chart/locales/sl.ts +9 -0
  64. package/src/components/dashboard/invoice-status-chart/use-invoice-status.ts +105 -0
  65. package/src/components/dashboard/loading-card.tsx +19 -0
  66. package/src/components/dashboard/payment-methods-chart/index.ts +4 -0
  67. package/src/components/dashboard/payment-methods-chart/locales/sl.ts +12 -0
  68. package/src/components/dashboard/payment-methods-chart/payment-methods-chart.tsx +152 -0
  69. package/src/components/dashboard/payment-methods-chart/use-payment-methods.ts +50 -0
  70. package/src/components/dashboard/payment-trend-chart/index.ts +4 -0
  71. package/src/components/dashboard/payment-trend-chart/locales/sl.ts +5 -0
  72. package/src/components/dashboard/payment-trend-chart/payment-trend-chart.tsx +137 -0
  73. package/src/components/dashboard/payment-trend-chart/use-payment-trend.ts +92 -0
  74. package/src/components/dashboard/revenue-card.tsx +49 -0
  75. package/src/components/dashboard/revenue-trend-chart/index.ts +4 -0
  76. package/src/components/dashboard/revenue-trend-chart/locales/sl.ts +5 -0
  77. package/src/components/dashboard/revenue-trend-chart/revenue-trend-chart.tsx +137 -0
  78. package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +93 -0
  79. package/src/components/dashboard/shared/index.ts +5 -0
  80. package/src/components/dashboard/shared/use-revenue-data.ts +160 -0
  81. package/src/components/dashboard/shared/use-stats-counts.ts +89 -0
  82. package/src/components/dashboard/shared/use-stats-query.ts +38 -0
  83. package/src/components/dashboard/stat-card.tsx +41 -0
  84. package/src/components/dashboard/tax-collected-card/index.ts +2 -0
  85. package/src/components/dashboard/tax-collected-card/tax-collected-card.tsx +77 -0
  86. package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +145 -0
  87. package/src/components/dashboard/top-customers-chart/index.ts +4 -0
  88. package/src/components/dashboard/top-customers-chart/locales/sl.ts +5 -0
  89. package/src/components/dashboard/top-customers-chart/top-customers-chart.tsx +130 -0
  90. package/src/components/dashboard/top-customers-chart/use-top-customers.ts +72 -0
  91. package/src/components/documents/create/document-add-item-form.tsx +379 -0
  92. package/src/components/documents/create/document-add-item-tax-rate-field.tsx +120 -0
  93. package/src/components/documents/create/document-details-section.tsx +597 -0
  94. package/src/components/documents/create/document-items-section.tsx +133 -0
  95. package/src/components/documents/create/document-recipient-section.tsx +101 -0
  96. package/src/components/documents/create/form-types.ts +36 -0
  97. package/src/components/documents/create/index.ts +9 -0
  98. package/src/components/documents/create/live-preview.tsx +235 -0
  99. package/src/components/documents/create/mark-as-paid-section.tsx +82 -0
  100. package/src/components/documents/create/prepare-document-submission.test.ts +132 -0
  101. package/src/components/documents/create/prepare-document-submission.ts +187 -0
  102. package/src/components/documents/create/prepare-preview-data.test.ts +155 -0
  103. package/src/components/documents/create/prepare-preview-data.ts +16 -0
  104. package/src/components/documents/create/smart-code-insert-button.tsx +139 -0
  105. package/src/components/documents/create/use-document-customer-form.ts +161 -0
  106. package/src/components/documents/document-preview.tsx +13 -0
  107. package/src/components/documents/documents.hooks.ts +146 -0
  108. package/src/components/documents/index.ts +23 -0
  109. package/src/components/documents/shared/document-preview-display.tsx +172 -0
  110. package/src/components/documents/shared/index.ts +3 -0
  111. package/src/components/documents/shared/scaled-document-preview.tsx +70 -0
  112. package/src/components/documents/shared/use-a4-scaling.ts +62 -0
  113. package/src/components/documents/types.ts +61 -0
  114. package/src/components/documents/view/document-actions-bar.tsx +328 -0
  115. package/src/components/documents/view/document-details-card.tsx +179 -0
  116. package/src/components/documents/view/document-payments-list.tsx +256 -0
  117. package/src/components/documents/view/index.ts +4 -0
  118. package/src/components/documents/view/locales/de.ts +85 -0
  119. package/src/components/documents/view/locales/sl.ts +84 -0
  120. package/src/components/documents/view/use-document-download.ts +125 -0
  121. package/src/components/entities/create-entity-form.tsx +105 -0
  122. package/src/components/entities/entities.hooks.ts +50 -0
  123. package/src/components/entities/entity-settings-form/email-template-variables-info.tsx +103 -0
  124. package/src/components/entities/entity-settings-form/entity-settings-form.tsx +1326 -0
  125. package/src/components/entities/entity-settings-form/image-upload-with-crop.tsx +222 -0
  126. package/src/components/entities/entity-settings-form/index.ts +2 -0
  127. package/src/components/entities/entity-settings-form/input-with-preview.tsx +190 -0
  128. package/src/components/entities/entity-settings-form/locales/de.ts +192 -0
  129. package/src/components/entities/entity-settings-form/locales/sl.ts +188 -0
  130. package/src/components/entities/furs-settings-form/furs-settings-form.tsx +410 -0
  131. package/src/components/entities/furs-settings-form/furs-settings.hooks.ts +320 -0
  132. package/src/components/entities/furs-settings-form/index.ts +3 -0
  133. package/src/components/entities/furs-settings-form/locales/de.ts +233 -0
  134. package/src/components/entities/furs-settings-form/locales/en.ts +194 -0
  135. package/src/components/entities/furs-settings-form/locales/sl.ts +196 -0
  136. package/src/components/entities/furs-settings-form/sections/certificate-settings-section.tsx +242 -0
  137. package/src/components/entities/furs-settings-form/sections/enable-fiscalization-section.tsx +139 -0
  138. package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +252 -0
  139. package/src/components/entities/furs-settings-form/sections/premises-management-section.tsx +370 -0
  140. package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +420 -0
  141. package/src/components/entities/keys.ts +2 -0
  142. package/src/components/entities/settings/branding-settings-form.tsx +274 -0
  143. package/src/components/entities/settings/company-settings-form.tsx +256 -0
  144. package/src/components/entities/settings/defaults-settings-form.tsx +501 -0
  145. package/src/components/entities/settings/email-settings-form.tsx +288 -0
  146. package/src/components/entities/settings/eslog-settings-form.tsx +113 -0
  147. package/src/components/entities/settings/index.ts +8 -0
  148. package/src/components/entities/settings/number-format-settings-form.tsx +244 -0
  149. package/src/components/entities/settings/pdf-template-selector/demo-invoice-data.ts +164 -0
  150. package/src/components/entities/settings/pdf-template-selector/index.ts +2 -0
  151. package/src/components/entities/settings/pdf-template-selector/locales/de.ts +18 -0
  152. package/src/components/entities/settings/pdf-template-selector/locales/sl.ts +18 -0
  153. package/src/components/entities/settings/pdf-template-selector/pdf-template-cards.tsx +49 -0
  154. package/src/components/entities/settings/settings-footer.tsx +16 -0
  155. package/src/components/entities/settings/tax-rules-settings-form.tsx +346 -0
  156. package/src/components/estimates/create/create-estimate-form.tsx +384 -0
  157. package/src/components/estimates/create/locales/de.ts +64 -0
  158. package/src/components/estimates/create/locales/sl.ts +63 -0
  159. package/src/components/estimates/create/prepare-estimate-submission.ts +39 -0
  160. package/src/components/estimates/create/use-estimate-customer-form.ts +5 -0
  161. package/src/components/estimates/estimates.hooks.ts +15 -0
  162. package/src/components/estimates/index.ts +6 -0
  163. package/src/components/estimates/list/index.ts +3 -0
  164. package/src/components/estimates/list/list-row-actions.tsx +103 -0
  165. package/src/components/estimates/list/list-table.tsx +171 -0
  166. package/src/components/estimates/list/locales/de.ts +26 -0
  167. package/src/components/estimates/list/locales/sl.ts +26 -0
  168. package/src/components/estimates/list/use-estimate-download.ts +63 -0
  169. package/src/components/export/document-export-form.tsx +288 -0
  170. package/src/components/export/index.ts +2 -0
  171. package/src/components/form/form-input.tsx +89 -0
  172. package/src/components/form/index.ts +1 -0
  173. package/src/components/invoices/create/create-invoice-form.tsx +852 -0
  174. package/src/components/invoices/create/eslog-validation.test.ts +242 -0
  175. package/src/components/invoices/create/eslog-validation.ts +208 -0
  176. package/src/components/invoices/create/locales/de.ts +118 -0
  177. package/src/components/invoices/create/locales/sl.ts +114 -0
  178. package/src/components/invoices/create/prepare-invoice-submission.test.ts +777 -0
  179. package/src/components/invoices/create/prepare-invoice-submission.ts +79 -0
  180. package/src/components/invoices/create/use-invoice-customer-form.ts +5 -0
  181. package/src/components/invoices/index.ts +9 -0
  182. package/src/components/invoices/invoices-furs.hooks.ts +28 -0
  183. package/src/components/invoices/invoices.hooks.ts +110 -0
  184. package/src/components/invoices/list/index.ts +3 -0
  185. package/src/components/invoices/list/list-row-actions.tsx +132 -0
  186. package/src/components/invoices/list/list-table.tsx +165 -0
  187. package/src/components/invoices/list/locales/de.ts +33 -0
  188. package/src/components/invoices/list/locales/sl.ts +33 -0
  189. package/src/components/invoices/list/use-invoice-download.ts +62 -0
  190. package/src/components/invoices/send-email-dialog/index.ts +1 -0
  191. package/src/components/invoices/send-email-dialog/locales/de.ts +18 -0
  192. package/src/components/invoices/send-email-dialog/locales/sl.ts +17 -0
  193. package/src/components/invoices/send-email-dialog/send-email-dialog.tsx +289 -0
  194. package/src/components/invoices/send-email-dialog.tsx +2 -0
  195. package/src/components/invoices/shared/index.ts +2 -0
  196. package/src/components/invoices/shared/scaled-document-preview.tsx +32 -0
  197. package/src/components/invoices/shared/use-a4-scaling.tsx +39 -0
  198. package/src/components/invoices/view/eslog-info-display.tsx +160 -0
  199. package/src/components/invoices/view/furs-info-display.tsx +213 -0
  200. package/src/components/items/create-item-form/create-item-form.tsx +155 -0
  201. package/src/components/items/create-item-form/locales/de.ts +14 -0
  202. package/src/components/items/create-item-form/locales/en.ts +9 -0
  203. package/src/components/items/create-item-form/locales/sl.ts +14 -0
  204. package/src/components/items/item-combobox.tsx +147 -0
  205. package/src/components/items/item-list-table/item-list-header.tsx +33 -0
  206. package/src/components/items/item-list-table/item-list-row-actions.tsx +48 -0
  207. package/src/components/items/item-list-table/item-list-row.tsx +32 -0
  208. package/src/components/items/item-list-table/item-list-table.tsx +76 -0
  209. package/src/components/items/item-list-table/locales/de.ts +10 -0
  210. package/src/components/items/item-list-table/locales/en.ts +10 -0
  211. package/src/components/items/item-list-table/locales/sl.ts +10 -0
  212. package/src/components/items/items.hooks.ts +63 -0
  213. package/src/components/loading-spinner.tsx +24 -0
  214. package/src/components/payments/create-payment-form/create-payment-form.tsx +222 -0
  215. package/src/components/payments/create-payment-form/locales/de.ts +20 -0
  216. package/src/components/payments/create-payment-form/locales/sl.ts +20 -0
  217. package/src/components/payments/edit-payment-form/edit-payment-form.tsx +230 -0
  218. package/src/components/payments/edit-payment-form/index.ts +1 -0
  219. package/src/components/payments/edit-payment-form/locales/de.ts +20 -0
  220. package/src/components/payments/edit-payment-form/locales/sl.ts +20 -0
  221. package/src/components/payments/index.ts +4 -0
  222. package/src/components/payments/list/index.ts +2 -0
  223. package/src/components/payments/list/list-row-actions.tsx +98 -0
  224. package/src/components/payments/list/list-table.tsx +186 -0
  225. package/src/components/payments/list/locales/de.ts +19 -0
  226. package/src/components/payments/list/locales/sl.ts +19 -0
  227. package/src/components/payments/payments.hooks.ts +15 -0
  228. package/src/components/request-logs/index.ts +3 -0
  229. package/src/components/request-logs/request-log-detail.tsx +242 -0
  230. package/src/components/request-logs/request-log-list-table.tsx +266 -0
  231. package/src/components/request-logs/request-logs-page.tsx +10 -0
  232. package/src/components/table/README.md +410 -0
  233. package/src/components/table/data-table.tsx +251 -0
  234. package/src/components/table/date-cell.tsx +35 -0
  235. package/src/components/table/filter-bar.tsx +114 -0
  236. package/src/components/table/filter-panel.tsx +407 -0
  237. package/src/components/table/hooks/use-table-fetch.ts +17 -0
  238. package/src/components/table/hooks/use-table-query.ts +36 -0
  239. package/src/components/table/hooks/use-table-state.ts +293 -0
  240. package/src/components/table/index.ts +35 -0
  241. package/src/components/table/search-input.tsx +85 -0
  242. package/src/components/table/sortable-header.tsx +56 -0
  243. package/src/components/table/table-empty-state.tsx +40 -0
  244. package/src/components/table/table-no-results.tsx +41 -0
  245. package/src/components/table/table-pagination.tsx +42 -0
  246. package/src/components/table/table-skeleton.tsx +54 -0
  247. package/src/components/table/types.ts +136 -0
  248. package/src/components/tax-reports/index.ts +1 -0
  249. package/src/components/tax-reports/kir-export-form.tsx +172 -0
  250. package/src/components/taxes/create-tax-form/create-tax-form.tsx +112 -0
  251. package/src/components/taxes/create-tax-form/locales/de.ts +8 -0
  252. package/src/components/taxes/create-tax-form/locales/en.ts +7 -0
  253. package/src/components/taxes/create-tax-form/locales/sl.ts +8 -0
  254. package/src/components/taxes/tax-list-table/locales/de.ts +11 -0
  255. package/src/components/taxes/tax-list-table/locales/en.ts +10 -0
  256. package/src/components/taxes/tax-list-table/locales/sl.ts +11 -0
  257. package/src/components/taxes/tax-list-table/tax-list-header.tsx +29 -0
  258. package/src/components/taxes/tax-list-table/tax-list-row-actions.tsx +43 -0
  259. package/src/components/taxes/tax-list-table/tax-list-row.tsx +46 -0
  260. package/src/components/taxes/tax-list-table/tax-list-table.tsx +59 -0
  261. package/src/components/taxes/taxes.hooks.ts +35 -0
  262. package/src/components/ui/alert-dialog.tsx +61 -0
  263. package/src/components/ui/alert.tsx +72 -0
  264. package/src/components/ui/badge.tsx +48 -0
  265. package/src/components/ui/breadcrumb.tsx +132 -0
  266. package/src/components/ui/button.tsx +61 -0
  267. package/src/components/ui/calendar.tsx +213 -0
  268. package/src/components/ui/card.tsx +94 -0
  269. package/src/components/ui/chart.tsx +380 -0
  270. package/src/components/ui/checkbox.tsx +27 -0
  271. package/src/components/ui/collapsible.tsx +56 -0
  272. package/src/components/ui/command.tsx +187 -0
  273. package/src/components/ui/dialog.tsx +187 -0
  274. package/src/components/ui/drawer.tsx +123 -0
  275. package/src/components/ui/dropdown-menu.tsx +291 -0
  276. package/src/components/ui/form.tsx +166 -0
  277. package/src/components/ui/input-group.tsx +149 -0
  278. package/src/components/ui/input.tsx +20 -0
  279. package/src/components/ui/label.tsx +18 -0
  280. package/src/components/ui/loading-spinner.tsx +16 -0
  281. package/src/components/ui/popover.tsx +108 -0
  282. package/src/components/ui/radio-group.tsx +37 -0
  283. package/src/components/ui/select.tsx +200 -0
  284. package/src/components/ui/separator.tsx +23 -0
  285. package/src/components/ui/sheet.tsx +145 -0
  286. package/src/components/ui/sidebar.tsx +771 -0
  287. package/src/components/ui/skeleton.tsx +13 -0
  288. package/src/components/ui/sonner.tsx +60 -0
  289. package/src/components/ui/spinner.tsx +10 -0
  290. package/src/components/ui/sticky-form-footer.tsx +55 -0
  291. package/src/components/ui/switch.tsx +30 -0
  292. package/src/components/ui/table.tsx +101 -0
  293. package/src/components/ui/tabs.tsx +80 -0
  294. package/src/components/ui/textarea.tsx +18 -0
  295. package/src/components/ui/tooltip.tsx +89 -0
  296. package/src/components/wl-subscription/index.ts +2 -0
  297. package/src/components/wl-subscription/locked-feature.tsx +173 -0
  298. package/src/components/wl-subscription/upgrade-modal.tsx +209 -0
  299. package/src/frontend.tsx +28 -0
  300. package/src/generate-schemas.ts +265 -0
  301. package/src/generated/schemas/advanceinvoice.ts +177 -0
  302. package/src/generated/schemas/creditnote.ts +187 -0
  303. package/src/generated/schemas/customer.ts +29 -0
  304. package/src/generated/schemas/entity.ts +252 -0
  305. package/src/generated/schemas/estimate.ts +159 -0
  306. package/src/generated/schemas/furssettings.ts +25 -0
  307. package/src/generated/schemas/index.ts +24 -0
  308. package/src/generated/schemas/invoice.ts +167 -0
  309. package/src/generated/schemas/item.ts +38 -0
  310. package/src/generated/schemas/payment.ts +44 -0
  311. package/src/generated/schemas/previewadvanceinvoice_body.ts +354 -0
  312. package/src/generated/schemas/previewestimate_body.ts +309 -0
  313. package/src/generated/schemas/registerfursmovablepremise_body.ts +22 -0
  314. package/src/generated/schemas/registerfursrealestatepremise_body.ts +32 -0
  315. package/src/generated/schemas/renderdocument_body.ts +594 -0
  316. package/src/generated/schemas/sendemail_body.ts +26 -0
  317. package/src/generated/schemas/startpdfexport_body.ts +20 -0
  318. package/src/generated/schemas/tax.ts +48 -0
  319. package/src/generated/schemas/uploadfile_body.ts +23 -0
  320. package/src/generated/schemas/uploadfurscertificate_body.ts +20 -0
  321. package/src/generated/schemas/userfurssettings.ts +19 -0
  322. package/src/hooks/create-resource-hooks.test.ts +483 -0
  323. package/src/hooks/create-resource-hooks.ts +300 -0
  324. package/src/hooks/use-debounce.ts +12 -0
  325. package/src/hooks/use-duplicate-document.ts +185 -0
  326. package/src/hooks/use-media-query.tsx +19 -0
  327. package/src/hooks/use-mobile.ts +39 -0
  328. package/src/hooks/use-next-document-number.ts +57 -0
  329. package/src/hooks/use-resource-mutation.ts +118 -0
  330. package/src/hooks/use-vies-check.ts +130 -0
  331. package/src/index.css +11 -0
  332. package/src/index.html +13 -0
  333. package/src/index.tsx +12 -0
  334. package/src/lib/auth.ts +4 -0
  335. package/src/lib/browser-cookies.ts +70 -0
  336. package/src/lib/constants.ts +287 -0
  337. package/src/lib/cookies.ts +36 -0
  338. package/src/lib/schemas/advance-invoice.ts +43 -0
  339. package/src/lib/schemas/credit-note.ts +32 -0
  340. package/src/lib/schemas/estimate.ts +31 -0
  341. package/src/lib/schemas/index.ts +18 -0
  342. package/src/lib/schemas/invoice.ts +43 -0
  343. package/src/lib/schemas/shared.ts +79 -0
  344. package/src/lib/translation.ts +38 -0
  345. package/src/lib/utils.ts +6 -0
  346. package/src/providers/entities-context.tsx +41 -0
  347. package/src/providers/entities-provider.tsx +201 -0
  348. package/src/providers/form-footer-context.tsx +72 -0
  349. package/src/providers/sdk-provider.tsx +164 -0
  350. package/src/providers/white-label-provider.tsx +91 -0
  351. package/src/providers/wl-subscription-provider.tsx +277 -0
  352. package/src/utils/string-helpers.ts +111 -0
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Shared document items section for invoices and estimates
3
+ * Handles: item management (add, remove, reorder)
4
+ */
5
+ import { PlusIcon } from "lucide-react";
6
+ import type { MutableRefObject } from "react";
7
+ import type { UseFormGetValues, UseFormSetValue, UseFormWatch } from "react-hook-form";
8
+ import { Button } from "@/ui/components/ui/button";
9
+ import DocumentAddItemForm from "./document-add-item-form";
10
+ import type { AnyControl } from "./form-types";
11
+
12
+ /** Map of item index to gross price mode */
13
+ export type PriceModesMap = Record<number, boolean>;
14
+
15
+ type DocumentItemsSectionProps = {
16
+ control: AnyControl;
17
+
18
+ watch: UseFormWatch<any>;
19
+
20
+ setValue: UseFormSetValue<any>;
21
+
22
+ getValues: UseFormGetValues<any>;
23
+ entityId: string;
24
+ currencyCode?: string;
25
+ onAddNewTax?: () => void;
26
+ t: (key: string) => string;
27
+ /** When true, tax controls are disabled (e.g., for VIES reverse charge) */
28
+ taxesDisabled?: boolean;
29
+ /** Message to show when taxes are disabled */
30
+ taxesDisabledMessage?: string;
31
+ /** Maximum number of taxes per item, derived from country rules. Defaults to 1. */
32
+ maxTaxesPerItem?: number;
33
+ /** Ref to collect price modes per item (for submit transformation) */
34
+ priceModesRef?: MutableRefObject<PriceModesMap>;
35
+ /** Initial price modes (from duplicated document) */
36
+ initialPriceModes?: PriceModesMap;
37
+ };
38
+
39
+ export function DocumentItemsSection({
40
+ control,
41
+ watch,
42
+ setValue,
43
+ getValues,
44
+ entityId,
45
+ currencyCode,
46
+ onAddNewTax,
47
+ t,
48
+ taxesDisabled,
49
+ taxesDisabledMessage,
50
+ maxTaxesPerItem,
51
+ priceModesRef,
52
+ initialPriceModes = {},
53
+ }: DocumentItemsSectionProps) {
54
+ 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
+ ]);
66
+ };
67
+
68
+ const removeItem = (index: number) => {
69
+ const currentItems = getValues("items") || [];
70
+ setValue(
71
+ "items",
72
+ currentItems.filter((_: unknown, i: number) => i !== index),
73
+ );
74
+ };
75
+
76
+ const moveItemUp = (index: number) => {
77
+ if (index === 0) return;
78
+ const items = getValues("items");
79
+ const newItems = [...items];
80
+ [newItems[index], newItems[index - 1]] = [newItems[index - 1], newItems[index]];
81
+ setValue("items", newItems);
82
+ };
83
+
84
+ const moveItemDown = (index: number) => {
85
+ const items = getValues("items");
86
+ if (index === items.length - 1) return;
87
+ const newItems = [...items];
88
+ [newItems[index], newItems[index + 1]] = [newItems[index + 1], newItems[index]];
89
+ setValue("items", newItems);
90
+ };
91
+
92
+ const items = watch("items") || [];
93
+
94
+ return (
95
+ <div className="flex flex-col gap-4">
96
+ <h2 className="font-bold text-xl">{t("Items")}</h2>
97
+
98
+ {items.map((_: unknown, index: number) => (
99
+ // biome-ignore lint/suspicious/noArrayIndexKey: index is stable
100
+ <div key={index}>
101
+ <DocumentAddItemForm
102
+ form={{ control, watch, setValue, getValues } as any}
103
+ index={index}
104
+ control={control}
105
+ entityId={entityId}
106
+ currencyCode={currencyCode}
107
+ onRemove={() => removeItem(index)}
108
+ onMoveUp={() => moveItemUp(index)}
109
+ onMoveDown={() => moveItemDown(index)}
110
+ onAddNewTax={onAddNewTax}
111
+ showRemove={items.length > 1}
112
+ showMoveUp={index > 0}
113
+ showMoveDown={index < items.length - 1}
114
+ t={t}
115
+ taxesDisabled={taxesDisabled}
116
+ taxesDisabledMessage={taxesDisabledMessage}
117
+ maxTaxesPerItem={maxTaxesPerItem}
118
+ initialIsGrossPrice={initialPriceModes[index] ?? false}
119
+ onPriceModeChange={(isGross) => {
120
+ if (priceModesRef) {
121
+ priceModesRef.current[index] = isGross;
122
+ }
123
+ }}
124
+ />
125
+ </div>
126
+ ))}
127
+
128
+ <Button type="button" variant="outline" onClick={addItem} className="w-full cursor-pointer border-dashed">
129
+ <PlusIcon className="mr-2 h-4 w-4" /> {t("Add item")}
130
+ </Button>
131
+ </div>
132
+ );
133
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Shared document recipient section for invoices and estimates
3
+ * Handles customer selection and inline customer form
4
+ */
5
+ import { X } from "lucide-react";
6
+ import { useEffect, useRef } from "react";
7
+ import { FormInput } from "@/ui/components/form";
8
+ import { Button } from "@/ui/components/ui/button";
9
+ import { Label } from "@/ui/components/ui/label";
10
+ import { cn } from "@/ui/lib/utils";
11
+ import { CustomerAutocomplete } from "../../customers/customer-autocomplete";
12
+ import type { AnyControl } from "./form-types";
13
+
14
+ type DocumentRecipientSectionProps = {
15
+ control: AnyControl;
16
+ entityId: string;
17
+
18
+ onCustomerSelect: (customerId: string, customer: any) => void;
19
+ onCustomerClear: () => void;
20
+ showCustomerForm: boolean;
21
+ shouldFocusName: boolean;
22
+ selectedCustomerId?: string;
23
+ /** Initial customer name for display (used when duplicating documents) */
24
+ initialCustomerName?: string;
25
+ t: (key: string) => string;
26
+ };
27
+
28
+ export function DocumentRecipientSection({
29
+ control,
30
+ entityId,
31
+ onCustomerSelect,
32
+ onCustomerClear,
33
+ showCustomerForm,
34
+ shouldFocusName,
35
+ selectedCustomerId,
36
+ initialCustomerName,
37
+ t,
38
+ }: DocumentRecipientSectionProps) {
39
+ const nameInputRef = useRef<HTMLInputElement>(null);
40
+
41
+ useEffect(() => {
42
+ if (showCustomerForm && shouldFocusName) {
43
+ // Small delay to ensure the input is rendered
44
+ setTimeout(() => {
45
+ nameInputRef.current?.focus();
46
+ }, 0);
47
+ }
48
+ }, [showCustomerForm, shouldFocusName]);
49
+
50
+ return (
51
+ <div className="flex-1 space-y-4">
52
+ <div className="flex items-center justify-between">
53
+ <h2 className="font-bold text-xl">{t("Recipient")}</h2>
54
+ {showCustomerForm && (
55
+ <Button
56
+ type="button"
57
+ variant="outline"
58
+ size="sm"
59
+ onClick={onCustomerClear}
60
+ className={cn("h-7 cursor-pointer px-2 text-xs")}
61
+ >
62
+ <X className="size-3" />
63
+ {t("Clear")}
64
+ </Button>
65
+ )}
66
+ </div>
67
+
68
+ <div className="space-y-2">
69
+ <Label>{t("Name")}</Label>
70
+ <CustomerAutocomplete
71
+ entityId={entityId}
72
+ value={selectedCustomerId}
73
+ onValueChange={onCustomerSelect}
74
+ onClear={onCustomerClear}
75
+ placeholder={t("Search or create customer...")}
76
+ initialDisplayName={initialCustomerName}
77
+ />
78
+ </div>
79
+
80
+ {showCustomerForm && (
81
+ <>
82
+ <FormInput control={control} name="customer.address" placeholder={t("Address")} label="" ref={nameInputRef} />
83
+
84
+ <FormInput control={control} name="customer.address_2" placeholder={t("Address 2")} label="" />
85
+
86
+ <div className="grid grid-cols-2 gap-4">
87
+ <FormInput control={control} name="customer.post_code" placeholder={t("Post Code")} label="" />
88
+ <FormInput control={control} name="customer.city" placeholder={t("City")} label="" />
89
+ </div>
90
+
91
+ <div className="grid grid-cols-2 gap-4">
92
+ <FormInput control={control} name="customer.state" placeholder={t("State")} label="" />
93
+ <FormInput control={control} name="customer.country" placeholder={t("Country")} label="" />
94
+ </div>
95
+
96
+ <FormInput control={control} name="customer.tax_number" placeholder={t("Tax Number")} label="" />
97
+ </>
98
+ )}
99
+ </div>
100
+ );
101
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Shared form types for document components.
3
+ *
4
+ * This file provides type utilities to handle react-hook-form's complex generic types
5
+ * when passing control and other form utilities between components.
6
+ */
7
+ import type { Control, FieldValues, UseFormGetValues, UseFormSetValue, UseFormWatch } from "react-hook-form";
8
+
9
+ /**
10
+ * A more permissive Control type that accepts any form control.
11
+ * Use this in component props when the component doesn't need to know
12
+ * the exact form type, only that it has certain fields.
13
+ */
14
+ // biome-ignore lint/suspicious/noExplicitAny: Required for react-hook-form compatibility
15
+ export type AnyControl = Control<any, any, any>;
16
+
17
+ /**
18
+ * Base props for document form sections that receive form utilities.
19
+ * Components using these props can work with any form that has the expected fields.
20
+ */
21
+ export type DocumentFormSectionProps = {
22
+ control: AnyControl;
23
+ // biome-ignore lint/suspicious/noExplicitAny: Required for react-hook-form compatibility
24
+ watch: UseFormWatch<any>;
25
+ // biome-ignore lint/suspicious/noExplicitAny: Required for react-hook-form compatibility
26
+ setValue: UseFormSetValue<any>;
27
+ // biome-ignore lint/suspicious/noExplicitAny: Required for react-hook-form compatibility
28
+ getValues: UseFormGetValues<any>;
29
+ };
30
+
31
+ /**
32
+ * Props for components that only need control (no watch/setValue/getValues).
33
+ */
34
+ export type ControlOnlyProps = {
35
+ control: AnyControl;
36
+ };
@@ -0,0 +1,9 @@
1
+ export { default as DocumentAddItemForm } from "./document-add-item-form";
2
+ export { default as DocumentAddItemTaxRateField } from "./document-add-item-tax-rate-field";
3
+ export { DocumentDetailsSection } from "./document-details-section";
4
+ export { DocumentItemsSection } from "./document-items-section";
5
+ export { DocumentRecipientSection } from "./document-recipient-section";
6
+ export { LiveInvoicePreview } from "./live-preview";
7
+ export { MarkAsPaidSection } from "./mark-as-paid-section";
8
+ export { prepareDocumentSubmission } from "./prepare-document-submission";
9
+ export { useDocumentCustomerForm } from "./use-document-customer-form";
@@ -0,0 +1,235 @@
1
+ "use client";
2
+
3
+ import type { CreateInvoiceRequest } from "@spaceinvoices/js-sdk";
4
+ import { Loader2 } from "lucide-react";
5
+ import { useCallback, useEffect, useRef, useState } from "react";
6
+ import { cn } from "@/ui/lib/utils";
7
+ import { useEntities } from "@/ui/providers/entities-context";
8
+ import { useSDK } from "@/ui/providers/sdk-provider";
9
+ import { ScaledDocumentPreview } from "../shared/scaled-document-preview";
10
+ import { useA4Scaling } from "../shared/use-a4-scaling";
11
+ import { filterUnresolvedTaxes } from "./prepare-preview-data";
12
+
13
+ export type PdfTemplateId = "modern" | "classic" | "minimal" | "fashion";
14
+
15
+ type LiveInvoicePreviewProps = {
16
+ data: Partial<CreateInvoiceRequest>;
17
+ currency?: string;
18
+ template?: PdfTemplateId;
19
+ className?: string;
20
+ apiBaseUrl?: string;
21
+ getAuthToken?: () => string | undefined;
22
+ /** Locale for document rendering (e.g., "en-US", "sl-SI"). Uses user's UI language. */
23
+ locale?: string;
24
+ /** Fixed scale to use instead of dynamic scaling. Useful to prevent layout shifts. */
25
+ fixedScale?: number;
26
+ };
27
+
28
+ /**
29
+ * Live Invoice Preview Component
30
+ *
31
+ * Generates a real-time HTML preview of an invoice as the user fills out the form.
32
+ * Uses debouncing to avoid excessive API calls and shows loading states.
33
+ *
34
+ * Features:
35
+ * - Debounced API requests (500ms delay after user stops typing)
36
+ * - Loading skeleton while fetching preview
37
+ * - Error handling with fallback display
38
+ * - Fully styled HTML with scoped CSS (prevents style leakage)
39
+ */
40
+ export function LiveInvoicePreview({
41
+ data,
42
+ currency: _currency = "EUR",
43
+ template,
44
+ className,
45
+ locale,
46
+ fixedScale,
47
+ }: LiveInvoicePreviewProps) {
48
+ const [previewHtml, setPreviewHtml] = useState<string>("");
49
+ const [isLoading, setIsLoading] = useState(false);
50
+ const [error, setError] = useState<string | null>(null);
51
+ const { activeEntity } = useEntities();
52
+ const { sdk } = useSDK();
53
+ const abortControllerRef = useRef<AbortController | null>(null);
54
+ const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
55
+
56
+ const { containerRef, contentRef, scale: dynamicScale, contentHeight, A4_WIDTH_PX } = useA4Scaling(previewHtml);
57
+ const scale = fixedScale ?? dynamicScale;
58
+
59
+ /**
60
+ * Fetch preview from API
61
+ */
62
+ const fetchPreview = useCallback(
63
+ async (invoiceData: Partial<CreateInvoiceRequest>) => {
64
+ // Don't fetch if no items exist (name can be empty for preview)
65
+ if (!invoiceData.items || invoiceData.items.length === 0) {
66
+ setPreviewHtml("");
67
+ return;
68
+ }
69
+
70
+ // Cancel any pending request
71
+ if (abortControllerRef.current) {
72
+ abortControllerRef.current.abort();
73
+ }
74
+
75
+ // Create new abort controller for this request
76
+ const abortController = new AbortController();
77
+ abortControllerRef.current = abortController;
78
+
79
+ setIsLoading(true);
80
+ setError(null);
81
+
82
+ try {
83
+ if (!sdk || !activeEntity?.id) {
84
+ throw new Error("Authentication required");
85
+ }
86
+
87
+ // Prepare preview data with active entity as issuer (if not already set)
88
+ // Exclude 'number' as it's auto-generated by the render endpoint
89
+ const { number: _number, ...invoiceDataWithoutNumber } = invoiceData as any;
90
+ const previewData = {
91
+ ...invoiceDataWithoutNumber,
92
+ // Filter out unresolved tax_ids (race condition: form may add
93
+ // { tax_id: undefined } before the tax dropdown auto-selects a value)
94
+ items: filterUnresolvedTaxes(invoiceData.items),
95
+ issuer: invoiceData.issuer || {
96
+ name: activeEntity.name,
97
+ address: activeEntity.address,
98
+ address_2: activeEntity.address_2,
99
+ post_code: activeEntity.post_code,
100
+ city: activeEntity.city,
101
+ state: activeEntity.state,
102
+ country: activeEntity.country,
103
+ tax_number: activeEntity.tax_number,
104
+ },
105
+ };
106
+
107
+ // Call the render API using the SDK wrapper
108
+ const html = await sdk.invoices.renderInvoicePreview(
109
+ previewData as any,
110
+ { partial: "true", template, locale },
111
+ { entity_id: activeEntity.id },
112
+ );
113
+
114
+ setPreviewHtml(html);
115
+ setError(null);
116
+ } catch (err) {
117
+ // Ignore abort errors (they're expected when user keeps typing)
118
+ if (err instanceof Error && err.name === "AbortError") {
119
+ return;
120
+ }
121
+
122
+ // Ignore 422 validation errors - expected while user is still filling the form
123
+ if (err instanceof Error && "status" in err && (err as any).status === 422) {
124
+ return;
125
+ }
126
+
127
+ setError(err instanceof Error ? err.message : "Failed to generate preview");
128
+ setPreviewHtml("");
129
+ } finally {
130
+ // Only set loading to false if this request wasn't aborted
131
+ if (!abortController.signal.aborted) {
132
+ setIsLoading(false);
133
+ }
134
+ }
135
+ },
136
+ [
137
+ activeEntity?.id,
138
+ activeEntity?.address,
139
+ activeEntity?.address_2,
140
+ activeEntity?.country,
141
+ activeEntity?.state,
142
+ activeEntity?.tax_number,
143
+ activeEntity?.post_code,
144
+ activeEntity?.city,
145
+ activeEntity?.name,
146
+ template,
147
+ locale,
148
+ sdk,
149
+ ],
150
+ );
151
+
152
+ /**
153
+ * Debounced preview fetch
154
+ * Waits 500ms after user stops typing before fetching
155
+ */
156
+ useEffect(() => {
157
+ // Clear previous timeout
158
+ if (debounceTimeoutRef.current) {
159
+ clearTimeout(debounceTimeoutRef.current);
160
+ }
161
+
162
+ // Set new timeout
163
+ debounceTimeoutRef.current = setTimeout(() => {
164
+ fetchPreview(data);
165
+ }, 500);
166
+
167
+ // Cleanup
168
+ return () => {
169
+ if (debounceTimeoutRef.current) {
170
+ clearTimeout(debounceTimeoutRef.current);
171
+ }
172
+ };
173
+ }, [data, fetchPreview]);
174
+
175
+ /**
176
+ * Cleanup on unmount
177
+ */
178
+ useEffect(() => {
179
+ return () => {
180
+ // Cancel any pending request
181
+ if (abortControllerRef.current) {
182
+ abortControllerRef.current.abort();
183
+ }
184
+ // Clear timeout
185
+ if (debounceTimeoutRef.current) {
186
+ clearTimeout(debounceTimeoutRef.current);
187
+ }
188
+ };
189
+ }, []);
190
+
191
+ return (
192
+ <div ref={containerRef} className={cn("relative", className)}>
193
+ {/* Loading overlay */}
194
+ {isLoading && (
195
+ <div className="absolute inset-0 z-10 flex items-center justify-center bg-background/80 backdrop-blur-sm">
196
+ <div className="flex flex-col items-center gap-2">
197
+ <Loader2 className="h-8 w-8 animate-spin text-primary" />
198
+ <p className="text-muted-foreground text-sm">Generating preview...</p>
199
+ </div>
200
+ </div>
201
+ )}
202
+
203
+ {/* Error state */}
204
+ {error && !isLoading && (
205
+ <div className="flex min-h-[200px] items-center justify-center rounded-lg border border-destructive/50 bg-destructive/10 p-8">
206
+ <div className="text-center">
207
+ <p className="font-semibold text-destructive">Preview Error</p>
208
+ <p className="text-muted-foreground text-sm">{error}</p>
209
+ </div>
210
+ </div>
211
+ )}
212
+
213
+ {/* Empty state */}
214
+ {!previewHtml && !isLoading && !error && (
215
+ <div className="flex min-h-[200px] items-center justify-center rounded-lg border border-dashed p-8">
216
+ <div className="text-center">
217
+ <p className="font-semibold text-muted-foreground">Invoice Preview</p>
218
+ <p className="text-muted-foreground text-sm">Start filling the form to see a live preview</p>
219
+ </div>
220
+ </div>
221
+ )}
222
+
223
+ {/* Preview - Scoped HTML injection with A4 scaling */}
224
+ {previewHtml && !error && (
225
+ <ScaledDocumentPreview
226
+ htmlContent={previewHtml}
227
+ scale={scale}
228
+ contentHeight={contentHeight}
229
+ A4_WIDTH_PX={A4_WIDTH_PX}
230
+ contentRef={contentRef}
231
+ />
232
+ )}
233
+ </div>
234
+ );
235
+ }
@@ -0,0 +1,82 @@
1
+ import { HelpCircle } from "lucide-react";
2
+ import { Checkbox } from "@/ui/components/ui/checkbox";
3
+ import { Label } from "@/ui/components/ui/label";
4
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/ui/components/ui/select";
5
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@/ui/components/ui/tooltip";
6
+ import { cn } from "@/ui/lib/utils";
7
+
8
+ // Regular payment types (excluding special types like credit_note and advance)
9
+ const regularPaymentTypes = ["cash", "bank_transfer", "card", "check", "other"] as const;
10
+
11
+ // Labels for payment types (used for translations)
12
+ const PAYMENT_TYPE_LABELS: Record<string, string> = {
13
+ cash: "Cash",
14
+ bank_transfer: "Bank Transfer",
15
+ card: "Card",
16
+ check: "Check",
17
+ other: "Other",
18
+ };
19
+
20
+ type MarkAsPaidSectionProps = {
21
+ /** Whether the document is marked as paid */
22
+ checked: boolean;
23
+ /** Called when the checkbox changes */
24
+ onCheckedChange: (checked: boolean) => void;
25
+ /** Selected payment type */
26
+ paymentType: string;
27
+ /** Called when payment type changes */
28
+ onPaymentTypeChange: (value: string) => void;
29
+ /** Translation function */
30
+ t: (key: string) => string;
31
+ };
32
+
33
+ export function MarkAsPaidSection({
34
+ checked,
35
+ onCheckedChange,
36
+ paymentType,
37
+ onPaymentTypeChange,
38
+ t,
39
+ }: MarkAsPaidSectionProps) {
40
+ return (
41
+ <div className={cn("flex flex-col gap-4 rounded-md border p-4", checked && "md:flex-row md:items-center md:gap-6")}>
42
+ <div className="flex flex-row items-center space-x-3 space-y-0">
43
+ <Checkbox checked={checked} onCheckedChange={(v) => onCheckedChange(v === true)} />
44
+ <div className="flex items-center gap-1 leading-none">
45
+ <Label>{checked ? t("Paid") : t("Mark as Paid")}</Label>
46
+ {!checked && (
47
+ <Tooltip>
48
+ <TooltipTrigger asChild>
49
+ <button
50
+ type="button"
51
+ className="rounded-full p-1 transition-colors hover:bg-accent"
52
+ onClick={(e) => e.preventDefault()}
53
+ >
54
+ <HelpCircle className="size-4 text-muted-foreground" />
55
+ </button>
56
+ </TooltipTrigger>
57
+ <TooltipContent side="top">{t("Invoice will be marked as fully paid upon creation")}</TooltipContent>
58
+ </Tooltip>
59
+ )}
60
+ </div>
61
+ </div>
62
+
63
+ {checked && (
64
+ <>
65
+ <div className="hidden flex-1 md:block" />
66
+ <Select value={paymentType} onValueChange={(v) => v && onPaymentTypeChange(v)}>
67
+ <SelectTrigger className="w-full md:w-fit">
68
+ <SelectValue placeholder={t("Select payment type")}>{t(PAYMENT_TYPE_LABELS[paymentType])}</SelectValue>
69
+ </SelectTrigger>
70
+ <SelectContent>
71
+ {regularPaymentTypes.map((type) => (
72
+ <SelectItem key={type} value={type}>
73
+ {t(PAYMENT_TYPE_LABELS[type])}
74
+ </SelectItem>
75
+ ))}
76
+ </SelectContent>
77
+ </Select>
78
+ </>
79
+ )}
80
+ </div>
81
+ );
82
+ }