@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,300 @@
1
+ import type SDK from "@spaceinvoices/js-sdk";
2
+ import type { UseMutationOptions } from "@tanstack/react-query";
3
+
4
+ import { useQueryClient } from "@tanstack/react-query";
5
+ import { useResourceMutation } from "./use-resource-mutation";
6
+
7
+ /**
8
+ * Options for resource mutation hooks
9
+ */
10
+ type ResourceMutationHookOptions<TData, TError, TVariables = any> = {
11
+ entityId?: string | null;
12
+ accountId?: string | null;
13
+ onSuccess?: (data: TData, variables: TVariables, context: unknown) => void;
14
+ onError?: (error: TError, variables: TVariables, context: unknown) => void;
15
+ mutationOptions?: Omit<UseMutationOptions<TData, TError, any, unknown>, "mutationFn" | "onSuccess" | "onError">;
16
+ };
17
+
18
+ /**
19
+ * Build query key with optional filters
20
+ */
21
+ function buildQueryKey(
22
+ baseKey: string,
23
+ filters?: { entityId?: string | null; accountId?: string | null },
24
+ ): [string, Record<string, string>?] {
25
+ if (!filters?.entityId && !filters?.accountId) {
26
+ return [baseKey];
27
+ }
28
+
29
+ const params: Record<string, string> = {};
30
+ if (filters.entityId) params.entityId = filters.entityId;
31
+ if (filters.accountId) params.accountId = filters.accountId;
32
+
33
+ return [baseKey, params];
34
+ }
35
+
36
+ /**
37
+ * Invalidate resource-specific queries
38
+ */
39
+ function invalidateResourceQueries(
40
+ queryClient: ReturnType<typeof useQueryClient>,
41
+ cacheKey: string,
42
+ resourceId: string,
43
+ filters?: { entityId?: string | null; accountId?: string | null },
44
+ ): void {
45
+ const detailKey = `${cacheKey}-${resourceId}`;
46
+ queryClient.invalidateQueries({ queryKey: buildQueryKey(detailKey, filters) });
47
+ }
48
+
49
+ /**
50
+ * Factory function to create resource-specific CRUD hooks
51
+ *
52
+ * @param resourceName - The name of the resource in the SDK (e.g., 'customers')
53
+ * @param cacheKey - The cache key for the resource queries
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * const { useCreateResource, useUpdateResource, useDeleteResource } =
58
+ * createResourceHooks<Customer, CreateCustomerRequest['data']>('customers', 'customers');
59
+ *
60
+ * // Usage
61
+ * const createCustomer = useCreateResource();
62
+ * ```
63
+ */
64
+ export function createResourceHooks<
65
+ TResource extends { id: string },
66
+ TCreateData = unknown,
67
+ TUpdateData = Partial<Omit<TResource, "id">>,
68
+ >(resourceName: keyof SDK, cacheKey: string) {
69
+ /**
70
+ * Hook for creating a new resource
71
+ */
72
+ function useCreateResource<TError = Error>(
73
+ options: ResourceMutationHookOptions<TResource, TError, TCreateData> = {},
74
+ ) {
75
+ const queryClient = useQueryClient();
76
+
77
+ return useResourceMutation<TResource, TError, TCreateData>({
78
+ resourceName,
79
+ methodName: "create",
80
+ cacheKey,
81
+ entityId: options.entityId,
82
+ accountId: options.accountId,
83
+ mutationOptions: {
84
+ onMutate: async (variables) => {
85
+ // Cancel any outgoing refetches to avoid overwriting optimistic update
86
+ const listQueryKey = buildQueryKey(cacheKey, {
87
+ entityId: options.entityId,
88
+ accountId: options.accountId,
89
+ });
90
+ await queryClient.cancelQueries({ queryKey: listQueryKey });
91
+
92
+ // Snapshot the previous value
93
+ const previousData = queryClient.getQueryData(listQueryKey);
94
+
95
+ // Optimistically add the new resource to the cache
96
+ queryClient.setQueryData(listQueryKey, (oldData: any) => {
97
+ if (!oldData?.data) return oldData;
98
+
99
+ // Create temporary resource for optimistic update
100
+ // Use 'unknown' as intermediate to safely cast
101
+ const tempResource = {
102
+ ...variables,
103
+ id: `temp-${Date.now()}`,
104
+ created_at: new Date().toISOString(),
105
+ updated_at: new Date().toISOString(),
106
+ } as unknown as TResource;
107
+
108
+ return {
109
+ ...oldData,
110
+ data: [tempResource, ...oldData.data],
111
+ pagination: oldData.pagination
112
+ ? {
113
+ ...oldData.pagination,
114
+ total: oldData.pagination.total + 1,
115
+ }
116
+ : undefined,
117
+ };
118
+ });
119
+
120
+ return { previousData };
121
+ },
122
+ onSuccess: options.onSuccess,
123
+ onError: (error, variables, context: any) => {
124
+ // Rollback on error
125
+ if (context?.previousData) {
126
+ const listQueryKey = buildQueryKey(cacheKey, {
127
+ entityId: options.entityId,
128
+ accountId: options.accountId,
129
+ });
130
+ queryClient.setQueryData(listQueryKey, context.previousData);
131
+ }
132
+ options.onError?.(error, variables, context);
133
+ },
134
+ onSettled: () => {
135
+ // Always refetch after error or success to ensure consistency
136
+ const listQueryKey = buildQueryKey(cacheKey, {
137
+ entityId: options.entityId,
138
+ accountId: options.accountId,
139
+ });
140
+ queryClient.invalidateQueries({ queryKey: listQueryKey });
141
+ },
142
+ ...options.mutationOptions,
143
+ },
144
+ });
145
+ }
146
+
147
+ /**
148
+ * Hook for updating an existing resource
149
+ */
150
+ function useUpdateResource<TError = Error>(
151
+ options: ResourceMutationHookOptions<TResource, TError, { id: string; data: TUpdateData }> = {},
152
+ ) {
153
+ const queryClient = useQueryClient();
154
+
155
+ return useResourceMutation<TResource, TError, { id: string; data: TUpdateData }>({
156
+ resourceName,
157
+ methodName: "update",
158
+ cacheKey: [cacheKey, `${cacheKey}-detail`],
159
+ entityId: options.entityId,
160
+ accountId: options.accountId,
161
+ mutationOptions: {
162
+ onMutate: async (variables) => {
163
+ const listQueryKey = buildQueryKey(cacheKey, {
164
+ entityId: options.entityId,
165
+ accountId: options.accountId,
166
+ });
167
+ const detailKey = `${cacheKey}-${variables.id}`;
168
+ const detailQueryKey = buildQueryKey(detailKey, {
169
+ entityId: options.entityId,
170
+ accountId: options.accountId,
171
+ });
172
+
173
+ // Cancel outgoing refetches
174
+ await queryClient.cancelQueries({ queryKey: listQueryKey });
175
+ await queryClient.cancelQueries({ queryKey: detailQueryKey });
176
+
177
+ // Snapshot previous values
178
+ const previousListData = queryClient.getQueryData(listQueryKey);
179
+ const previousDetailData = queryClient.getQueryData(detailQueryKey);
180
+
181
+ // Optimistically update list cache
182
+ queryClient.setQueryData(listQueryKey, (oldData: any) => {
183
+ if (!oldData?.data) return oldData;
184
+
185
+ return {
186
+ ...oldData,
187
+ data: oldData.data.map((resource: TResource) =>
188
+ resource.id === variables.id
189
+ ? {
190
+ ...resource,
191
+ ...variables.data,
192
+ updated_at: new Date().toISOString(),
193
+ }
194
+ : resource,
195
+ ),
196
+ };
197
+ });
198
+
199
+ // Optimistically update detail cache
200
+ queryClient.setQueryData(detailQueryKey, (oldData: any) => {
201
+ if (!oldData) return oldData;
202
+ return {
203
+ ...oldData,
204
+ ...variables.data,
205
+ updated_at: new Date().toISOString(),
206
+ };
207
+ });
208
+
209
+ return { previousListData, previousDetailData };
210
+ },
211
+ onSuccess: (data, variables, context) => {
212
+ // Invalidate the specific resource detail cache
213
+ invalidateResourceQueries(queryClient, cacheKey, variables.id, {
214
+ entityId: options.entityId,
215
+ accountId: options.accountId,
216
+ });
217
+
218
+ options.onSuccess?.(data, variables, context);
219
+ },
220
+ onError: (error, variables, context: any) => {
221
+ // Rollback on error
222
+ if (context?.previousListData) {
223
+ const listQueryKey = buildQueryKey(cacheKey, {
224
+ entityId: options.entityId,
225
+ accountId: options.accountId,
226
+ });
227
+ queryClient.setQueryData(listQueryKey, context.previousListData);
228
+ }
229
+ if (context?.previousDetailData) {
230
+ const detailKey = `${cacheKey}-${variables.id}`;
231
+ const detailQueryKey = buildQueryKey(detailKey, {
232
+ entityId: options.entityId,
233
+ accountId: options.accountId,
234
+ });
235
+ queryClient.setQueryData(detailQueryKey, context.previousDetailData);
236
+ }
237
+ options.onError?.(error, variables, context);
238
+ },
239
+ onSettled: () => {
240
+ // Always refetch to ensure consistency
241
+ const listQueryKey = buildQueryKey(cacheKey, {
242
+ entityId: options.entityId,
243
+ accountId: options.accountId,
244
+ });
245
+ queryClient.invalidateQueries({ queryKey: listQueryKey });
246
+ },
247
+ ...options.mutationOptions,
248
+ },
249
+ });
250
+ }
251
+
252
+ /**
253
+ * Hook for deleting a resource
254
+ */
255
+ function useDeleteResource<TError = Error>(options: ResourceMutationHookOptions<void, TError, { id: string }> = {}) {
256
+ const queryClient = useQueryClient();
257
+
258
+ return useResourceMutation<void, TError, { id: string }>({
259
+ resourceName,
260
+ methodName: "delete",
261
+ cacheKey,
262
+ entityId: options.entityId,
263
+ accountId: options.accountId,
264
+ mutationOptions: {
265
+ onSuccess: (data, variables, context) => {
266
+ // Invalidate the specific resource detail cache
267
+ invalidateResourceQueries(queryClient, cacheKey, variables.id, {
268
+ entityId: options.entityId,
269
+ accountId: options.accountId,
270
+ });
271
+
272
+ // Optimistically remove from list cache
273
+ const listQueryKey = buildQueryKey(cacheKey, {
274
+ entityId: options.entityId,
275
+ accountId: options.accountId,
276
+ });
277
+
278
+ queryClient.setQueriesData({ queryKey: listQueryKey }, (oldData: any) => {
279
+ if (!oldData?.data) return oldData;
280
+
281
+ return {
282
+ ...oldData,
283
+ data: oldData.data.filter((resource: TResource) => resource.id !== variables.id),
284
+ };
285
+ });
286
+
287
+ options.onSuccess?.(data, variables, context);
288
+ },
289
+ onError: options.onError,
290
+ ...options.mutationOptions,
291
+ },
292
+ });
293
+ }
294
+
295
+ return {
296
+ useCreateResource,
297
+ useUpdateResource,
298
+ useDeleteResource,
299
+ };
300
+ }
@@ -0,0 +1,12 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ export function useDebounce<T>(value: T, delay: number): T {
4
+ const [debouncedValue, setDebouncedValue] = useState<T>(value);
5
+
6
+ useEffect(() => {
7
+ const timer = setTimeout(() => setDebouncedValue(value), delay);
8
+ return () => clearTimeout(timer);
9
+ }, [value, delay]);
10
+
11
+ return debouncedValue;
12
+ }
@@ -0,0 +1,185 @@
1
+ import type {
2
+ AdvanceInvoiceWithItems,
3
+ CreateAdvanceInvoiceRequest,
4
+ CreateCreditNoteRequest,
5
+ CreateEstimateRequest,
6
+ CreateInvoiceRequest,
7
+ CreditNoteWithItems,
8
+ EstimateWithItems,
9
+ InvoiceWithItems,
10
+ } from "@spaceinvoices/js-sdk";
11
+ import { useQuery } from "@tanstack/react-query";
12
+
13
+ import { useEntities } from "@/ui/providers/entities-context";
14
+ import { useSDK } from "@/ui/providers/sdk-provider";
15
+
16
+ export type DocumentType = "invoice" | "estimate" | "credit_note" | "advance_invoice";
17
+ type Document = InvoiceWithItems | EstimateWithItems | CreditNoteWithItems | AdvanceInvoiceWithItems;
18
+ type CreateRequest =
19
+ | CreateInvoiceRequest
20
+ | CreateEstimateRequest
21
+ | CreateCreditNoteRequest
22
+ | CreateAdvanceInvoiceRequest;
23
+
24
+ /**
25
+ * Get document type from ID prefix
26
+ */
27
+ export function getDocumentTypeFromId(id: string): DocumentType | null {
28
+ if (id.startsWith("inv_")) return "invoice";
29
+ if (id.startsWith("est_")) return "estimate";
30
+ if (id.startsWith("cn_")) return "credit_note";
31
+ if (id.startsWith("adv_")) return "advance_invoice";
32
+ return null;
33
+ }
34
+
35
+ /**
36
+ * Get allowed target types for duplication/conversion
37
+ */
38
+ export function getAllowedDuplicateTargets(sourceType: DocumentType): DocumentType[] {
39
+ switch (sourceType) {
40
+ case "invoice":
41
+ return ["invoice", "credit_note"];
42
+ case "estimate":
43
+ return ["estimate", "invoice"];
44
+ case "credit_note":
45
+ return ["credit_note"];
46
+ case "advance_invoice":
47
+ return ["advance_invoice", "invoice"];
48
+ default:
49
+ return [];
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Transform a source document into form-compatible initial values
55
+ * Copies relevant fields and resets computed/generated ones
56
+ */
57
+ function transformDocumentForDuplication(source: Document, _targetType: DocumentType): Partial<CreateRequest> {
58
+ // Transform items - copy only the fields needed for creation
59
+ const items = source.items?.map((item: (typeof source.items)[number]) => ({
60
+ name: item.name,
61
+ description: item.description,
62
+ quantity: item.quantity,
63
+ // Use gross_price if set, otherwise use price. The form uses is_gross_price as a UI toggle.
64
+ price: (item as { gross_price?: number }).gross_price ?? item.price,
65
+ // Copy tax references (tax_id), not computed tax data
66
+ taxes: item.taxes?.map((tax: { tax_id?: string }) => ({ tax_id: tax.tax_id })),
67
+ // Derive is_gross_price from whether gross_price is set
68
+ gross_price: (item as { gross_price?: number }).gross_price,
69
+ }));
70
+
71
+ // Build customer data - always copy if available (form needs this for display)
72
+ const customerData = source.customer
73
+ ? {
74
+ name: source.customer.name,
75
+ address: source.customer.address,
76
+ address_2: source.customer.address_2,
77
+ city: source.customer.city,
78
+ post_code: source.customer.post_code,
79
+ country: source.customer.country,
80
+ country_code: source.customer.country_code,
81
+ tax_number: source.customer.tax_number,
82
+ email: source.customer.email,
83
+ }
84
+ : undefined;
85
+
86
+ // Build base duplicate data
87
+ const baseData: Partial<CreateRequest> = {
88
+ // Customer - always pass both customer_id AND customer data when available
89
+ // The form needs customer data for display, even when customer_id is set
90
+ ...(source.customer_id ? { customer_id: source.customer_id } : {}),
91
+ ...(customerData ? { customer: customerData } : {}),
92
+ // Items
93
+ items,
94
+ // Currency
95
+ currency_code: source.currency_code,
96
+ // Notes
97
+ note: source.note,
98
+ payment_terms: source.payment_terms,
99
+ // Date - use today's date as ISO string
100
+ date: new Date().toISOString(),
101
+ // Number - leave empty for auto-generation
102
+ // Do NOT copy: number, totals, taxes, payments, furs, eslog, vies, shareable_id
103
+ };
104
+
105
+ return baseData;
106
+ }
107
+
108
+ export type UseDuplicateDocumentOptions = {
109
+ /** Source document ID to duplicate from */
110
+ sourceId: string | undefined;
111
+ /** Target document type (may differ from source for conversions) */
112
+ targetType: DocumentType;
113
+ /** Whether to enable the query */
114
+ enabled?: boolean;
115
+ };
116
+
117
+ export type UseDuplicateDocumentResult = {
118
+ /** Transformed initial values for the form */
119
+ initialValues: Partial<CreateRequest> | undefined;
120
+ /** Loading state */
121
+ isLoading: boolean;
122
+ /** Error if fetch failed */
123
+ error: Error | null;
124
+ /** Source document type */
125
+ sourceType: DocumentType | null;
126
+ };
127
+
128
+ /**
129
+ * Hook to fetch and transform a document for duplication
130
+ *
131
+ * @example
132
+ * ```tsx
133
+ * const { initialValues, isLoading } = useDuplicateDocument({
134
+ * sourceId: searchParams.duplicateFrom,
135
+ * targetType: 'invoice',
136
+ * });
137
+ * ```
138
+ */
139
+ export function useDuplicateDocument({
140
+ sourceId,
141
+ targetType,
142
+ enabled = true,
143
+ }: UseDuplicateDocumentOptions): UseDuplicateDocumentResult {
144
+ const { sdk } = useSDK();
145
+ const { activeEntity } = useEntities();
146
+
147
+ const sourceType = sourceId ? getDocumentTypeFromId(sourceId) : null;
148
+
149
+ const query = useQuery({
150
+ queryKey: ["duplicate-document", sourceId, targetType, activeEntity?.id],
151
+ queryFn: async () => {
152
+ if (!sourceId || !activeEntity?.id || !sourceType) {
153
+ throw new Error("Source document ID and entity ID are required");
154
+ }
155
+
156
+ // Fetch source document based on its type
157
+ let source: Document;
158
+ if (sourceType === "invoice") {
159
+ source = await sdk.invoices.get(sourceId, undefined, { entity_id: activeEntity.id });
160
+ } else if (sourceType === "estimate") {
161
+ // estimates.get only takes 2 args (no params)
162
+ source = await sdk.estimates.get(sourceId, { entity_id: activeEntity.id });
163
+ } else if (sourceType === "advance_invoice") {
164
+ source = await sdk.advanceInvoices.get(sourceId, undefined, { entity_id: activeEntity.id });
165
+ } else {
166
+ // Credit note
167
+ source = await sdk.creditNotes.get(sourceId, undefined, { entity_id: activeEntity.id });
168
+ }
169
+
170
+ if (!source) {
171
+ throw new Error("Source document not found");
172
+ }
173
+
174
+ return transformDocumentForDuplication(source, targetType);
175
+ },
176
+ enabled: enabled && !!sourceId && !!activeEntity?.id && !!sourceType,
177
+ });
178
+
179
+ return {
180
+ initialValues: query.data,
181
+ isLoading: query.isLoading,
182
+ error: query.error,
183
+ sourceType,
184
+ };
185
+ }
@@ -0,0 +1,19 @@
1
+ import * as React from "react";
2
+
3
+ export function useMediaQuery(query: string) {
4
+ const [value, setValue] = React.useState(false);
5
+
6
+ React.useEffect(() => {
7
+ function onChange(event: MediaQueryListEvent) {
8
+ setValue(event.matches);
9
+ }
10
+
11
+ const result = matchMedia(query);
12
+ result.addEventListener("change", onChange);
13
+ setValue(result.matches);
14
+
15
+ return () => result.removeEventListener("change", onChange);
16
+ }, [query]);
17
+
18
+ return value;
19
+ }
@@ -0,0 +1,39 @@
1
+ import * as React from "react";
2
+
3
+ const MOBILE_BREAKPOINT = 768;
4
+ const SMALL_SCREEN_BREAKPOINT = 1024;
5
+
6
+ export function useIsMobile() {
7
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
8
+
9
+ React.useEffect(() => {
10
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
11
+ const onChange = () => {
12
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
13
+ };
14
+ mql.addEventListener("change", onChange);
15
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
16
+ return () => mql.removeEventListener("change", onChange);
17
+ }, []);
18
+
19
+ return !!isMobile;
20
+ }
21
+
22
+ export function useIsSmallScreen() {
23
+ const [isSmallScreen, setIsSmallScreen] = React.useState<boolean | undefined>(undefined);
24
+
25
+ React.useEffect(() => {
26
+ const checkSize = () => {
27
+ const width = window.innerWidth;
28
+ setIsSmallScreen(width >= MOBILE_BREAKPOINT && width < SMALL_SCREEN_BREAKPOINT);
29
+ };
30
+ const mql = window.matchMedia(
31
+ `(min-width: ${MOBILE_BREAKPOINT}px) and (max-width: ${SMALL_SCREEN_BREAKPOINT - 1}px)`,
32
+ );
33
+ mql.addEventListener("change", checkSize);
34
+ checkSize();
35
+ return () => mql.removeEventListener("change", checkSize);
36
+ }, []);
37
+
38
+ return !!isSmallScreen;
39
+ }
@@ -0,0 +1,57 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+
3
+ import { useSDK } from "@/ui/providers/sdk-provider";
4
+
5
+ export const NEXT_DOCUMENT_NUMBER_CACHE_KEY = "next-document-number";
6
+
7
+ export type DocumentType = "invoice" | "estimate" | "credit_note" | "advance_invoice";
8
+
9
+ /** Response type for next document number preview */
10
+ export type NextDocumentNumberResponse = {
11
+ number: string | null;
12
+ furs: {
13
+ business_premise_name: string;
14
+ electronic_device_name: string;
15
+ } | null;
16
+ };
17
+
18
+ /**
19
+ * Hook to fetch the next document number preview
20
+ * Does not increment the sequence - purely for preview purposes
21
+ *
22
+ * Uses sdk.documents.getNextNumber() - the shared documents API
23
+ */
24
+ export function useNextDocumentNumber(
25
+ entityId: string,
26
+ type: DocumentType,
27
+ options?: {
28
+ businessPremiseName?: string;
29
+ electronicDeviceName?: string;
30
+ enabled?: boolean;
31
+ },
32
+ ) {
33
+ const { sdk } = useSDK();
34
+
35
+ return useQuery<NextDocumentNumberResponse>({
36
+ queryKey: [
37
+ NEXT_DOCUMENT_NUMBER_CACHE_KEY,
38
+ entityId,
39
+ type,
40
+ options?.businessPremiseName,
41
+ options?.electronicDeviceName,
42
+ ],
43
+ queryFn: async () => {
44
+ const response = await sdk.documents.getNextNumber(
45
+ {
46
+ type,
47
+ business_premise_name: options?.businessPremiseName,
48
+ electronic_device_name: options?.electronicDeviceName,
49
+ },
50
+ { entity_id: entityId },
51
+ );
52
+ return response;
53
+ },
54
+ enabled: options?.enabled !== false && !!entityId && !!sdk?.documents,
55
+ staleTime: 0, // Always refetch when form opens
56
+ });
57
+ }