@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,384 @@
1
+ import { zodResolver } from "@hookform/resolvers/zod";
2
+ import type { CreateEstimateRequest, Estimate } from "@spaceinvoices/js-sdk";
3
+ import { useQueryClient } from "@tanstack/react-query";
4
+ import { RefreshCw } from "lucide-react";
5
+ import type { ReactNode } from "react";
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
+ import type { Resolver } from "react-hook-form";
8
+ import { useForm, useWatch } from "react-hook-form";
9
+ import type { z } from "zod";
10
+ import { Form } from "@/ui/components/ui/form";
11
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/ui/components/ui/tooltip";
12
+ import { createEstimateSchema } from "@/ui/generated/schemas";
13
+ import { useNextDocumentNumber } from "@/ui/hooks/use-next-document-number";
14
+ import type { ComponentTranslationProps } from "@/ui/lib/translation";
15
+ import { createTranslation } from "@/ui/lib/translation";
16
+ import { useEntities } from "@/ui/providers/entities-context";
17
+ import { useFormFooterRegistration } from "@/ui/providers/form-footer-context";
18
+ import { CUSTOMERS_CACHE_KEY } from "../../customers/customers.hooks";
19
+ import {
20
+ DocumentDetailsSection,
21
+ DocumentNoteField,
22
+ DocumentPaymentTermsField,
23
+ } from "../../documents/create/document-details-section";
24
+ import { DocumentItemsSection, type PriceModesMap } from "../../documents/create/document-items-section";
25
+ import { DocumentRecipientSection } from "../../documents/create/document-recipient-section";
26
+ import type { DocumentTypes } from "../../documents/types";
27
+ import { useCreateEstimate } from "../estimates.hooks";
28
+ import de from "./locales/de";
29
+ import sl from "./locales/sl";
30
+ import { prepareEstimateSubmission } from "./prepare-estimate-submission";
31
+ import { useEstimateCustomerForm } from "./use-estimate-customer-form";
32
+
33
+ const translations = {
34
+ sl,
35
+ de,
36
+ } as const;
37
+
38
+ // Form values: extend schema with local-only fields (number is for display, not sent to API)
39
+ type CreateEstimateFormValues = z.infer<typeof createEstimateSchema> & {
40
+ number?: string;
41
+ };
42
+
43
+ /** Preview payload extends request with display-only fields */
44
+ type EstimatePreviewPayload = Partial<CreateEstimateRequest> & { number?: string };
45
+
46
+ type CreateEstimateFormProps = {
47
+ type: DocumentTypes;
48
+ entityId: string;
49
+ onSuccess?: (data: Estimate) => void;
50
+ onError?: (error: unknown) => void;
51
+ onChange?: (data: EstimatePreviewPayload) => void;
52
+ onAddNewTax?: () => void;
53
+ /** Callback to update header action (title toggle) */
54
+ onHeaderActionChange?: (action: ReactNode) => void;
55
+ /** Initial values for form fields (used for document duplication) */
56
+ initialValues?: Partial<CreateEstimateRequest>;
57
+ } & ComponentTranslationProps;
58
+
59
+ export default function CreateEstimateForm({
60
+ type,
61
+ entityId,
62
+ onSuccess,
63
+ onError,
64
+ onChange,
65
+ onAddNewTax,
66
+ onHeaderActionChange,
67
+ initialValues,
68
+ t: translateProp,
69
+ namespace,
70
+ locale,
71
+ }: CreateEstimateFormProps) {
72
+ const t = createTranslation({
73
+ t: translateProp,
74
+ namespace,
75
+ locale,
76
+ translations,
77
+ });
78
+
79
+ const { activeEntity } = useEntities();
80
+ const queryClient = useQueryClient();
81
+
82
+ // Title type state: "estimate" (default) or "quote"
83
+ const [titleType, setTitleType] = useState<"estimate" | "quote">((initialValues as any)?.title_type || "estimate");
84
+
85
+ // Draft submission state
86
+ const [isDraftPending, setIsDraftPending] = useState(false);
87
+
88
+ // Get default estimate note from entity settings
89
+ const defaultEstimateNote = (activeEntity?.settings as any)?.default_estimate_note || "";
90
+
91
+ // Fetch next estimate number
92
+ const { data: nextNumberData } = useNextDocumentNumber(entityId, "estimate", {
93
+ enabled: !!entityId,
94
+ });
95
+
96
+ // Get default payment terms from entity settings
97
+ const defaultPaymentTerms = (activeEntity?.settings as any)?.default_estimate_payment_terms || "";
98
+
99
+ const form = useForm<CreateEstimateFormValues>({
100
+ resolver: zodResolver(createEstimateSchema) as Resolver<CreateEstimateFormValues>,
101
+ defaultValues: {
102
+ number: "",
103
+ date: initialValues?.date || new Date().toISOString(),
104
+ customer_id: initialValues?.customer_id ?? undefined,
105
+ // Cast customer to form schema type (API type may have additional fields)
106
+ customer: (initialValues?.customer as CreateEstimateFormValues["customer"]) ?? undefined,
107
+ items: initialValues?.items?.length
108
+ ? initialValues.items.map((item) => ({
109
+ name: item.name || "",
110
+ description: item.description || "",
111
+ quantity: item.quantity ?? 1,
112
+ // Use gross_price if set, otherwise use price
113
+ price: item.gross_price ?? item.price,
114
+ taxes: item.taxes || [],
115
+ }))
116
+ : [
117
+ {
118
+ name: "",
119
+ description: "",
120
+ quantity: 1,
121
+ price: undefined,
122
+ taxes: [],
123
+ },
124
+ ],
125
+ currency_code: initialValues?.currency_code || activeEntity?.currency_code || "EUR",
126
+ note: initialValues?.note ?? defaultEstimateNote,
127
+ payment_terms: initialValues?.payment_terms ?? defaultPaymentTerms,
128
+ },
129
+ });
130
+
131
+ // Price modes per item (gross vs net) - collected from component state at submit
132
+ const onHeaderActionChangeRef = useRef(onHeaderActionChange);
133
+ onHeaderActionChangeRef.current = onHeaderActionChange;
134
+
135
+ const onChangeRef = useRef(onChange);
136
+ onChangeRef.current = onChange;
137
+
138
+ const initialPriceModes = useMemo(() => {
139
+ if (!initialValues?.items) return {};
140
+ return initialValues.items.reduce((acc, item, index) => {
141
+ acc[index] = item.gross_price != null;
142
+ return acc;
143
+ }, {} as PriceModesMap);
144
+ }, [initialValues?.items]);
145
+ const priceModesRef = useRef<PriceModesMap>(initialPriceModes);
146
+
147
+ // Update number when fetched
148
+ useEffect(() => {
149
+ if (nextNumberData?.number) {
150
+ form.setValue("number", nextNumberData.number);
151
+ }
152
+ }, [nextNumberData?.number, form]);
153
+
154
+ // Update default note when entity loads
155
+ useEffect(() => {
156
+ const entityDefaultNote = (activeEntity?.settings as any)?.default_estimate_note;
157
+ if (entityDefaultNote && !form.getValues("note")) {
158
+ form.setValue("note", entityDefaultNote);
159
+ }
160
+ }, [activeEntity, form]);
161
+
162
+ // Auto-add tax field for tax subject entities
163
+ useEffect(() => {
164
+ if (activeEntity?.is_tax_subject) {
165
+ const items = form.getValues("items") || [];
166
+ if (items.length > 0 && (!items[0].taxes || items[0].taxes.length === 0)) {
167
+ form.setValue("items.0.taxes", [{ tax_id: undefined }]);
168
+ }
169
+ }
170
+ }, [activeEntity?.is_tax_subject, form]);
171
+
172
+ // Update header with clickable title toggle
173
+ useEffect(() => {
174
+ const callback = onHeaderActionChangeRef.current;
175
+ if (!callback) return;
176
+
177
+ const toggleTitle = () => {
178
+ setTitleType((prev) => (prev === "estimate" ? "quote" : "estimate"));
179
+ };
180
+
181
+ callback(
182
+ <TooltipProvider>
183
+ <Tooltip>
184
+ <TooltipTrigger asChild>
185
+ <button
186
+ type="button"
187
+ onClick={toggleTitle}
188
+ className="flex cursor-pointer items-center gap-2 font-bold text-2xl hover:text-primary"
189
+ >
190
+ {titleType === "estimate" ? t("Create Estimate") : t("Create Quote")}
191
+ <RefreshCw className="size-4 text-muted-foreground" />
192
+ </button>
193
+ </TooltipTrigger>
194
+ <TooltipContent side="bottom">
195
+ {titleType === "estimate" ? t("Click to switch to Quote") : t("Click to switch to Estimate")}
196
+ </TooltipContent>
197
+ </Tooltip>
198
+ </TooltipProvider>,
199
+ );
200
+ }, [titleType, t]);
201
+
202
+ const formValues = useWatch({
203
+ control: form.control,
204
+ });
205
+
206
+ // Extract customer management logic into a custom hook
207
+ const {
208
+ originalCustomer,
209
+ showCustomerForm,
210
+ shouldFocusName,
211
+ selectedCustomerId,
212
+ initialCustomerName,
213
+ handleCustomerSelect,
214
+ handleCustomerClear,
215
+ } = useEstimateCustomerForm(form as any);
216
+
217
+ const { mutate: createEstimate, isPending } = useCreateEstimate({
218
+ entityId,
219
+ onSuccess: (data) => {
220
+ // Invalidate customers cache when a customer was created/linked
221
+ if (data.customer_id) {
222
+ queryClient.invalidateQueries({ queryKey: [CUSTOMERS_CACHE_KEY] });
223
+ }
224
+ onSuccess?.(data);
225
+ },
226
+ onError,
227
+ });
228
+
229
+ // Shared submit logic for both regular save and save as draft
230
+ const submitEstimate = useCallback(
231
+ (values: CreateEstimateFormValues, isDraft: boolean) => {
232
+ try {
233
+ const submission = prepareEstimateSubmission(values, {
234
+ originalCustomer,
235
+ priceModes: priceModesRef.current,
236
+ titleType,
237
+ isDraft,
238
+ });
239
+ createEstimate(submission);
240
+ } catch (error) {
241
+ console.error("Estimate submission error:", error);
242
+ if (onError) {
243
+ onError(error);
244
+ }
245
+ }
246
+ },
247
+ [createEstimate, onError, originalCustomer, titleType],
248
+ );
249
+
250
+ // Handle save as draft
251
+ const handleSaveAsDraft = useCallback(async () => {
252
+ setIsDraftPending(true);
253
+ try {
254
+ const isValid = await form.trigger();
255
+ if (isValid) {
256
+ const values = form.getValues();
257
+ submitEstimate(values, true);
258
+ }
259
+ } finally {
260
+ setIsDraftPending(false);
261
+ }
262
+ }, [form, submitEstimate]);
263
+
264
+ const secondaryAction = useMemo(
265
+ () => ({
266
+ label: t("Save as Draft"),
267
+ onClick: handleSaveAsDraft,
268
+ isPending: isDraftPending,
269
+ }),
270
+ [t, handleSaveAsDraft, isDraftPending],
271
+ );
272
+
273
+ useFormFooterRegistration({
274
+ formId: "create-estimate-form",
275
+ isPending,
276
+ isDirty: form.formState.isDirty,
277
+ label: titleType === "estimate" ? t("Create Estimate") : t("Create Quote"),
278
+ secondaryAction,
279
+ });
280
+
281
+ // Set default payment terms from entity settings when entity data is available
282
+ useEffect(() => {
283
+ const entityDefaultPaymentTerms = (activeEntity?.settings as any)?.default_estimate_payment_terms;
284
+ if (entityDefaultPaymentTerms && !form.getValues("payment_terms")) {
285
+ form.setValue("payment_terms", entityDefaultPaymentTerms);
286
+ }
287
+ }, [activeEntity, form]);
288
+
289
+ useEffect(() => {
290
+ const callback = onChangeRef.current;
291
+ if (!callback) return;
292
+
293
+ const currentItems = form.getValues("items") || [];
294
+
295
+ // Transform items to use gross_price when price mode is gross
296
+ const transformedItems = currentItems.map((item: any, index: number) => {
297
+ const { price, ...rest } = item;
298
+ const isGross = priceModesRef.current[index] ?? false;
299
+ if (isGross) {
300
+ return { ...rest, gross_price: price };
301
+ }
302
+ return { ...rest, price };
303
+ });
304
+
305
+ const payload: EstimatePreviewPayload = {
306
+ number: formValues.number,
307
+ date: formValues.date,
308
+ customer_id: formValues.customer_id,
309
+ customer: formValues.customer,
310
+ items: transformedItems,
311
+ currency_code: formValues.currency_code,
312
+ note: formValues.note,
313
+ payment_terms: formValues.payment_terms,
314
+ title_type: titleType,
315
+ };
316
+ callback(payload);
317
+ }, [formValues, form, titleType]);
318
+
319
+ const onSubmit = (values: CreateEstimateFormValues) => {
320
+ submitEstimate(values, false);
321
+ };
322
+
323
+ return (
324
+ <Form {...form}>
325
+ <form id="create-estimate-form" onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
326
+ <div className="flex w-full flex-col md:flex-row md:gap-6">
327
+ <DocumentRecipientSection
328
+ control={form.control}
329
+ entityId={entityId}
330
+ onCustomerSelect={handleCustomerSelect}
331
+ onCustomerClear={handleCustomerClear}
332
+ showCustomerForm={showCustomerForm}
333
+ shouldFocusName={shouldFocusName}
334
+ selectedCustomerId={selectedCustomerId}
335
+ initialCustomerName={initialCustomerName}
336
+ t={t}
337
+ />
338
+
339
+ <DocumentDetailsSection control={form.control} documentType={type} t={t} />
340
+ </div>
341
+
342
+ <DocumentItemsSection
343
+ control={form.control}
344
+ watch={form.watch}
345
+ setValue={form.setValue}
346
+ getValues={form.getValues}
347
+ entityId={entityId}
348
+ currencyCode={activeEntity?.currency_code ?? undefined}
349
+ onAddNewTax={onAddNewTax}
350
+ t={t}
351
+ maxTaxesPerItem={activeEntity?.country_rules?.max_taxes_per_item}
352
+ priceModesRef={priceModesRef}
353
+ initialPriceModes={initialPriceModes}
354
+ />
355
+
356
+ <DocumentNoteField
357
+ control={form.control}
358
+ t={t}
359
+ entity={activeEntity}
360
+ document={{
361
+ number: formValues.number,
362
+ date: formValues.date,
363
+ date_valid_till: formValues.date_valid_till,
364
+ currency_code: formValues.currency_code,
365
+ customer: formValues.customer as any,
366
+ }}
367
+ />
368
+
369
+ <DocumentPaymentTermsField
370
+ control={form.control}
371
+ t={t}
372
+ entity={activeEntity}
373
+ document={{
374
+ number: formValues.number,
375
+ date: formValues.date,
376
+ date_valid_till: formValues.date_valid_till,
377
+ currency_code: formValues.currency_code,
378
+ customer: formValues.customer as any,
379
+ }}
380
+ />
381
+ </form>
382
+ </Form>
383
+ );
384
+ }
@@ -0,0 +1,64 @@
1
+ export default {
2
+ "Create Estimate": "Angebot erstellen",
3
+ "Create Quote": "Angebot erstellen",
4
+ Estimate: "Angebot",
5
+ Quote: "Angebot",
6
+ "Click to switch to Quote": "Klicken Sie, um zu Angebot zu wechseln",
7
+ "Click to switch to Estimate": "Klicken Sie, um zu Angebot zu wechseln",
8
+ Details: "Details",
9
+ Number: "Nummer",
10
+ Date: "Datum",
11
+ "Due Date": "Fälligkeitsdatum",
12
+ "Valid Until": "Gültig bis",
13
+ Recipient: "Empfänger",
14
+ "Search or create customer...": "Kunde suchen oder erstellen...",
15
+ Clear: "Löschen",
16
+ Name: "Name",
17
+ Address: "Adresse",
18
+ "Address 2": "Adresse 2",
19
+ "Post Code": "Postleitzahl",
20
+ City: "Stadt",
21
+ State: "Bundesland",
22
+ Country: "Land",
23
+ "Tax Number": "Steuernummer",
24
+ Items: "Positionen",
25
+ "Add item": "Position hinzufügen",
26
+ Tax: "Steuer",
27
+ "Tax rate": "Steuersatz",
28
+ "Add tax": "Steuer hinzufügen",
29
+ "Select tax...": "Steuer auswählen...",
30
+ "Loading...": "Laden...",
31
+ "No taxes found": "Keine Steuern gefunden",
32
+ "Add new tax...": "Neue Steuer hinzufügen...",
33
+ // Note field
34
+ Note: "Notiz",
35
+ "Insert variable": "Variable einfügen",
36
+ "Add payment instructions, terms, or other notes...":
37
+ "Zahlungsanweisungen, Bedingungen oder andere Notizen hinzufügen...",
38
+ // Payment terms field
39
+ "Payment Terms": "Zahlungsbedingungen",
40
+ "Add payment terms...": "Zahlungsbedingungen hinzufügen...",
41
+ // Document item fields
42
+ Quantity: "Menge",
43
+ Price: "Preis",
44
+ Unit: "Einheit",
45
+ Discount: "Rabatt",
46
+ Description: "Beschreibung",
47
+ Save: "Speichern",
48
+ "Save as Draft": "Als Entwurf speichern",
49
+ "Pick a date": "Datum auswählen",
50
+ // Currency
51
+ Currency: "Währung",
52
+ "Select currency": "Währung auswählen",
53
+ // Discount types
54
+ "Percentage discount": "Prozentualer Rabatt",
55
+ "Fixed amount discount": "Fester Rabatt",
56
+ Percentage: "Prozent",
57
+ "Fixed amount": "Fester Betrag",
58
+ "Taxes disabled": "Steuern deaktiviert",
59
+ // Gross price support
60
+ "Gross price": "Bruttopreis",
61
+ "Net price": "Nettopreis",
62
+ "Gross price (tax included)": "Bruttopreis (inkl. MwSt.)",
63
+ "Net price (before tax)": "Nettopreis (exkl. MwSt.)",
64
+ } as const;
@@ -0,0 +1,63 @@
1
+ export default {
2
+ "Create Estimate": "Ustvari predračun",
3
+ "Create Quote": "Ustvari ponudbo",
4
+ Estimate: "Predračun",
5
+ Quote: "Ponudba",
6
+ "Click to switch to Quote": "Kliknite za zamenjavo v Ponudbo",
7
+ "Click to switch to Estimate": "Kliknite za zamenjavo v Predračun",
8
+ Details: "Podrobnosti",
9
+ Number: "Številka",
10
+ Date: "Datum",
11
+ "Due Date": "Rok plačila",
12
+ "Valid Until": "Veljavno do",
13
+ Recipient: "Prejemnik",
14
+ "Search or create customer...": "Išči ali ustvari stranko...",
15
+ Clear: "Počisti",
16
+ Name: "Ime",
17
+ Address: "Naslov",
18
+ "Address 2": "Naslov 2",
19
+ "Post Code": "Poštna številka",
20
+ City: "Mesto",
21
+ State: "Regija",
22
+ Country: "Država",
23
+ "Tax Number": "Davčna številka",
24
+ Items: "Postavke",
25
+ "Add item": "Dodaj postavko",
26
+ Tax: "Davek",
27
+ "Tax rate": "Davčna stopnja",
28
+ "Add tax": "Dodaj davek",
29
+ "Select tax...": "Izberi davek...",
30
+ "Loading...": "Nalaganje...",
31
+ "No taxes found": "Ni najdenih davkov",
32
+ "Add new tax...": "Dodaj nov davek...",
33
+ // Note field
34
+ Note: "Opomba",
35
+ "Insert variable": "Vstavi spremenljivko",
36
+ "Add payment instructions, terms, or other notes...": "Dodajte navodila za plačilo, pogoje ali druge opombe...",
37
+ // Payment terms field
38
+ "Payment Terms": "Plačilni pogoji",
39
+ "Add payment terms...": "Dodajte plačilne pogoje...",
40
+ // Document item fields
41
+ Quantity: "Količina",
42
+ Price: "Cena",
43
+ Unit: "Enota",
44
+ Discount: "Popust",
45
+ Description: "Opis",
46
+ Save: "Shrani",
47
+ "Save as Draft": "Shrani kot osnutek",
48
+ "Pick a date": "Izberi datum",
49
+ // Currency
50
+ Currency: "Valuta",
51
+ "Select currency": "Izberi valuto",
52
+ // Discount types
53
+ "Percentage discount": "Odstotni popust",
54
+ "Fixed amount discount": "Fiksni popust",
55
+ Percentage: "Odstotek",
56
+ "Fixed amount": "Fiksni znesek",
57
+ "Taxes disabled": "Davki onemogočeni",
58
+ // Gross price support
59
+ "Gross price": "Bruto cena",
60
+ "Net price": "Neto cena",
61
+ "Gross price (tax included)": "Bruto cena (z davkom)",
62
+ "Net price (before tax)": "Neto cena (brez davka)",
63
+ } as const;
@@ -0,0 +1,39 @@
1
+ import type { CreateEstimateRequest } from "@spaceinvoices/js-sdk";
2
+ import type { CreateEstimateSchema } from "@/ui/generated/schemas";
3
+ import { prepareDocumentSubmission } from "../../documents/create/prepare-document-submission";
4
+ import type { CustomerData } from "../../documents/create/use-document-customer-form";
5
+
6
+ /** Map of item index to gross price mode */
7
+ type PriceModesMap = Record<number, boolean>;
8
+
9
+ type PrepareOptions = {
10
+ originalCustomer: CustomerData | null;
11
+ /** Map of item index to gross price mode (collected from component state) */
12
+ priceModes?: PriceModesMap;
13
+ /** Title type: "estimate" or "quote" */
14
+ titleType?: "estimate" | "quote";
15
+ /** Whether to save as draft (skips numbering) */
16
+ isDraft?: boolean;
17
+ };
18
+
19
+ /**
20
+ * Prepares estimate form data for API submission
21
+ * Handles customer data transformation (no payment data for estimates)
22
+ */
23
+ export function prepareEstimateSubmission(
24
+ values: CreateEstimateSchema,
25
+ options: PrepareOptions,
26
+ ): CreateEstimateRequest {
27
+ const baseSubmission = prepareDocumentSubmission(values, {
28
+ originalCustomer: options.originalCustomer,
29
+ documentType: "estimate",
30
+ secondaryDate: values.date_valid_till,
31
+ priceModes: options.priceModes,
32
+ isDraft: options.isDraft,
33
+ }) as CreateEstimateRequest;
34
+
35
+ return {
36
+ ...baseSubmission,
37
+ title_type: options.titleType,
38
+ };
39
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @deprecated Use useDocumentCustomerForm from documents/create instead
3
+ * This re-export exists for backward compatibility
4
+ */
5
+ export { useDocumentCustomerForm as useEstimateCustomerForm } from "../../documents/create/use-document-customer-form";
@@ -0,0 +1,15 @@
1
+ import type { CreateEstimateRequest, Estimate } from "@spaceinvoices/js-sdk";
2
+
3
+ import { createResourceHooks } from "@/ui/hooks/create-resource-hooks";
4
+
5
+ // Define a constant for the estimates cache key
6
+ export const ESTIMATES_CACHE_KEY = "estimates";
7
+
8
+ // Create estimate-specific hooks using the factory
9
+ const {
10
+ useCreateResource: useCreateEstimate,
11
+ useUpdateResource: useUpdateEstimate,
12
+ useDeleteResource: useDeleteEstimate,
13
+ } = createResourceHooks<Estimate, CreateEstimateRequest>("estimates", ESTIMATES_CACHE_KEY);
14
+
15
+ export { useCreateEstimate, useUpdateEstimate, useDeleteEstimate };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Estimates components - Main exports
3
+ */
4
+ export { default as CreateEstimateForm } from "./create/create-estimate-form";
5
+ export { ESTIMATES_CACHE_KEY, useCreateEstimate, useDeleteEstimate, useUpdateEstimate } from "./estimates.hooks";
6
+ export { default as EstimateListTable } from "./list/list-table";
@@ -0,0 +1,3 @@
1
+ export { default as EstimateListRowActions } from "./list-row-actions";
2
+ export { default as EstimateListTable } from "./list-table";
3
+ export { useEstimateDownload } from "./use-estimate-download";
@@ -0,0 +1,103 @@
1
+ import type { Estimate } from "@spaceinvoices/js-sdk";
2
+
3
+ import { Copy, Download, Eye, Link2Off, Loader2, MoreHorizontal } from "lucide-react";
4
+ import { Button } from "@/ui/components/ui/button";
5
+ import {
6
+ DropdownMenu,
7
+ DropdownMenuContent,
8
+ DropdownMenuGroup,
9
+ DropdownMenuItem,
10
+ DropdownMenuLabel,
11
+ DropdownMenuSeparator,
12
+ DropdownMenuTrigger,
13
+ } from "@/ui/components/ui/dropdown-menu";
14
+ import type { ComponentTranslationProps } from "@/ui/lib/translation";
15
+ import { createTranslation } from "@/ui/lib/translation";
16
+ import { useEstimateDownload } from "./use-estimate-download";
17
+
18
+ type EstimateListRowActionsProps = {
19
+ estimate: Estimate;
20
+ onDuplicate?: (estimate: Estimate) => void;
21
+ onDownloadStart?: () => void;
22
+ onDownloadSuccess?: (fileName: string) => void;
23
+ onDownloadError?: (error: string) => void;
24
+ onUnshare?: (estimate: Estimate) => Promise<void>;
25
+ isUnsharing?: boolean;
26
+ } & ComponentTranslationProps;
27
+
28
+ export default function EstimateListRowActions({
29
+ estimate,
30
+ onDuplicate,
31
+ onDownloadStart,
32
+ onDownloadSuccess,
33
+ onDownloadError,
34
+ onUnshare,
35
+ isUnsharing,
36
+ ...i18nProps
37
+ }: EstimateListRowActionsProps) {
38
+ const t = createTranslation(i18nProps);
39
+ const { isDownloading, downloadPDF } = useEstimateDownload({
40
+ onDownloadStart,
41
+ onDownloadSuccess,
42
+ onDownloadError,
43
+ ...i18nProps,
44
+ });
45
+
46
+ return (
47
+ <DropdownMenu>
48
+ <DropdownMenuTrigger asChild>
49
+ <Button variant="ghost" className="h-8 w-8 cursor-pointer p-0" id="action-menu-trigger">
50
+ <span className="sr-only">{t("Open menu")}</span>
51
+ <MoreHorizontal className="h-4 w-4" />
52
+ </Button>
53
+ </DropdownMenuTrigger>
54
+ <DropdownMenuContent align="end">
55
+ <DropdownMenuLabel>{t("Actions")}</DropdownMenuLabel>
56
+ <DropdownMenuGroup>
57
+ <DropdownMenuItem className="cursor-pointer" onClick={() => navigator.clipboard.writeText(estimate.id)}>
58
+ <Copy className="h-4 w-4" />
59
+ {t("Copy estimate ID")}
60
+ </DropdownMenuItem>
61
+ </DropdownMenuGroup>
62
+ <DropdownMenuSeparator />
63
+ <DropdownMenuGroup>
64
+ <DropdownMenuItem
65
+ className="cursor-pointer"
66
+ onClick={() => {
67
+ // TODO: Use router
68
+ window.location.href = `/app/estimates/view/${estimate.id}`;
69
+ }}
70
+ >
71
+ <Eye className="h-4 w-4" />
72
+ {t("View estimate")}
73
+ </DropdownMenuItem>
74
+ <DropdownMenuItem className="cursor-pointer" onClick={() => downloadPDF(estimate)} disabled={isDownloading}>
75
+ <Download className="h-4 w-4" />
76
+ {isDownloading ? t("Downloading...") : t("Download PDF")}
77
+ </DropdownMenuItem>
78
+ {onDuplicate && (
79
+ <DropdownMenuItem className="cursor-pointer" onClick={() => onDuplicate(estimate)}>
80
+ <Copy className="h-4 w-4" />
81
+ {t("Duplicate")}
82
+ </DropdownMenuItem>
83
+ )}
84
+ </DropdownMenuGroup>
85
+ {estimate.shareable_id && onUnshare && (
86
+ <>
87
+ <DropdownMenuSeparator />
88
+ <DropdownMenuGroup>
89
+ <DropdownMenuItem
90
+ className="cursor-pointer text-destructive focus:text-destructive"
91
+ onClick={() => onUnshare(estimate)}
92
+ disabled={isUnsharing}
93
+ >
94
+ {isUnsharing ? <Loader2 className="h-4 w-4 animate-spin" /> : <Link2Off className="h-4 w-4" />}
95
+ {t("Unshare")}
96
+ </DropdownMenuItem>
97
+ </DropdownMenuGroup>
98
+ </>
99
+ )}
100
+ </DropdownMenuContent>
101
+ </DropdownMenu>
102
+ );
103
+ }