@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,188 @@
1
+ export default {
2
+ // Company settings
3
+ "Company Information": "Podatki o podjetju",
4
+ "Basic information about your company": "Osnovni podatki o vašem podjetju",
5
+ // Branding settings
6
+ Branding: "Blagovna znamka",
7
+ "Primary Color": "Primarna barva",
8
+ Logo: "Logotip",
9
+ "Logo URL": "URL logotipa",
10
+ "Enter the URL of your company logo": "Vnesite URL logotipa vašega podjetja",
11
+ "Upload your company logo for invoices": "Naložite logotip vašega podjetja za račune",
12
+ "Upload Logo": "Naloži logotip",
13
+ "Change Logo": "Spremeni logotip",
14
+ "Current logo (displayed on invoices)": "Trenutni logotip (prikazan na računih)",
15
+ "Logo preview": "Predogled logotipa",
16
+ "Crop Your Logo": "Obreži logotip",
17
+ Signature: "Podpis",
18
+ "Upload a signature image for PDFs (optional)": "Naložite sliko podpisa za PDF-je (neobvezno)",
19
+ "Upload Signature": "Naloži podpis",
20
+ "Change Signature": "Spremeni podpis",
21
+ "Current signature (for PDF documents)": "Trenutni podpis (za PDF dokumente)",
22
+ "Signature preview": "Predogled podpisa",
23
+ "Crop Your Signature": "Obreži podpis",
24
+ "Adjust the crop area or upload the full image": "Prilagodite območje obrezovanja ali naložite celotno sliko",
25
+ "Upload failed. Please try again.": "Nalaganje ni uspelo. Prosim poskusite znova.",
26
+ "Uploading...": "Nalaganje...",
27
+ "Upload & Save": "Naloži in shrani",
28
+ Cancel: "Prekliči",
29
+ Localization: "Lokalizacija",
30
+ "Currency Code": "Koda valute",
31
+ "ISO 4217 currency code (e.g., USD, EUR, GBP)": "ISO 4217 koda valute (npr. USD, EUR, GBP)",
32
+ Locale: "Lokalnost",
33
+ "BCP 47 language tag (e.g., en-US, de-DE, sl-SI)": "BCP 47 jezikovna oznaka (npr. en-US, de-DE, sl-SI)",
34
+ "Select currency": "Izberite valuto",
35
+ "Select locale": "Izberite lokalnost",
36
+ "Configure entity localization": "Konfigurirajte lokalizacijo pravne osebe",
37
+ "Email Settings": "Nastavitve e-pošte",
38
+ "Configure email settings for invoices": "Konfigurirajte nastavitve e-pošte za račune",
39
+ "Email Address": "Naslov e-pošte",
40
+ "Email address to send invoices to": "Naslov e-pošte za pošiljanje računov",
41
+ "Email Subject": "Zadeva e-pošte",
42
+ "Subject line for email invoices": "Vrstica zadeve za e-pošte računov",
43
+ "Email Body": "Vsebina e-pošte",
44
+ "Body content for email invoices": "Vsebina teksta za e-pošte računov",
45
+ "Save Settings": "Shrani nastavitve",
46
+ // Document Defaults section
47
+ "Document Defaults": "Privzete vrednosti dokumentov",
48
+ "Default values for new documents": "Privzete vrednosti za nove dokumente",
49
+ "Default Note": "Privzeta opomba",
50
+ "Default Invoice Note": "Privzeta opomba računa",
51
+ "This note will be pre-filled when creating new invoices":
52
+ "Ta opomba bo vnaprej izpolnjena pri ustvarjanju novih računov",
53
+ "This note will be pre-filled when creating new estimates":
54
+ "Ta opomba bo vnaprej izpolnjena pri ustvarjanju novih predračunov",
55
+ "This note will be pre-filled when creating new credit notes":
56
+ "Ta opomba bo vnaprej izpolnjena pri ustvarjanju novih dobropisov",
57
+ "Payment terms pre-filled when creating new invoices":
58
+ "Plačilni pogoji bodo vnaprej izpolnjeni pri ustvarjanju novih računov",
59
+ "Payment terms pre-filled when creating new estimates":
60
+ "Plačilni pogoji bodo vnaprej izpolnjeni pri ustvarjanju novih predračunov",
61
+ "Payment terms pre-filled when creating new credit notes":
62
+ "Plačilni pogoji bodo vnaprej izpolnjeni pri ustvarjanju novih dobropisov",
63
+ "Payment due by {document_due_date}. Please reference invoice {document_number}.":
64
+ "Rok plačila {document_due_date}. Prosimo, navedite številko računa {document_number}.",
65
+ "Net 30 days. Payment due by {document_due_date}.": "Neto 30 dni. Rok plačila {document_due_date}.",
66
+ "This estimate is valid until {document_valid_until}.": "Ta predračun velja do {document_valid_until}.",
67
+ "Payment due upon acceptance.": "Plačilo ob sprejemu.",
68
+ "Credit note for invoice {document_number}.": "Dobropis za račun {document_number}.",
69
+ "Credit will be applied to your account.": "Dobropis bo upoštevan na vašem računu.",
70
+ "Invoice Notes": "Opombe računa",
71
+ "Smart Template Variables": "Pametne spremenljivke predloge",
72
+ "Use variables to personalize your notes automatically": "Uporabite spremenljivke za samodejno prilagajanje opomb",
73
+ "Set a default note that will appear on all new invoices. Use template variables to personalize the note automatically.":
74
+ "Nastavite privzeto opombo, ki bo prikazana na vseh novih računih. Uporabite spremenljivke predloge za samodejno prilagajanje opombe.",
75
+ // Smart code insert button
76
+ "Insert variable": "Vstavi spremenljivko",
77
+ Entity: "Podjetje",
78
+ Document: "Dokument",
79
+ Customer: "Stranka",
80
+ Other: "Drugo",
81
+ "Company name": "Ime podjetja",
82
+ "Email address": "E-poštni naslov",
83
+ "Invoice number": "Številka računa",
84
+ "Invoice date": "Datum računa",
85
+ "Due date": "Rok plačila",
86
+ "Total amount": "Skupni znesek",
87
+ Currency: "Valuta",
88
+ "Customer name": "Ime stranke",
89
+ "Customer email": "E-pošta stranke",
90
+ "Today's date": "Današnji datum",
91
+ "Current year": "Tekoče leto",
92
+ // Payment terms defaults
93
+ "Default Payment Terms": "Privzeti plačilni pogoji",
94
+ "This payment terms will be pre-filled when creating new documents":
95
+ "Ti plačilni pogoji bodo vnaprej izpolnjeni pri ustvarjanju novih dokumentov",
96
+ "Payment Terms": "Plačilni pogoji",
97
+ "Add payment terms...": "Dodajte plačilne pogoje...",
98
+ // Document footer
99
+ "Document Footer": "Noga dokumenta",
100
+ "Footer text displayed at the bottom of PDF documents": "Besedilo noge, prikazano na dnu PDF dokumentov",
101
+ "Footer text displayed at the bottom of all PDF documents": "Besedilo noge, prikazano na dnu vseh PDF dokumentov",
102
+ "{entity_name} | Due Date: {document_due_date} | Invoice #{document_number}":
103
+ "{entity_name} | Rok plačila: {document_due_date} | Račun #{document_number}",
104
+ // Overdue Notifications section
105
+ "Overdue Notifications": "Opomniki o zapadlih računih",
106
+ "Automatically remind customers about overdue invoices": "Samodejno opominjajte stranke o zapadlih računih",
107
+ "Enable Notifications": "Omogoči opominke",
108
+ "Send automatic payment reminder emails to customers with overdue invoices":
109
+ "Pošiljajte avtomatska opomniška e-poštna sporočila strankam z zapadlimi računi",
110
+ "Reminder Days": "Dnevi opomnikov",
111
+ "Days after due date to send reminders (comma-separated). E.g., '7, 14, 30' sends reminders at 7, 14, and 30 days overdue.":
112
+ "Dnevi po roku plačila za pošiljanje opomnikov (ločeni z vejico). Npr. '7, 14, 30' pošlje opomnike 7, 14 in 30 dni po zapadlosti.",
113
+ "Custom Reminder Templates": "Prilagojene predloge opomnikov",
114
+ "Subject line for overdue notification emails": "Vrstica zadeve za e-pošte opomnikov o zapadlosti",
115
+ "Body content for overdue emails. Use {invoice_list} to include the invoice table, or it will be auto-appended.":
116
+ "Vsebina teksta za e-pošte o zapadlosti. Uporabite {invoice_list} za vključitev tabele računov, sicer bo samodejno dodana.",
117
+ "Dear {customer_name},\n\nThis is a reminder about your overdue invoices...":
118
+ "Spoštovani {customer_name},\n\nTo je opomnik o vaših zapadlih računih...",
119
+ "Payment Reminders": "Opomniki o plačilih",
120
+ "When enabled, customers will automatically receive email reminders when their invoices become overdue. Emails are sent on the exact day each threshold is reached.":
121
+ "Ko je omogočeno, bodo stranke samodejno prejele e-poštne opomnike, ko njihovi računi zapadejo. E-pošta se pošlje natančno na dan, ko je dosežen vsak prag.",
122
+ "Available Variables": "Razpoložljive spremenljivke",
123
+ "{customer_name}, {entity_name}, {entity_email}, {invoice_list}, {total_amount}, {overdue_count}":
124
+ "{customer_name}, {entity_name}, {entity_email}, {invoice_list}, {total_amount}, {overdue_count}",
125
+ // UPN QR Payment section
126
+ "UPN QR Payment": "UPN QR plačilo",
127
+ "Configure UPN QR payment slip for invoices": "Konfigurirajte UPN QR plačilni nalog za račune",
128
+ IBAN: "IBAN",
129
+ "Bank account IBAN for receiving payments": "IBAN bančnega računa za prejemanje plačil",
130
+ "Enable UPN QR on invoices": "Omogoči UPN QR na računih",
131
+ "Show payment QR code on PDF invoices for easy mobile banking payments":
132
+ "Prikaži QR kodo za plačilo na PDF računih za enostavna mobilna bančna plačila",
133
+ "Display Mode": "Način prikaza",
134
+ "QR code only": "Samo QR koda",
135
+ "Shows compact QR code inline with invoice content": "Prikaže kompaktno QR kodo v vrstici z vsebino računa",
136
+ "Full UPN payment slip": "Celoten UPN plačilni nalog",
137
+ "Shows complete payment slip at bottom of page": "Prikaže celoten plačilni nalog na dnu strani",
138
+ "Purpose Code": "Koda namena",
139
+ "OTHR - Other": "OTHR - Drugo",
140
+ "GDSV - Goods and Services": "GDSV - Blago in storitve",
141
+ "SUPP - Supplier Payment": "SUPP - Plačilo dobavitelju",
142
+ "Payment purpose code (ISO 20022)": "Koda namena plačila (ISO 20022)",
143
+ "UPN QR Payments": "UPN QR plačila",
144
+ "UPN QR is a Slovenian standard for payment slips. When enabled, your invoices will include a QR code that customers can scan with their mobile banking app to pay instantly.":
145
+ "UPN QR je slovenski standard za plačilne naloge. Ko je omogočen, bodo vaši računi vključevali QR kodo, ki jo stranke lahko skenirajo z mobilno bančno aplikacijo za takojšnje plačilo.",
146
+ // Number format settings
147
+ Invoice: "Račun",
148
+ Estimate: "Predračun",
149
+ "Credit Note": "Dobropis",
150
+ "Advance Invoice": "Avansni račun",
151
+ "Number Format": "Format številke",
152
+ Preview: "Predogled",
153
+ // Tax rules settings
154
+ "EU Tax Rules": "EU davčna pravila",
155
+ "Automatic tax handling for cross-border transactions": "Samodejno obravnavanje davkov za čezmejne transakcije",
156
+ "tax-rules.vies_validate_vat.label": "VIES preverjanje DDV",
157
+ "tax-rules.vies_validate_vat.description":
158
+ "Preveri EU DDV številke preko VIES API-ja. DDV številke strank se preverijo pred ustvarjanjem računov.",
159
+ "tax-rules.auto_reverse_charge.label": "Samodejni obrnjeni davek",
160
+ "tax-rules.auto_reverse_charge.description":
161
+ "Samodejno uporabi 0% davek (obrnjena davčna obveznost) za intra-EU B2B transakcije z veljavnimi DDV številkami.",
162
+ "tax-rules.auto_remove_tax_export.label": "Samodejno odstrani izvozni davek",
163
+ "tax-rules.auto_remove_tax_export.description": "Samodejno odstrani vse davke za izvoz v države izven EU.",
164
+ "tax-rules.require_gross_prices.label": "Zahtevaj bruto cene",
165
+ "tax-rules.require_gross_prices.description":
166
+ "Zahtevaj bruto cene, ko se uporabi obrnjena davčna obveznost. Prepreči nepričakovane spremembe skupnih zneskov računov.",
167
+ // Tax clauses settings
168
+ "Default Tax Clauses": "Privzete davčne klavzule",
169
+ "Set default tax clauses that are automatically added to documents based on transaction type":
170
+ "Nastavite privzete davčne klavzule, ki se samodejno dodajo dokumentom glede na vrsto transakcije.",
171
+ "tax-clauses.other-title": "Čezmejne davčne klavzule",
172
+ "tax-clauses.other-description": "Davčne klavzule za EU in mednarodne transakcije (samoobdavčitev, izvoz).",
173
+ "tax-clauses.intra_eu_b2b.label": "Intra-EU B2B (obrnjena davčna obveznost)",
174
+ "tax-clauses.intra_eu_b2b.description":
175
+ "Za prodajo EU podjetjem z veljavnimi DDV številkami. Običajno vključuje izjavo o obrnjeni davčni obveznosti.",
176
+ "Enter reverse charge clause...": "Vnesite klavzulo za samoobdavčitev...",
177
+ "tax-clauses.export.label": "Izvoz (izven EU)",
178
+ "tax-clauses.export.description": "Za prodajo strankam izven EU. Običajno navaja oprostitev DDV za izvoz.",
179
+ "Enter export exemption clause...": "Vnesite klavzulo za izvozno oprostitev...",
180
+ "tax-clauses.domestic.label": "Privzeto / Domače",
181
+ "tax-clauses.domestic.description":
182
+ "Privzeta klavzula za domače transakcije. Za subjekte, ki niso zavezanci za DDV, se prikaže na vseh dokumentih.",
183
+ "Enter default tax clause...": "Vnesite privzeto davčno klavzulo...",
184
+ "tax-clauses.intra_eu_b2c.label": "Intra-EU B2C",
185
+ "Enter EU consumer sales clause...": "Vnesite klavzulo za EU potrošniško prodajo...",
186
+ "tax-clauses.other.description":
187
+ "Te vrste transakcij običajno ne zahtevajo posebnih davčnih klavzul, vendar jih lahko konfigurirate po potrebi.",
188
+ } as const;
@@ -0,0 +1,410 @@
1
+ import { zodResolver } from "@hookform/resolvers/zod";
2
+ import type { Entity } from "@spaceinvoices/js-sdk";
3
+ import { AlertCircle, CheckCircle2, Info } from "lucide-react";
4
+ import { type FC, type ReactNode, useCallback, useEffect, useState } from "react";
5
+ import { useForm } from "react-hook-form";
6
+ import type { z } from "zod";
7
+ import { Alert, AlertDescription, AlertTitle } from "@/ui/components/ui/alert";
8
+ import { Form } from "@/ui/components/ui/form";
9
+ import { PageLoadingSpinner } from "@/ui/components/ui/loading-spinner";
10
+ import { Tabs, TabsList, TabsTrigger } from "@/ui/components/ui/tabs";
11
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@/ui/components/ui/tooltip";
12
+ import { updateFursSettingsSchema } from "@/ui/generated/schemas";
13
+ import type { ComponentTranslationProps } from "@/ui/lib/translation";
14
+ import { createTranslation } from "@/ui/lib/translation";
15
+ import { cn } from "@/ui/lib/utils";
16
+ import { useFormFooterRegistration } from "@/ui/providers/form-footer-context";
17
+ import { useFursPremises, useFursSettings, useUpdateFursSettings } from "./furs-settings.hooks";
18
+ // Import locale files
19
+ import de from "./locales/de";
20
+ import en from "./locales/en";
21
+ import sl from "./locales/sl";
22
+ import { CertificateSettingsSection } from "./sections/certificate-settings-section";
23
+ import { EnableFiscalizationSection } from "./sections/enable-fiscalization-section";
24
+ import { GeneralSettingsSection } from "./sections/general-settings-section";
25
+ import { PremisesManagementSection } from "./sections/premises-management-section";
26
+
27
+ const translations = { de, sl, en } as const;
28
+
29
+ /**
30
+ * FURS Settings Form Schema
31
+ * Uses generated schema from OpenAPI spec
32
+ */
33
+ const fursSettingsFormSchema = updateFursSettingsSchema.required({
34
+ enabled: true,
35
+ });
36
+
37
+ export type FursSettingsFormSchema = z.infer<typeof fursSettingsFormSchema>;
38
+
39
+ export type StepType = "settings" | "certificate" | "premises" | "enable";
40
+ export type SectionType =
41
+ | "operator"
42
+ | "fiscalization"
43
+ | "advanced"
44
+ | "certificate-upload"
45
+ | "premises-list"
46
+ | "enable-toggle";
47
+
48
+ interface FursSettingsFormProps extends ComponentTranslationProps {
49
+ entity: Entity;
50
+ onSuccess?: () => void;
51
+ onError?: (error: unknown) => void;
52
+ /**
53
+ * Current active step (optional, for URL state management at page level)
54
+ */
55
+ initialStep?: StepType;
56
+ /**
57
+ * Callback when step changes (optional, for URL state management at page level)
58
+ */
59
+ onStepChange?: (step: StepType) => void;
60
+ /**
61
+ * Optional render prop to wrap each section with help content
62
+ */
63
+ renderSection?: (section: SectionType, content: ReactNode) => ReactNode;
64
+ }
65
+
66
+ /**
67
+ * FURS Settings Form Component
68
+ *
69
+ * Implements progressive unlocking flow:
70
+ * 1. General Settings (Step 1 - always accessible, configure numbering strategy & operator info)
71
+ * 2. Certificate Upload (Step 2 - always accessible, upload P12/PFX certificate)
72
+ * 3. Business Premises (Step 3 - unlocked after valid certificate, register locations & devices)
73
+ * 4. Enable Fiscalization (Step 4 - unlocked when all prerequisites met, final activation step)
74
+ *
75
+ * Smart initial step selection: Opens to first incomplete step, or first step if all complete.
76
+ */
77
+ export const FursSettingsForm: FC<FursSettingsFormProps> = ({
78
+ entity,
79
+ onSuccess,
80
+ onError,
81
+ t: translateFn,
82
+ namespace,
83
+ locale,
84
+ initialStep = "settings",
85
+ onStepChange,
86
+ renderSection,
87
+ }) => {
88
+ // Step navigation state (can be controlled via props for URL sync)
89
+ const [activeStep, setActiveStep] = useState<StepType>(initialStep);
90
+ const [hasInitializedStep, setHasInitializedStep] = useState(false);
91
+
92
+ // Create a guaranteed translation function using the createTranslation utility
93
+ const translate = createTranslation({
94
+ t: translateFn,
95
+ namespace,
96
+ locale,
97
+ translations,
98
+ });
99
+
100
+ // Handle step changes
101
+ const handleStepChange = useCallback(
102
+ (newStep: StepType) => {
103
+ setActiveStep(newStep);
104
+ onStepChange?.(newStep); // Notify parent (e.g., for URL updates)
105
+ },
106
+ [onStepChange],
107
+ );
108
+
109
+ // Fetch FURS settings and premises
110
+ const { data: fursSettings, isLoading: settingsLoading } = useFursSettings(entity.id);
111
+ const { data: premises, isLoading: premisesLoading } = useFursPremises(entity.id);
112
+
113
+ const { mutate: updateSettings, isPending } = useUpdateFursSettings({
114
+ onSuccess: () => {
115
+ onSuccess?.();
116
+ },
117
+ onError: (error) => {
118
+ onError?.(error);
119
+ },
120
+ });
121
+
122
+ const form = useForm<FursSettingsFormSchema>({
123
+ resolver: zodResolver(fursSettingsFormSchema),
124
+ values: {
125
+ enabled: fursSettings?.enabled || false,
126
+ numbering_strategy: fursSettings?.numbering_strategy || "C",
127
+ operator_tax_number: fursSettings?.operator_tax_number || "",
128
+ operator_label: fursSettings?.operator_label || "",
129
+ },
130
+ });
131
+
132
+ // Register with form footer for sticky save button
133
+ useFormFooterRegistration({
134
+ formId: "furs-settings-form",
135
+ isPending,
136
+ isDirty: form.formState.isDirty,
137
+ label: translate("Save Settings"),
138
+ });
139
+
140
+ // Determine completion status
141
+ const hasCertificate = fursSettings?.has_certificate || false;
142
+ const certificateValid = fursSettings?.certificate_status === "valid";
143
+ const hasPremises = (premises?.length || 0) > 0;
144
+ // Check if at least one premise has at least one device
145
+ const hasPremiseWithDevice =
146
+ hasPremises && premises?.some((premise: any) => premise.Devices && premise.Devices.length > 0);
147
+
148
+ // Step unlocking logic (new flow: settings -> certificate -> premises -> enable)
149
+ // - Step 1 (Settings): Always accessible
150
+ // - Step 2 (Certificate): Always accessible
151
+ // - Step 3 (Premises): Requires valid certificate
152
+ // - Step 4 (Enable): Requires certificate + premise + device
153
+ const fursEnabled = fursSettings?.enabled || false;
154
+ const canAccessCertificate = true; // Always accessible
155
+ const canAccessPremises = hasCertificate && certificateValid;
156
+ const canAccessEnable = certificateValid && hasPremises && hasPremiseWithDevice;
157
+
158
+ const steps = [
159
+ {
160
+ id: "settings" as const,
161
+ title: translate("General Settings"),
162
+ complete: true, // Settings are always "complete" - just configuration
163
+ unlocked: true,
164
+ },
165
+ {
166
+ id: "certificate" as const,
167
+ title: translate("Certificate"),
168
+ complete: hasCertificate && certificateValid,
169
+ unlocked: canAccessCertificate,
170
+ },
171
+ {
172
+ id: "premises" as const,
173
+ title: translate("Business Premises"),
174
+ complete: hasPremiseWithDevice,
175
+ unlocked: canAccessPremises,
176
+ },
177
+ {
178
+ id: "enable" as const,
179
+ title: translate("Enable Fiscalization"),
180
+ complete: fursEnabled,
181
+ unlocked: canAccessEnable,
182
+ },
183
+ ];
184
+
185
+ // Smart initial step selection: open to first incomplete step
186
+ const getDefaultStep = (): StepType => {
187
+ // If URL param provided, use it (if valid and unlocked)
188
+ if (initialStep) {
189
+ const stepInfo = steps.find((s) => s.id === initialStep);
190
+ if (stepInfo?.unlocked) return initialStep;
191
+ }
192
+
193
+ // Find first incomplete unlocked step
194
+ if (!certificateValid) return "certificate";
195
+ if (!hasPremiseWithDevice) return "premises";
196
+ if (!fursEnabled) return "enable";
197
+
198
+ // All complete - show first step
199
+ return "settings";
200
+ };
201
+
202
+ // Smart initial step selection on first load (when data is ready)
203
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Intentionally run only when data loads, not on every dep change
204
+ useEffect(() => {
205
+ if (!hasInitializedStep && !settingsLoading && !premisesLoading) {
206
+ const smartStep = getDefaultStep();
207
+ if (smartStep !== activeStep) {
208
+ handleStepChange(smartStep);
209
+ }
210
+ setHasInitializedStep(true);
211
+ }
212
+ }, [settingsLoading, premisesLoading, hasInitializedStep]);
213
+
214
+ // Validate step and redirect if current step becomes locked
215
+ // biome-ignore lint/correctness/useExhaustiveDependencies: steps is recreated on each render but values are stable
216
+ useEffect(() => {
217
+ const currentStepInfo = steps.find((s) => s.id === activeStep);
218
+ if (currentStepInfo && !currentStepInfo.unlocked) {
219
+ // If current step is locked, redirect to first unlocked step
220
+ const firstUnlockedStep = steps.find((s) => s.unlocked);
221
+ if (firstUnlockedStep) {
222
+ handleStepChange(firstUnlockedStep.id);
223
+ }
224
+ }
225
+ }, [activeStep, handleStepChange]);
226
+
227
+ // Check if entity is Slovenian
228
+ if (entity.country_code !== "SI") {
229
+ return (
230
+ <Alert variant="destructive">
231
+ <AlertCircle className="h-4 w-4" />
232
+ <AlertTitle>{translate("FURS is for Slovenian Entities")}</AlertTitle>
233
+ <AlertDescription>
234
+ {translate("FURS fiscalization is only available for entities with country code SI")}
235
+ </AlertDescription>
236
+ </Alert>
237
+ );
238
+ }
239
+
240
+ const onSubmit = (data: FursSettingsFormSchema) => {
241
+ updateSettings({
242
+ entityId: entity.id,
243
+ data,
244
+ });
245
+ };
246
+
247
+ if (settingsLoading || premisesLoading) {
248
+ return <PageLoadingSpinner text={translate("Loading...")} />;
249
+ }
250
+
251
+ // Check if entity is in sandbox (test) mode
252
+ const isSandboxMode = entity.environment === "sandbox";
253
+
254
+ // Shared tabs navigation component - in left column only
255
+ const tabsNavigation = (
256
+ <div className="grid items-start gap-6 lg:grid-cols-[1fr_280px]">
257
+ <Tabs
258
+ value={activeStep}
259
+ onValueChange={(value) => handleStepChange(value as typeof activeStep)}
260
+ className="w-full"
261
+ >
262
+ <TabsList className="grid w-full grid-cols-4 rounded-none p-0">
263
+ {steps.map((step, index) => {
264
+ const isLocked = !step.unlocked;
265
+ let tooltipText = "";
266
+
267
+ if (isLocked) {
268
+ if (step.id === "premises") {
269
+ tooltipText = translate("Upload and validate digital certificate first");
270
+ } else if (step.id === "enable") {
271
+ if (!certificateValid) {
272
+ tooltipText = translate("Upload and validate digital certificate first");
273
+ } else if (!hasPremises) {
274
+ tooltipText = translate("Register at least one business premise first");
275
+ } else {
276
+ tooltipText = translate("Register at least one electronic device first");
277
+ }
278
+ }
279
+ }
280
+
281
+ const trigger = (
282
+ <TabsTrigger
283
+ value={step.id}
284
+ disabled={isLocked}
285
+ className={cn("cursor-pointer justify-center", !step.unlocked && "opacity-50")}
286
+ >
287
+ <span className="flex items-center gap-2">
288
+ {step.complete ? (
289
+ <CheckCircle2 className="h-4 w-4 text-green-500" />
290
+ ) : (
291
+ <span className="text-xs">{index + 1}</span>
292
+ )}
293
+ {step.title}
294
+ </span>
295
+ </TabsTrigger>
296
+ );
297
+
298
+ if (isLocked) {
299
+ return (
300
+ <Tooltip key={step.id} delayDuration={0}>
301
+ <TooltipTrigger asChild>
302
+ <span className="flex cursor-not-allowed justify-center">{trigger}</span>
303
+ </TooltipTrigger>
304
+ <TooltipContent>
305
+ <p>{tooltipText}</p>
306
+ </TooltipContent>
307
+ </Tooltip>
308
+ );
309
+ }
310
+
311
+ return (
312
+ <span key={step.id} className="flex justify-center">
313
+ {trigger}
314
+ </span>
315
+ );
316
+ })}
317
+ </TabsList>
318
+ </Tabs>
319
+ <div className="hidden lg:block" />
320
+ </div>
321
+ );
322
+
323
+ // Helper to wrap section content with render prop if provided
324
+ const wrapSection = (section: SectionType, content: ReactNode) => {
325
+ if (renderSection) {
326
+ return renderSection(section, content);
327
+ }
328
+ return content;
329
+ };
330
+
331
+ return (
332
+ <Form {...form}>
333
+ <form id="furs-settings-form" onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
334
+ {/* Sandbox notice - wrapped in grid for left alignment */}
335
+ {isSandboxMode && (
336
+ <div className="grid items-start gap-6 lg:grid-cols-[1fr_280px]">
337
+ <Alert>
338
+ <Info className="h-4 w-4" />
339
+ <AlertTitle>{translate("Test Mode (Sandbox)")}</AlertTitle>
340
+ <AlertDescription>
341
+ {translate(
342
+ "This entity is in test mode. FURS invoices will be sent to the test/demo environment. No real fiscalization will occur.",
343
+ )}
344
+ </AlertDescription>
345
+ </Alert>
346
+ <div className="hidden lg:block" />
347
+ </div>
348
+ )}
349
+
350
+ {/* Tabs navigation */}
351
+ {tabsNavigation}
352
+
353
+ {/* Step content - each section wrapped with its own help */}
354
+ {activeStep === "settings" && (
355
+ <GeneralSettingsSection
356
+ form={form}
357
+ entity={entity}
358
+ t={translate}
359
+ onSuccess={onSuccess}
360
+ onError={onError}
361
+ wrapSection={wrapSection}
362
+ />
363
+ )}
364
+
365
+ {activeStep === "certificate" && (
366
+ <CertificateSettingsSection
367
+ entity={entity}
368
+ fursSettings={fursSettings}
369
+ t={translate}
370
+ onSuccess={onSuccess}
371
+ onError={onError}
372
+ wrapSection={wrapSection}
373
+ />
374
+ )}
375
+
376
+ {activeStep === "premises" && (
377
+ <div className="space-y-6">
378
+ {!hasCertificate && (
379
+ <Alert>
380
+ <AlertCircle className="h-4 w-4" />
381
+ <AlertTitle>{translate("Certificate Required")}</AlertTitle>
382
+ <AlertDescription>
383
+ {translate("You must upload a digital certificate before you can register business premises")}
384
+ </AlertDescription>
385
+ </Alert>
386
+ )}
387
+ <PremisesManagementSection
388
+ entity={entity}
389
+ premises={(premises || []) as any}
390
+ t={translate}
391
+ onSuccess={onSuccess}
392
+ onError={onError}
393
+ wrapSection={wrapSection}
394
+ />
395
+ </div>
396
+ )}
397
+
398
+ {activeStep === "enable" && (
399
+ <EnableFiscalizationSection
400
+ form={form}
401
+ fursSettings={fursSettings}
402
+ premises={(premises || []) as any}
403
+ t={translate}
404
+ wrapSection={wrapSection}
405
+ />
406
+ )}
407
+ </form>
408
+ </Form>
409
+ );
410
+ };