@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,132 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { prepareDocumentSubmission } from "./prepare-document-submission";
3
+
4
+ describe("prepareDocumentSubmission", () => {
5
+ describe("isDraft option", () => {
6
+ test("sets is_draft to true when isDraft option is true", () => {
7
+ const values = {
8
+ number: "INV-001",
9
+ date: "2024-01-15",
10
+ items: [],
11
+ };
12
+
13
+ const result = prepareDocumentSubmission(values, {
14
+ originalCustomer: null,
15
+ documentType: "invoice",
16
+ isDraft: true,
17
+ });
18
+
19
+ expect(result.is_draft).toBe(true);
20
+ });
21
+
22
+ test("does not set is_draft when isDraft option is false", () => {
23
+ const values = {
24
+ number: "INV-001",
25
+ date: "2024-01-15",
26
+ items: [],
27
+ };
28
+
29
+ const result = prepareDocumentSubmission(values, {
30
+ originalCustomer: null,
31
+ documentType: "invoice",
32
+ isDraft: false,
33
+ });
34
+
35
+ expect(result.is_draft).toBeUndefined();
36
+ });
37
+
38
+ test("does not set is_draft when isDraft option is not provided", () => {
39
+ const values = {
40
+ number: "INV-001",
41
+ date: "2024-01-15",
42
+ items: [],
43
+ };
44
+
45
+ const result = prepareDocumentSubmission(values, {
46
+ originalCustomer: null,
47
+ documentType: "invoice",
48
+ });
49
+
50
+ expect(result.is_draft).toBeUndefined();
51
+ });
52
+
53
+ test("isDraft works with all document types", () => {
54
+ const documentTypes = ["invoice", "estimate", "credit_note", "advance_invoice"] as const;
55
+
56
+ for (const docType of documentTypes) {
57
+ const values = {
58
+ date: "2024-01-15",
59
+ items: [],
60
+ };
61
+
62
+ const result = prepareDocumentSubmission(values, {
63
+ originalCustomer: null,
64
+ documentType: docType,
65
+ isDraft: true,
66
+ });
67
+
68
+ expect(result.is_draft).toBe(true);
69
+ }
70
+ });
71
+
72
+ test("isDraft preserves other payload fields", () => {
73
+ const values = {
74
+ number: "INV-001", // number is intentionally removed by prepareDocumentSubmission
75
+ date: "2024-01-15",
76
+ items: [{ name: "Item 1", quantity: 1, price: 100 }],
77
+ note: "Test note",
78
+ currency_code: "EUR",
79
+ };
80
+
81
+ const result = prepareDocumentSubmission(values, {
82
+ originalCustomer: null,
83
+ documentType: "invoice",
84
+ isDraft: true,
85
+ secondaryDate: "2024-02-15",
86
+ });
87
+
88
+ expect(result.is_draft).toBe(true);
89
+ expect(result.number).toBeUndefined(); // number is always server-generated
90
+ expect(result.note).toBe("Test note");
91
+ expect(result.currency_code).toBe("EUR");
92
+ expect(result.items).toHaveLength(1);
93
+ expect(result.date_due).toBeInstanceOf(Date);
94
+ });
95
+
96
+ test("isDraft works with customer data", () => {
97
+ const values = {
98
+ date: "2024-01-15",
99
+ customer_id: "cust-123",
100
+ items: [],
101
+ };
102
+
103
+ const result = prepareDocumentSubmission(values, {
104
+ originalCustomer: null,
105
+ documentType: "invoice",
106
+ isDraft: true,
107
+ });
108
+
109
+ expect(result.is_draft).toBe(true);
110
+ expect(result.customer_id).toBe("cust-123");
111
+ });
112
+
113
+ test("isDraft works with markAsPaid (both can be set)", () => {
114
+ const values = {
115
+ date: "2024-01-15",
116
+ items: [],
117
+ };
118
+
119
+ const result = prepareDocumentSubmission(values, {
120
+ originalCustomer: null,
121
+ documentType: "invoice",
122
+ isDraft: true,
123
+ markAsPaid: true,
124
+ paymentType: "cash",
125
+ });
126
+
127
+ // Both flags can technically be set, though the form logic prevents this
128
+ expect(result.is_draft).toBe(true);
129
+ expect(result.payment).toBeDefined();
130
+ });
131
+ });
132
+ });
@@ -0,0 +1,187 @@
1
+ type CustomerData = {
2
+ name?: string | null;
3
+ address?: string | null;
4
+ address_2?: string | null;
5
+ post_code?: string | null;
6
+ city?: string | null;
7
+ state?: string | null;
8
+ country?: string | null;
9
+ tax_number?: string | null;
10
+ save_customer?: boolean;
11
+ };
12
+
13
+ type BaseDocumentValues = {
14
+ number?: string;
15
+ date?: string;
16
+ customer_id?: string | null;
17
+ customer?: CustomerData | null;
18
+
19
+ items?: any[];
20
+ currency_code?: string;
21
+ };
22
+
23
+ /** Map of item index to gross price mode */
24
+ type PriceModesMap = Record<number, boolean>;
25
+
26
+ type PrepareDocumentOptions = {
27
+ /** Original customer data for comparison (to detect modifications) */
28
+ originalCustomer: CustomerData | null;
29
+ /** Whether the customer form was shown to the user */
30
+ wasCustomerFormShown?: boolean;
31
+ /** For invoices/credit notes: whether to mark as paid */
32
+ markAsPaid?: boolean;
33
+ /** For invoices/credit notes: payment type when markAsPaid is true */
34
+ paymentType?: string;
35
+ /** Document type for specific date handling */
36
+ documentType: "invoice" | "estimate" | "credit_note" | "advance_invoice";
37
+ /** Secondary date field value (date_due for invoices, date_valid_till for estimates) */
38
+ secondaryDate?: string;
39
+ /** Map of item index to gross price mode (collected from component state) */
40
+ priceModes?: PriceModesMap;
41
+ /** Whether to save as draft (skips numbering and fiscalization) */
42
+ isDraft?: boolean;
43
+ };
44
+
45
+ /**
46
+ * Prepares document form data for API submission.
47
+ * Handles customer data transformation and payment data for all document types.
48
+ *
49
+ * This is a shared utility for invoices, estimates, and credit notes.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * // For invoice
54
+ * const payload = prepareDocumentSubmission(values, {
55
+ * originalCustomer,
56
+ * wasCustomerFormShown: showCustomerForm,
57
+ * markAsPaid: values.markAsPaid,
58
+ * paymentType: values.paymentType,
59
+ * documentType: "invoice",
60
+ * secondaryDate: values.date_due,
61
+ * });
62
+ *
63
+ * // For estimate
64
+ * const payload = prepareDocumentSubmission(values, {
65
+ * originalCustomer,
66
+ * documentType: "estimate",
67
+ * secondaryDate: values.date_valid_till,
68
+ * });
69
+ * ```
70
+ */
71
+ export function prepareDocumentSubmission<T extends BaseDocumentValues>(
72
+ values: T,
73
+ options: PrepareDocumentOptions,
74
+ ): any {
75
+ // Document numbers are always auto-generated by the server
76
+ // Remove number from payload (even if form provides a preview value)
77
+
78
+ // Handle customer logic
79
+ if (values.customer_id && values.customer) {
80
+ // If customer form was not shown, remove customer data (keep only customer_id)
81
+ if (options.wasCustomerFormShown === false) {
82
+ delete values.customer;
83
+ } else {
84
+ // Existing customer loaded - check if data was actually modified
85
+ const customerChanged =
86
+ options.originalCustomer && JSON.stringify(values.customer) !== JSON.stringify(options.originalCustomer);
87
+
88
+ if (!customerChanged) {
89
+ // No changes - send only customer_id
90
+ delete values.customer;
91
+ } else {
92
+ // Changes detected - clean null/empty values and send with save_customer flag
93
+ const cleanedCustomer: any = { save_customer: true };
94
+ for (const [key, value] of Object.entries(values.customer)) {
95
+ if (key !== "save_customer" && value !== "" && value !== null && value !== undefined) {
96
+ cleanedCustomer[key] = value;
97
+ }
98
+ }
99
+ values.customer = cleanedCustomer;
100
+ }
101
+ }
102
+ } else if (values.customer) {
103
+ // New inline customer - clean null/empty values and add save flag
104
+ const cleanedCustomer: any = { save_customer: true };
105
+ let hasAnyValue = false;
106
+
107
+ for (const [key, value] of Object.entries(values.customer)) {
108
+ if (key !== "save_customer" && value !== "" && value !== null && value !== undefined) {
109
+ cleanedCustomer[key] = value;
110
+ hasAnyValue = true;
111
+ }
112
+ }
113
+
114
+ if (!hasAnyValue) {
115
+ delete values.customer;
116
+ } else {
117
+ values.customer = cleanedCustomer;
118
+ }
119
+ }
120
+
121
+ // Clean up customer_id if empty
122
+ if (!values.customer_id) {
123
+ delete values.customer_id;
124
+ }
125
+
126
+ // Clean up taxes and handle gross price transformation
127
+ if (values.items) {
128
+ const priceModes = options.priceModes ?? {};
129
+ values.items = values.items.map((item: any, index: number) => {
130
+ const { price, ...rest } = item;
131
+
132
+ // Transform price based on price mode (from component state, not form)
133
+ const isGrossPrice = priceModes[index] ?? false;
134
+ const priceFields = isGrossPrice ? { gross_price: price } : { price };
135
+
136
+ return {
137
+ ...rest,
138
+ ...priceFields,
139
+ taxes: item.taxes?.map((tax: any) => {
140
+ if (tax.tax_id) {
141
+ // Only send tax_id, API will resolve the rate
142
+ return { tax_id: tax.tax_id };
143
+ }
144
+ return tax;
145
+ }),
146
+ };
147
+ });
148
+ }
149
+
150
+ // Build payload with date conversions
151
+ // Destructure to exclude fields we handle explicitly (number is always server-generated)
152
+ const { number: _number, note, payment_terms, ...restValues } = values as any;
153
+ const payload: any = {
154
+ ...restValues,
155
+ ...(note?.trim() && { note: note.trim() }),
156
+ // Advance invoices don't have payment terms - they are documents requesting payment
157
+ ...(options.documentType !== "advance_invoice" && payment_terms?.trim() && { payment_terms: payment_terms.trim() }),
158
+ date: values.date ? new Date(values.date) : undefined,
159
+ };
160
+
161
+ // Add secondary date field based on document type
162
+ if ((options.documentType === "invoice" || options.documentType === "advance_invoice") && options.secondaryDate) {
163
+ payload.date_due = new Date(options.secondaryDate);
164
+ } else if (options.documentType === "estimate" && options.secondaryDate) {
165
+ payload.date_valid_till = new Date(options.secondaryDate);
166
+ }
167
+ // Credit notes don't have a secondary date field
168
+
169
+ // Handle markAsPaid for invoices and credit notes
170
+ if (options.documentType !== "estimate" && options.markAsPaid) {
171
+ payload.payment = {
172
+ type: options.paymentType || "bank_transfer",
173
+ date: payload.date ?? undefined,
174
+ };
175
+ }
176
+
177
+ // Remove UI-only fields from payload
178
+ delete payload.markAsPaid;
179
+ delete payload.paymentType;
180
+
181
+ // Add draft flag if requested
182
+ if (options.isDraft) {
183
+ payload.is_draft = true;
184
+ }
185
+
186
+ return payload;
187
+ }
@@ -0,0 +1,155 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { filterUnresolvedTaxes } from "./prepare-preview-data";
3
+
4
+ describe("filterUnresolvedTaxes", () => {
5
+ test("returns empty array for undefined items", () => {
6
+ const result = filterUnresolvedTaxes(undefined);
7
+ expect(result).toEqual([]);
8
+ });
9
+
10
+ test("returns empty array for empty items", () => {
11
+ const result = filterUnresolvedTaxes([]);
12
+ expect(result).toEqual([]);
13
+ });
14
+
15
+ test("preserves items without taxes", () => {
16
+ const items = [{ name: "Item 1", quantity: 1, price: 100 }];
17
+ const result = filterUnresolvedTaxes(items as any);
18
+
19
+ expect(result).toHaveLength(1);
20
+ expect(result[0].name).toBe("Item 1");
21
+ expect(result[0].taxes).toEqual([]);
22
+ });
23
+
24
+ test("preserves items with empty taxes array", () => {
25
+ const items = [{ name: "Item 1", quantity: 1, price: 100, taxes: [] }];
26
+ const result = filterUnresolvedTaxes(items as any);
27
+
28
+ expect(result).toHaveLength(1);
29
+ expect(result[0].taxes).toEqual([]);
30
+ });
31
+
32
+ test("filters out taxes with undefined tax_id", () => {
33
+ const items = [
34
+ {
35
+ name: "Item 1",
36
+ quantity: 1,
37
+ price: 100,
38
+ taxes: [{ tax_id: undefined }],
39
+ },
40
+ ];
41
+ const result = filterUnresolvedTaxes(items as any);
42
+
43
+ expect(result).toHaveLength(1);
44
+ expect(result[0].taxes).toEqual([]);
45
+ });
46
+
47
+ test("filters out taxes with null tax_id", () => {
48
+ const items = [
49
+ {
50
+ name: "Item 1",
51
+ quantity: 1,
52
+ price: 100,
53
+ taxes: [{ tax_id: null }],
54
+ },
55
+ ];
56
+ const result = filterUnresolvedTaxes(items as any);
57
+
58
+ expect(result).toHaveLength(1);
59
+ expect(result[0].taxes).toEqual([]);
60
+ });
61
+
62
+ test("preserves taxes with valid tax_id", () => {
63
+ const items = [
64
+ {
65
+ name: "Item 1",
66
+ quantity: 1,
67
+ price: 100,
68
+ taxes: [{ tax_id: "tax-123" }],
69
+ },
70
+ ];
71
+ const result = filterUnresolvedTaxes(items as any);
72
+
73
+ expect(result).toHaveLength(1);
74
+ expect(result[0].taxes).toEqual([{ tax_id: "tax-123" }]);
75
+ });
76
+
77
+ test("filters mixed valid and invalid taxes", () => {
78
+ const items = [
79
+ {
80
+ name: "Item 1",
81
+ quantity: 1,
82
+ price: 100,
83
+ taxes: [{ tax_id: undefined }, { tax_id: "tax-123" }, { tax_id: null }],
84
+ },
85
+ ];
86
+ const result = filterUnresolvedTaxes(items as any);
87
+
88
+ expect(result).toHaveLength(1);
89
+ expect(result[0].taxes).toEqual([{ tax_id: "tax-123" }]);
90
+ });
91
+
92
+ test("handles multiple items with different tax states", () => {
93
+ const items = [
94
+ {
95
+ name: "Item 1",
96
+ quantity: 1,
97
+ price: 100,
98
+ taxes: [{ tax_id: undefined }],
99
+ },
100
+ {
101
+ name: "Item 2",
102
+ quantity: 2,
103
+ price: 200,
104
+ taxes: [{ tax_id: "tax-456" }],
105
+ },
106
+ {
107
+ name: "Item 3",
108
+ quantity: 3,
109
+ price: 300,
110
+ taxes: [],
111
+ },
112
+ ];
113
+ const result = filterUnresolvedTaxes(items as any);
114
+
115
+ expect(result).toHaveLength(3);
116
+ expect(result[0].taxes).toEqual([]);
117
+ expect(result[1].taxes).toEqual([{ tax_id: "tax-456" }]);
118
+ expect(result[2].taxes).toEqual([]);
119
+ });
120
+
121
+ test("preserves other item properties", () => {
122
+ const items = [
123
+ {
124
+ name: "Test Item",
125
+ description: "A test description",
126
+ quantity: 5,
127
+ price: 99.99,
128
+ unit: "pcs",
129
+ taxes: [{ tax_id: "tax-123", rate: 22 }],
130
+ },
131
+ ];
132
+ const result = filterUnresolvedTaxes(items as any);
133
+
134
+ expect(result[0].name).toBe("Test Item");
135
+ expect(result[0].description).toBe("A test description");
136
+ expect(result[0].quantity).toBe(5);
137
+ expect(result[0].price).toBe(99.99);
138
+ expect(result[0].unit).toBe("pcs");
139
+ expect(result[0].taxes).toEqual([{ tax_id: "tax-123", rate: 22 }]);
140
+ });
141
+
142
+ test("preserves tax properties other than tax_id when valid", () => {
143
+ const items = [
144
+ {
145
+ name: "Item 1",
146
+ quantity: 1,
147
+ price: 100,
148
+ taxes: [{ tax_id: "tax-123", rate: 22, name: "VAT" }],
149
+ },
150
+ ];
151
+ const result = filterUnresolvedTaxes(items as any);
152
+
153
+ expect(result[0].taxes).toEqual([{ tax_id: "tax-123", rate: 22, name: "VAT" }]);
154
+ });
155
+ });
@@ -0,0 +1,16 @@
1
+ import type { CreateInvoiceRequest } from "@spaceinvoices/js-sdk";
2
+
3
+ type ItemWithTaxes = NonNullable<CreateInvoiceRequest["items"]>[number];
4
+
5
+ /**
6
+ * Filters out items with unresolved tax_ids before sending to preview API.
7
+ *
8
+ * This handles a race condition where the form may add `{ tax_id: undefined }`
9
+ * for tax subject entities before the tax dropdown has auto-selected a value.
10
+ */
11
+ export function filterUnresolvedTaxes(items: ItemWithTaxes[] | undefined): ItemWithTaxes[] {
12
+ return (items || []).map((item) => ({
13
+ ...item,
14
+ taxes: (item.taxes || []).filter((tax) => tax.tax_id != null),
15
+ }));
16
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Smart Code Insert Button
3
+ * Dropdown button for inserting template variables into a textarea at cursor position
4
+ */
5
+ import { Sparkles } from "lucide-react";
6
+ import { useCallback, useRef } from "react";
7
+ import { Button } from "@/ui/components/ui/button";
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuGroup,
12
+ DropdownMenuItem,
13
+ DropdownMenuLabel,
14
+ DropdownMenuSeparator,
15
+ DropdownMenuTrigger,
16
+ } from "@/ui/components/ui/dropdown-menu";
17
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/ui/components/ui/tooltip";
18
+
19
+ const TEMPLATE_VARIABLES = [
20
+ {
21
+ category: "Entity",
22
+ variables: [
23
+ { code: "{entity_name}", label: "Company name" },
24
+ { code: "{entity_email}", label: "Email address" },
25
+ ],
26
+ },
27
+ {
28
+ category: "Document",
29
+ variables: [
30
+ { code: "{document_number}", label: "Invoice number" },
31
+ { code: "{document_date}", label: "Invoice date" },
32
+ { code: "{document_due_date}", label: "Due date" },
33
+ { code: "{document_total}", label: "Total amount" },
34
+ { code: "{document_currency}", label: "Currency" },
35
+ ],
36
+ },
37
+ {
38
+ category: "Customer",
39
+ variables: [
40
+ { code: "{customer_name}", label: "Customer name" },
41
+ { code: "{customer_email}", label: "Customer email" },
42
+ ],
43
+ },
44
+ {
45
+ category: "Bank Account",
46
+ variables: [
47
+ { code: "{bank_account}", label: "Full account info" },
48
+ { code: "{bank_account.iban}", label: "IBAN" },
49
+ { code: "{bank_account.bank_name}", label: "Bank name" },
50
+ { code: "{bank_account.bic}", label: "BIC/SWIFT" },
51
+ { code: "{bank_account.account_number}", label: "Account number" },
52
+ ],
53
+ },
54
+ {
55
+ category: "Other",
56
+ variables: [
57
+ { code: "{current_date}", label: "Today's date" },
58
+ { code: "{current_year}", label: "Current year" },
59
+ ],
60
+ },
61
+ ];
62
+
63
+ interface SmartCodeInsertButtonProps {
64
+ /** Reference to the textarea element */
65
+ textareaRef: React.RefObject<HTMLTextAreaElement | null>;
66
+ /** Callback when a variable is inserted - receives the new value */
67
+ onInsert: (newValue: string) => void;
68
+ /** Current value of the textarea */
69
+ value: string;
70
+ /** Translation function */
71
+ t: (key: string) => string;
72
+ }
73
+
74
+ export function SmartCodeInsertButton({ textareaRef, onInsert, value, t }: SmartCodeInsertButtonProps) {
75
+ // Store cursor position for when textarea loses focus before dropdown opens
76
+ const cursorPositionRef = useRef<number>(0);
77
+
78
+ const insertVariable = useCallback(
79
+ (code: string) => {
80
+ const textarea = textareaRef.current;
81
+ const currentValue = value || "";
82
+ const cursorPos = textarea?.selectionStart ?? cursorPositionRef.current ?? currentValue.length;
83
+
84
+ // Insert the code at cursor position
85
+ const newValue = currentValue.slice(0, cursorPos) + code + currentValue.slice(cursorPos);
86
+ onInsert(newValue);
87
+
88
+ // Restore focus and set cursor after inserted text
89
+ requestAnimationFrame(() => {
90
+ if (textarea) {
91
+ textarea.focus();
92
+ const newCursorPos = cursorPos + code.length;
93
+ textarea.setSelectionRange(newCursorPos, newCursorPos);
94
+ }
95
+ });
96
+ },
97
+ [textareaRef, value, onInsert],
98
+ );
99
+
100
+ return (
101
+ <DropdownMenu>
102
+ <TooltipProvider>
103
+ <Tooltip>
104
+ <TooltipTrigger asChild>
105
+ <DropdownMenuTrigger asChild>
106
+ <Button type="button" variant="ghost" size="sm" className="h-8 w-8 p-0">
107
+ <Sparkles className="h-4 w-4" />
108
+ <span className="sr-only">{t("Insert variable")}</span>
109
+ </Button>
110
+ </DropdownMenuTrigger>
111
+ </TooltipTrigger>
112
+ <TooltipContent side="top">
113
+ <p>{t("Insert variable")}</p>
114
+ </TooltipContent>
115
+ </Tooltip>
116
+ </TooltipProvider>
117
+ <DropdownMenuContent align="end" className="w-72">
118
+ {TEMPLATE_VARIABLES.map((group, groupIndex) => (
119
+ <div key={group.category}>
120
+ {groupIndex > 0 && <DropdownMenuSeparator />}
121
+ <DropdownMenuLabel className="text-muted-foreground text-xs">{t(group.category)}</DropdownMenuLabel>
122
+ <DropdownMenuGroup>
123
+ {group.variables.map((variable) => (
124
+ <DropdownMenuItem
125
+ key={variable.code}
126
+ onClick={() => insertVariable(variable.code)}
127
+ className="flex cursor-pointer items-center whitespace-nowrap"
128
+ >
129
+ <code className="mr-2 shrink-0 rounded bg-muted px-1 py-0.5 font-mono text-xs">{variable.code}</code>
130
+ <span className="text-muted-foreground text-xs">{t(variable.label)}</span>
131
+ </DropdownMenuItem>
132
+ ))}
133
+ </DropdownMenuGroup>
134
+ </div>
135
+ ))}
136
+ </DropdownMenuContent>
137
+ </DropdownMenu>
138
+ );
139
+ }