@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,43 @@
1
+ /**
2
+ * Advance invoice form schema.
3
+ *
4
+ * Requires: ./shared.ts
5
+ */
6
+ import type { CreateAdvanceInvoiceBody } from "@spaceinvoices/js-sdk";
7
+ import { z } from "zod";
8
+ import { customerSchema, lineItemSchema, transformItemsForApi } from "./shared";
9
+
10
+ // Advance invoices don't have payment_terms - they are documents requesting payment
11
+ export const advanceInvoiceFormSchema = z.object({
12
+ number: z.string().max(100).optional(),
13
+ date: z.string().optional(),
14
+ date_due: z.string().optional(),
15
+ customer_id: z.string().nullish(),
16
+ customer: customerSchema,
17
+ items: z.array(lineItemSchema).min(1),
18
+ note: z.string().nullish(),
19
+ currency_code: z.string().max(3).optional(),
20
+ metadata: z.record(z.string(), z.unknown()).optional(),
21
+ linked_documents: z.array(z.string()).optional(),
22
+ furs: z
23
+ .object({
24
+ business_premise_name: z.string().optional(),
25
+ electronic_device_name: z.string().optional(),
26
+ operator_tax_number: z.string().optional(),
27
+ operator_label: z.string().optional(),
28
+ skip: z.boolean().optional(),
29
+ })
30
+ .optional(),
31
+ eslog: z.object({ validation_enabled: z.boolean().optional() }).optional(),
32
+ bank_account_index: z.number().optional(),
33
+ });
34
+
35
+ export type AdvanceInvoiceFormValues = z.infer<typeof advanceInvoiceFormSchema>;
36
+
37
+ /** Convert form values to API request */
38
+ export function toCreateAdvanceInvoiceRequest(values: AdvanceInvoiceFormValues): CreateAdvanceInvoiceBody {
39
+ return {
40
+ ...values,
41
+ items: transformItemsForApi(values.items ?? []),
42
+ } as CreateAdvanceInvoiceBody;
43
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Credit note form schema.
3
+ *
4
+ * Requires: ./shared.ts
5
+ */
6
+ import type { CreateCreditNoteBody } from "@spaceinvoices/js-sdk";
7
+ import { z } from "zod";
8
+ import { customerSchema, lineItemSchema, transformItemsForApi } from "./shared";
9
+
10
+ export const creditNoteFormSchema = z.object({
11
+ number: z.string().max(100).optional(),
12
+ date: z.string().optional(),
13
+ customer_id: z.string().nullish(),
14
+ customer: customerSchema,
15
+ items: z.array(lineItemSchema).min(1),
16
+ note: z.string().nullish(),
17
+ payment_terms: z.string().nullish(),
18
+ currency_code: z.string().max(3).optional(),
19
+ metadata: z.record(z.string(), z.unknown()).optional(),
20
+ linked_documents: z.array(z.string()).optional(),
21
+ eslog: z.object({ validation_enabled: z.boolean().optional() }).optional(),
22
+ });
23
+
24
+ export type CreditNoteFormValues = z.infer<typeof creditNoteFormSchema>;
25
+
26
+ /** Convert form values to API request */
27
+ export function toCreateCreditNoteRequest(values: CreditNoteFormValues): CreateCreditNoteBody {
28
+ return {
29
+ ...values,
30
+ items: transformItemsForApi(values.items ?? []),
31
+ } as CreateCreditNoteBody;
32
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Estimate form schema.
3
+ *
4
+ * Requires: ./shared.ts
5
+ */
6
+ import type { CreateEstimateBody } from "@spaceinvoices/js-sdk";
7
+ import { z } from "zod";
8
+ import { customerSchema, lineItemSchema, transformItemsForApi } from "./shared";
9
+
10
+ export const estimateFormSchema = z.object({
11
+ number: z.string().max(100).optional(),
12
+ date: z.string().optional(),
13
+ date_valid_till: z.string().optional(),
14
+ customer_id: z.string().nullish(),
15
+ customer: customerSchema,
16
+ items: z.array(lineItemSchema).min(1),
17
+ note: z.string().nullish(),
18
+ payment_terms: z.string().nullish(),
19
+ currency_code: z.string().max(3).optional(),
20
+ metadata: z.record(z.string(), z.unknown()).optional(),
21
+ });
22
+
23
+ export type EstimateFormValues = z.infer<typeof estimateFormSchema>;
24
+
25
+ /** Convert form values to API request */
26
+ export function toCreateEstimateRequest(values: EstimateFormValues): CreateEstimateBody {
27
+ return {
28
+ ...values,
29
+ items: transformItemsForApi(values.items ?? []),
30
+ } as CreateEstimateBody;
31
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Form schemas for document creation.
3
+ *
4
+ * For copy-paste usage, grab the specific schema file you need + shared.ts:
5
+ * - shared.ts (required) - common building blocks
6
+ * - invoice.ts - invoice form
7
+ * - credit-note.ts - credit note form
8
+ * - estimate.ts - estimate form
9
+ * - advance-invoice.ts - advance invoice form
10
+ */
11
+
12
+ export * from "./advance-invoice";
13
+ export * from "./credit-note";
14
+ export * from "./estimate";
15
+ // Document-specific schemas
16
+ export * from "./invoice";
17
+ // Shared building blocks
18
+ export * from "./shared";
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Invoice form schema.
3
+ *
4
+ * Requires: ./shared.ts
5
+ */
6
+ import type { CreateInvoiceBody } from "@spaceinvoices/js-sdk";
7
+ import { z } from "zod";
8
+ import { customerSchema, lineItemSchema, transformItemsForApi } from "./shared";
9
+
10
+ export const invoiceFormSchema = z.object({
11
+ number: z.string().max(100).optional(),
12
+ date: z.string().optional(),
13
+ date_due: z.string().optional(),
14
+ customer_id: z.string().nullish(),
15
+ customer: customerSchema,
16
+ items: z.array(lineItemSchema).min(1),
17
+ note: z.string().nullish(),
18
+ payment_terms: z.string().nullish(),
19
+ currency_code: z.string().max(3).optional(),
20
+ metadata: z.record(z.string(), z.unknown()).optional(),
21
+ linked_documents: z.array(z.string()).optional(),
22
+ furs: z
23
+ .object({
24
+ business_premise_name: z.string().optional(),
25
+ electronic_device_name: z.string().optional(),
26
+ operator_tax_number: z.string().optional(),
27
+ operator_label: z.string().optional(),
28
+ skip: z.boolean().optional(),
29
+ })
30
+ .optional(),
31
+ eslog: z.object({ validation_enabled: z.boolean().optional() }).optional(),
32
+ bank_account_index: z.number().optional(),
33
+ });
34
+
35
+ export type InvoiceFormValues = z.infer<typeof invoiceFormSchema>;
36
+
37
+ /** Convert form values to API request */
38
+ export function toCreateInvoiceRequest(values: InvoiceFormValues): CreateInvoiceBody {
39
+ return {
40
+ ...values,
41
+ items: transformItemsForApi(values.items ?? []),
42
+ } as CreateInvoiceBody;
43
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Shared form schema building blocks.
3
+ *
4
+ * These are reusable across document forms (invoice, estimate, credit note, etc.)
5
+ * Copy this file along with any document-specific schema you need.
6
+ */
7
+ import { z } from "zod";
8
+
9
+ /** Customer schema - for inline customer data on documents */
10
+ export const customerSchema = z
11
+ .object({
12
+ name: z.string().nullish(),
13
+ email: z.string().email().nullish(),
14
+ address: z.string().nullish(),
15
+ address_2: z.string().nullish(),
16
+ post_code: z.string().nullish(),
17
+ city: z.string().nullish(),
18
+ state: z.string().nullish(),
19
+ country: z.string().nullish(),
20
+ country_code: z.string().nullish(),
21
+ tax_number: z.string().nullish(),
22
+ save_customer: z.boolean().optional(),
23
+ })
24
+ .nullish();
25
+
26
+ export type CustomerFormData = z.infer<typeof customerSchema>;
27
+
28
+ /** Tax schema for line items */
29
+ export const taxSchema = z.object({
30
+ rate: z.number().optional(),
31
+ tax_id: z.string().optional(),
32
+ classification: z.string().optional(),
33
+ reverse_charge: z.boolean().optional(),
34
+ amount: z.number().optional(),
35
+ });
36
+
37
+ export type TaxFormData = z.infer<typeof taxSchema>;
38
+
39
+ /** Discount schema for line items */
40
+ export const discountSchema = z.object({
41
+ value: z.number().min(0),
42
+ type: z.enum(["percent", "amount"]).optional().default("percent"),
43
+ });
44
+
45
+ export type DiscountFormData = z.infer<typeof discountSchema>;
46
+
47
+ /**
48
+ * Line item schema with UI-only is_gross_price flag.
49
+ *
50
+ * The `is_gross_price` field is UI-only - when true, the `price` field
51
+ * contains the gross (tax-inclusive) price and should be sent as `gross_price`
52
+ * to the API instead of `price`.
53
+ */
54
+ export const lineItemSchema = z.object({
55
+ name: z.string().min(1),
56
+ description: z.string().nullish(),
57
+ price: z.number().optional(),
58
+ gross_price: z.number().optional(),
59
+ quantity: z.number(),
60
+ unit: z.string().nullish(),
61
+ taxes: z.array(taxSchema).optional(),
62
+ discounts: z.array(discountSchema).max(5).optional(),
63
+ metadata: z.unknown().optional(),
64
+ /** UI-only: when true, price field contains gross (tax-inclusive) price */
65
+ is_gross_price: z.boolean().optional().default(false),
66
+ });
67
+
68
+ export type LineItemFormData = z.infer<typeof lineItemSchema>;
69
+
70
+ /**
71
+ * Transform line items for API submission.
72
+ * Converts is_gross_price flag to proper price/gross_price fields.
73
+ */
74
+ export function transformItemsForApi<T extends LineItemFormData>(items: T[]) {
75
+ return items.map(({ is_gross_price, price, ...item }) => ({
76
+ ...item,
77
+ ...(is_gross_price ? { gross_price: price } : { price }),
78
+ }));
79
+ }
@@ -0,0 +1,38 @@
1
+ import { useCallback } from "react";
2
+
3
+ type TranslationFunction = (key: string) => string;
4
+
5
+ export type ComponentTranslationProps = {
6
+ t?: TranslationFunction;
7
+ namespace?: string;
8
+ locale?: string;
9
+ translations?: Record<string, Record<string, string>>;
10
+ };
11
+
12
+ export function createTranslation({ t, namespace, locale = "en", translations = {} }: ComponentTranslationProps = {}) {
13
+ return useCallback(
14
+ (key: string): string => {
15
+ // 1. If external translation function provided, use it
16
+ if (t) {
17
+ const fullKey = namespace ? `${namespace}.${key}` : key;
18
+ const result = t(fullKey);
19
+
20
+ // If translation found (result is not the key), return it.
21
+ // We check against both fullKey (standard i18next behavior) and key (in case namespace was stripped)
22
+ if (result !== fullKey && result !== key) {
23
+ return result;
24
+ }
25
+ }
26
+
27
+ // 2. Look up in local translations for current locale
28
+ if (locale !== "en" && translations[locale]) {
29
+ const translation = translations[locale][key];
30
+ if (translation) return translation;
31
+ }
32
+
33
+ // 3. Fall back to key itself (which is the English text)
34
+ return key;
35
+ },
36
+ [t, namespace, locale, translations],
37
+ );
38
+ }
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,41 @@
1
+ import type { GetEntities200DataItem } from "@spaceinvoices/js-sdk";
2
+
3
+ import { createContext, useContext } from "react";
4
+
5
+ /** Entity type with country_rules included (from getEntities response) */
6
+ export type Entity = GetEntities200DataItem;
7
+
8
+ export type EntityEnvironment = "live" | "sandbox";
9
+
10
+ export type EntitiesContextType = {
11
+ entities: Entity[];
12
+ activeEntity: Entity | null;
13
+ setActiveEntity: (entity: Entity | null) => void;
14
+ environment: EntityEnvironment;
15
+ setEnvironment: (environment: EntityEnvironment) => void;
16
+ isLoading: boolean;
17
+ refetchEntities: () => Promise<void>;
18
+ isError: boolean;
19
+ error: Error | null;
20
+ status: "loading" | "error" | "success" | "pending";
21
+ };
22
+
23
+ export const EntitiesContext = createContext<EntitiesContextType | undefined>(undefined);
24
+
25
+ export const useEntities = () => {
26
+ const context = useContext(EntitiesContext);
27
+ if (!context) {
28
+ throw new Error("useEntities must be used within an EntitiesProvider");
29
+ }
30
+ return context;
31
+ };
32
+
33
+ /** Optional version that returns undefined when outside EntitiesProvider (for public views) */
34
+ export const useEntitiesOptional = () => {
35
+ return useContext(EntitiesContext);
36
+ };
37
+
38
+ export const useActiveEntity = () => {
39
+ const { activeEntity, setActiveEntity } = useEntities();
40
+ return { activeEntity, setActiveEntity };
41
+ };
@@ -0,0 +1,201 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import type { ReactNode } from "react";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { useCookies } from "react-cookie";
5
+ import { ACTIVE_ENTITY_COOKIE, ACTIVE_ENVIRONMENT_COOKIE } from "@/ui/components/entities/keys";
6
+
7
+ import { useSDK } from "@/ui/providers/sdk-provider";
8
+
9
+ import { EntitiesContext, type Entity, type EntityEnvironment } from "./entities-context";
10
+
11
+ // Define a constant for the entities cache key
12
+ export const ENTITIES_QUERY_KEY = ["entities"] as const;
13
+
14
+ function LoadingFallback() {
15
+ return (
16
+ <div className="fixed inset-0 flex items-center justify-center bg-background/50">
17
+ <div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
18
+ </div>
19
+ );
20
+ }
21
+
22
+ type EntitiesProviderProps = {
23
+ children: ReactNode;
24
+ initialActiveEntity?: Entity | null;
25
+ onNoEntities?: () => void;
26
+ };
27
+
28
+ export function EntitiesProvider({ children, initialActiveEntity, onNoEntities }: EntitiesProviderProps) {
29
+ const { sdk, isInitialized } = useSDK();
30
+ const [cookies, setCookie, removeCookie] = useCookies([ACTIVE_ENTITY_COOKIE, ACTIVE_ENVIRONMENT_COOKIE]);
31
+ const isInitialMount = useRef(true);
32
+ const initialEnvironmentFromCookie = cookies[ACTIVE_ENVIRONMENT_COOKIE] as EntityEnvironment | undefined;
33
+ const initialEntityFromCookie = cookies[ACTIVE_ENTITY_COOKIE] as Entity | undefined;
34
+
35
+ const resolvedInitialEnvironment: EntityEnvironment =
36
+ initialEnvironmentFromCookie ??
37
+ (initialEntityFromCookie?.environment as EntityEnvironment | undefined) ??
38
+ (initialActiveEntity?.environment as EntityEnvironment | undefined) ??
39
+ "live";
40
+
41
+ const [environment, setEnvironmentState] = useState<EntityEnvironment>(resolvedInitialEnvironment);
42
+ const previousEnvironment = useRef(environment);
43
+
44
+ // Initialize active entity from cookie or prop that matches the selected environment
45
+ const [activeEntityState, setActiveEntityState] = useState<Entity | null>(() => {
46
+ try {
47
+ if (initialEntityFromCookie && initialEntityFromCookie.environment === resolvedInitialEnvironment) {
48
+ return initialEntityFromCookie;
49
+ }
50
+ } catch (_e) {
51
+ // Ignore cookie parsing errors
52
+ }
53
+
54
+ if (initialActiveEntity && initialActiveEntity.environment === resolvedInitialEnvironment) {
55
+ return initialActiveEntity;
56
+ }
57
+
58
+ return null;
59
+ });
60
+
61
+ const {
62
+ data: entities = [],
63
+ isLoading,
64
+ refetch,
65
+ isError,
66
+ error,
67
+ status,
68
+ } = useQuery({
69
+ queryKey: [...ENTITIES_QUERY_KEY, environment],
70
+ queryFn: async () => {
71
+ if (!sdk) return [];
72
+
73
+ // Pass environment to filter entities for user tokens
74
+ const response = await sdk.entities.list({ limit: 100, environment });
75
+ return response.data;
76
+ },
77
+ enabled: !!sdk && isInitialized,
78
+ staleTime: 1000 * 60 * 5,
79
+ gcTime: 1000 * 60 * 60,
80
+ retry: 2,
81
+ refetchOnMount: true,
82
+ refetchOnWindowFocus: false,
83
+ refetchOnReconnect: true,
84
+ });
85
+
86
+ // Redirect to add entity page when no entities exist
87
+ const hasCalledNoEntities = useRef(false);
88
+ useEffect(() => {
89
+ if (!isLoading && entities.length === 0 && !hasCalledNoEntities.current) {
90
+ hasCalledNoEntities.current = true;
91
+ onNoEntities?.();
92
+ }
93
+ }, [isLoading, entities.length, onNoEntities]);
94
+
95
+ // Memoize entities to prevent unnecessary re-renders
96
+ const memoizedEntities = useMemo(() => entities, [entities]);
97
+
98
+ useEffect(() => {
99
+ if (previousEnvironment.current !== environment) {
100
+ previousEnvironment.current = environment;
101
+ setActiveEntityState(null);
102
+ }
103
+ }, [environment]);
104
+
105
+ // Sync active entity when entities list changes
106
+ // Use ref to read current activeEntityState without causing re-runs
107
+ const activeEntityRef = useRef(activeEntityState);
108
+ activeEntityRef.current = activeEntityState;
109
+
110
+ useEffect(() => {
111
+ if (!memoizedEntities.length) return;
112
+
113
+ const currentActive = activeEntityRef.current;
114
+
115
+ try {
116
+ if (currentActive) {
117
+ const updatedEntity = memoizedEntities.find((entity) => entity.id === currentActive.id);
118
+
119
+ if (updatedEntity) {
120
+ // Always update with fresh data from the server
121
+ setActiveEntityState(updatedEntity);
122
+ } else {
123
+ // If active entity no longer exists, fall back to first entity
124
+ setActiveEntityState(memoizedEntities[0]);
125
+ }
126
+ } else {
127
+ setActiveEntityState(memoizedEntities[0]);
128
+ }
129
+ } catch (_e) {
130
+ if (memoizedEntities.length > 0) {
131
+ setActiveEntityState(memoizedEntities[0]);
132
+ }
133
+ } finally {
134
+ if (isInitialMount.current) {
135
+ isInitialMount.current = false;
136
+ }
137
+ }
138
+ }, [memoizedEntities]); // Only depend on entities list
139
+
140
+ // Update cookie when active entity changes
141
+ useEffect(() => {
142
+ if (activeEntityState) {
143
+ setCookie(ACTIVE_ENTITY_COOKIE, activeEntityState, {
144
+ path: "/",
145
+ maxAge: 60 * 60 * 24 * 365,
146
+ sameSite: "lax",
147
+ });
148
+ } else {
149
+ removeCookie(ACTIVE_ENTITY_COOKIE, { path: "/" });
150
+ }
151
+ }, [activeEntityState, setCookie, removeCookie]);
152
+
153
+ useEffect(() => {
154
+ setCookie(ACTIVE_ENVIRONMENT_COOKIE, environment, {
155
+ path: "/",
156
+ maxAge: 60 * 60 * 24 * 365,
157
+ sameSite: "lax",
158
+ });
159
+ }, [environment, setCookie]);
160
+
161
+ const refetchEntities = useCallback(async () => {
162
+ await refetch();
163
+ }, [refetch]);
164
+
165
+ const handleEnvironmentChange = useCallback((nextEnvironment: EntityEnvironment) => {
166
+ setEnvironmentState((current) => (current === nextEnvironment ? current : nextEnvironment));
167
+ }, []);
168
+
169
+ const value = useMemo(
170
+ () => ({
171
+ entities: memoizedEntities,
172
+ activeEntity: activeEntityState,
173
+ setActiveEntity: setActiveEntityState,
174
+ environment,
175
+ setEnvironment: handleEnvironmentChange,
176
+ isLoading,
177
+ refetchEntities,
178
+ isError,
179
+ error,
180
+ status,
181
+ }),
182
+ [
183
+ memoizedEntities,
184
+ activeEntityState,
185
+ environment,
186
+ handleEnvironmentChange,
187
+ isLoading,
188
+ refetchEntities,
189
+ isError,
190
+ error,
191
+ status,
192
+ ],
193
+ );
194
+
195
+ // Show loading state only if entities are still loading
196
+ if (isLoading) {
197
+ return <LoadingFallback />;
198
+ }
199
+
200
+ return <EntitiesContext value={value}>{children}</EntitiesContext>;
201
+ }
@@ -0,0 +1,72 @@
1
+ import { createContext, type ReactNode, useContext, useEffect, useState } from "react";
2
+
3
+ type SecondaryAction = {
4
+ label: string;
5
+ onClick: () => void;
6
+ isPending?: boolean;
7
+ };
8
+
9
+ type FormFooterState = {
10
+ formId: string;
11
+ isPending: boolean;
12
+ isDirty: boolean;
13
+ label: string;
14
+ onSubmit?: () => void;
15
+ secondaryAction?: SecondaryAction;
16
+ };
17
+
18
+ type FormFooterContextType = {
19
+ state: FormFooterState | null;
20
+ setFormFooter: (state: FormFooterState | null) => void;
21
+ };
22
+
23
+ const FormFooterContext = createContext<FormFooterContextType | undefined>(undefined);
24
+
25
+ export function FormFooterProvider({ children }: { children: ReactNode }) {
26
+ const [state, setState] = useState<FormFooterState | null>(null);
27
+
28
+ return <FormFooterContext.Provider value={{ state, setFormFooter: setState }}>{children}</FormFooterContext.Provider>;
29
+ }
30
+
31
+ export function useFormFooterContext() {
32
+ const context = useContext(FormFooterContext);
33
+ if (!context) {
34
+ throw new Error("useFormFooterContext must be used within FormFooterProvider");
35
+ }
36
+ return context;
37
+ }
38
+
39
+ type UseFormFooterRegistrationProps = {
40
+ formId: string;
41
+ isPending: boolean;
42
+ isDirty: boolean;
43
+ label: string;
44
+ onSubmit?: () => void;
45
+ secondaryAction?: SecondaryAction;
46
+ };
47
+
48
+ export function useFormFooterRegistration({
49
+ formId,
50
+ isPending,
51
+ isDirty,
52
+ label,
53
+ onSubmit,
54
+ secondaryAction,
55
+ }: UseFormFooterRegistrationProps) {
56
+ const { setFormFooter } = useFormFooterContext();
57
+
58
+ useEffect(() => {
59
+ setFormFooter({
60
+ formId,
61
+ isPending,
62
+ isDirty,
63
+ label,
64
+ onSubmit,
65
+ secondaryAction,
66
+ });
67
+
68
+ return () => {
69
+ setFormFooter(null);
70
+ };
71
+ }, [formId, isPending, isDirty, label, onSubmit, secondaryAction, setFormFooter]);
72
+ }