@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,922 @@
1
+ #!/usr/bin/env node
2
+
3
+ // cli/src/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // cli/src/commands/init.ts
7
+ import fs4 from "fs";
8
+ import path4 from "path";
9
+ import prompts from "prompts";
10
+
11
+ // cli/src/utils/config.ts
12
+ import fs from "fs";
13
+ import path from "path";
14
+ var CONFIG_FILE = "spaceinvoices.json";
15
+ var DEFAULT_CONFIG = {
16
+ $schema: "https://raw.githubusercontent.com/space-invoices/react-ui/main/spaceinvoices.schema.json",
17
+ aliases: {
18
+ components: "@/components/space-invoices",
19
+ ui: "@/components/ui",
20
+ lib: "@/lib",
21
+ hooks: "@/hooks",
22
+ providers: "@/providers"
23
+ }
24
+ };
25
+ function getConfigPath(cwd = process.cwd()) {
26
+ return path.join(cwd, CONFIG_FILE);
27
+ }
28
+ function configExists(cwd = process.cwd()) {
29
+ return fs.existsSync(getConfigPath(cwd));
30
+ }
31
+ function readConfig(cwd = process.cwd()) {
32
+ const configPath = getConfigPath(cwd);
33
+ if (!fs.existsSync(configPath)) {
34
+ return null;
35
+ }
36
+ try {
37
+ const content = fs.readFileSync(configPath, "utf-8");
38
+ return JSON.parse(content);
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+ function writeConfig(config, cwd = process.cwd()) {
44
+ const configPath = getConfigPath(cwd);
45
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
46
+ }
47
+
48
+ // cli/src/utils/installer.ts
49
+ import { spawn } from "child_process";
50
+ import fs2 from "fs";
51
+ import path2 from "path";
52
+ function detectPackageManager(cwd = process.cwd()) {
53
+ if (fs2.existsSync(path2.join(cwd, "bun.lockb")) || fs2.existsSync(path2.join(cwd, "bun.lock"))) {
54
+ return "bun";
55
+ }
56
+ if (fs2.existsSync(path2.join(cwd, "pnpm-lock.yaml"))) {
57
+ return "pnpm";
58
+ }
59
+ if (fs2.existsSync(path2.join(cwd, "yarn.lock"))) {
60
+ return "yarn";
61
+ }
62
+ if (fs2.existsSync(path2.join(cwd, "package-lock.json"))) {
63
+ return "npm";
64
+ }
65
+ const packageJsonPath = path2.join(cwd, "package.json");
66
+ if (fs2.existsSync(packageJsonPath)) {
67
+ try {
68
+ const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
69
+ if (packageJson.packageManager) {
70
+ if (packageJson.packageManager.startsWith("bun")) return "bun";
71
+ if (packageJson.packageManager.startsWith("pnpm")) return "pnpm";
72
+ if (packageJson.packageManager.startsWith("yarn")) return "yarn";
73
+ if (packageJson.packageManager.startsWith("npm")) return "npm";
74
+ }
75
+ } catch {
76
+ }
77
+ }
78
+ return "npm";
79
+ }
80
+ function getInstallCommand(pm, packages, options = {}) {
81
+ const devFlag = options.dev ? " -D" : "";
82
+ switch (pm) {
83
+ case "bun":
84
+ return `bun add${devFlag} ${packages.join(" ")}`;
85
+ case "pnpm":
86
+ return `pnpm add${devFlag} ${packages.join(" ")}`;
87
+ case "yarn":
88
+ return `yarn add${devFlag} ${packages.join(" ")}`;
89
+ case "npm":
90
+ default:
91
+ return `npm install${options.dev ? " --save-dev" : ""} ${packages.join(" ")}`;
92
+ }
93
+ }
94
+ async function installPackages(packages, options = {}) {
95
+ if (packages.length === 0) return;
96
+ const cwd = options.cwd ?? process.cwd();
97
+ const pm = detectPackageManager(cwd);
98
+ const command = getInstallCommand(pm, packages, { dev: options.dev });
99
+ return new Promise((resolve, reject) => {
100
+ const [cmd, ...args] = command.split(" ");
101
+ const child = spawn(cmd, args, {
102
+ cwd,
103
+ stdio: "inherit",
104
+ shell: true
105
+ });
106
+ child.on("close", (code) => {
107
+ if (code === 0) {
108
+ resolve();
109
+ } else {
110
+ reject(new Error(`Package installation failed with code ${code}`));
111
+ }
112
+ });
113
+ child.on("error", (err) => {
114
+ reject(err);
115
+ });
116
+ });
117
+ }
118
+ function getInstalledPackages(cwd = process.cwd()) {
119
+ const packageJsonPath = path2.join(cwd, "package.json");
120
+ if (!fs2.existsSync(packageJsonPath)) {
121
+ return /* @__PURE__ */ new Set();
122
+ }
123
+ try {
124
+ const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
125
+ const deps = /* @__PURE__ */ new Set();
126
+ if (packageJson.dependencies) {
127
+ for (const dep of Object.keys(packageJson.dependencies)) {
128
+ deps.add(dep);
129
+ }
130
+ }
131
+ if (packageJson.devDependencies) {
132
+ for (const dep of Object.keys(packageJson.devDependencies)) {
133
+ deps.add(dep);
134
+ }
135
+ }
136
+ return deps;
137
+ } catch {
138
+ return /* @__PURE__ */ new Set();
139
+ }
140
+ }
141
+ function filterNewPackages(packages, cwd = process.cwd()) {
142
+ const installed = getInstalledPackages(cwd);
143
+ return packages.filter((pkg) => !installed.has(pkg));
144
+ }
145
+
146
+ // cli/src/utils/logger.ts
147
+ import pc from "picocolors";
148
+ var logger = {
149
+ info: (msg) => console.log(pc.blue("info"), msg),
150
+ success: (msg) => console.log(pc.green("success"), msg),
151
+ warn: (msg) => console.log(pc.yellow("warn"), msg),
152
+ error: (msg) => console.log(pc.red("error"), msg),
153
+ log: (msg) => console.log(msg),
154
+ break: () => console.log("")
155
+ };
156
+ function spinner(text) {
157
+ const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
158
+ let i = 0;
159
+ let intervalId = null;
160
+ return {
161
+ start: () => {
162
+ process.stdout.write(`\r${pc.cyan(frames[0])} ${text}`);
163
+ intervalId = setInterval(() => {
164
+ i = (i + 1) % frames.length;
165
+ process.stdout.write(`\r${pc.cyan(frames[i])} ${text}`);
166
+ }, 80);
167
+ },
168
+ stop: (finalText) => {
169
+ if (intervalId) {
170
+ clearInterval(intervalId);
171
+ intervalId = null;
172
+ }
173
+ process.stdout.write(`\r${" ".repeat(text.length + 4)}\r`);
174
+ if (finalText) {
175
+ console.log(finalText);
176
+ }
177
+ },
178
+ succeed: (msg) => {
179
+ if (intervalId) {
180
+ clearInterval(intervalId);
181
+ intervalId = null;
182
+ }
183
+ process.stdout.write(`\r${" ".repeat(text.length + 4)}\r`);
184
+ console.log(`${pc.green("\u2713")} ${msg || text}`);
185
+ },
186
+ fail: (msg) => {
187
+ if (intervalId) {
188
+ clearInterval(intervalId);
189
+ intervalId = null;
190
+ }
191
+ process.stdout.write(`\r${" ".repeat(text.length + 4)}\r`);
192
+ console.log(`${pc.red("\u2717")} ${msg || text}`);
193
+ }
194
+ };
195
+ }
196
+ function highlight(text) {
197
+ return pc.cyan(text);
198
+ }
199
+ function dim(text) {
200
+ return pc.dim(text);
201
+ }
202
+
203
+ // cli/src/utils/registry.ts
204
+ import fs3 from "fs";
205
+ import path3 from "path";
206
+ var REGISTRY_BASE_URL = "https://raw.githubusercontent.com/space-invoices/react-ui/main";
207
+ var localBasePath = null;
208
+ function setLocalBasePath(basePath) {
209
+ localBasePath = basePath;
210
+ }
211
+ var cachedRegistry = null;
212
+ async function fetchRegistry() {
213
+ if (cachedRegistry) {
214
+ return cachedRegistry;
215
+ }
216
+ if (localBasePath) {
217
+ const registryPath = path3.join(localBasePath, "registry.json");
218
+ const content = fs3.readFileSync(registryPath, "utf-8");
219
+ cachedRegistry = JSON.parse(content);
220
+ return cachedRegistry;
221
+ }
222
+ const response = await fetch(`${REGISTRY_BASE_URL}/registry.json`);
223
+ if (!response.ok) {
224
+ throw new Error(`Failed to fetch registry: ${response.statusText}`);
225
+ }
226
+ cachedRegistry = await response.json();
227
+ return cachedRegistry;
228
+ }
229
+ async function fetchFile(filePath) {
230
+ if (localBasePath) {
231
+ const fullPath = path3.join(localBasePath, "src", filePath);
232
+ return fs3.readFileSync(fullPath, "utf-8");
233
+ }
234
+ const url = `${REGISTRY_BASE_URL}/src/${filePath}`;
235
+ const response = await fetch(url);
236
+ if (!response.ok) {
237
+ throw new Error(`Failed to fetch file ${filePath}: ${response.statusText}`);
238
+ }
239
+ return response.text();
240
+ }
241
+ function getComponent(registry, name) {
242
+ return registry.components[name] ?? null;
243
+ }
244
+ function getProvider(registry, name) {
245
+ const providerName = name.replace(/^providers\//, "");
246
+ return registry.providers[providerName] ?? null;
247
+ }
248
+ function getUtil(registry, name) {
249
+ const utilName = name.replace(/^utils\//, "");
250
+ return registry.utils[utilName] ?? null;
251
+ }
252
+ function listComponents(registry) {
253
+ return Object.keys(registry.components);
254
+ }
255
+ function getComponentsByCategory(registry) {
256
+ const result = {};
257
+ for (const [key, component] of Object.entries(registry.components)) {
258
+ const category = component.category;
259
+ if (!result[category]) {
260
+ result[category] = {
261
+ name: registry.categories[category]?.name ?? category,
262
+ components: []
263
+ };
264
+ }
265
+ result[category].components.push({ key, name: component.name });
266
+ }
267
+ return result;
268
+ }
269
+
270
+ // cli/src/utils/transformer.ts
271
+ function transformImports(source, config) {
272
+ let result = source;
273
+ const replacements = [
274
+ // UI components: @/ui/components/ui/ -> config.aliases.ui/
275
+ [/@\/ui\/components\/ui\//g, `${config.aliases.ui}/`],
276
+ // Feature components: @/ui/components/ -> config.aliases.components/
277
+ [/@\/ui\/components\//g, `${config.aliases.components}/`],
278
+ // Providers: @/ui/providers/ -> config.aliases.providers/
279
+ [/@\/ui\/providers\//g, `${config.aliases.providers}/`],
280
+ // Lib utilities: @/ui/lib/ -> config.aliases.lib/
281
+ [/@\/ui\/lib\//g, `${config.aliases.lib}/`],
282
+ // Hooks: @/ui/hooks/ -> config.aliases.hooks/
283
+ [/@\/ui\/hooks\//g, `${config.aliases.hooks}/`]
284
+ ];
285
+ for (const [pattern, replacement] of replacements) {
286
+ result = result.replace(pattern, replacement);
287
+ }
288
+ return result;
289
+ }
290
+ function getDestinationPath(sourcePath, config) {
291
+ if (sourcePath.startsWith("components/ui/")) {
292
+ return {
293
+ destPath: sourcePath.replace("components/ui/", ""),
294
+ category: "ui"
295
+ };
296
+ }
297
+ if (sourcePath.startsWith("components/")) {
298
+ return {
299
+ destPath: sourcePath.replace("components/", ""),
300
+ category: "components"
301
+ };
302
+ }
303
+ if (sourcePath.startsWith("providers/")) {
304
+ return {
305
+ destPath: sourcePath.replace("providers/", ""),
306
+ category: "providers"
307
+ };
308
+ }
309
+ if (sourcePath.startsWith("lib/")) {
310
+ return {
311
+ destPath: sourcePath.replace("lib/", ""),
312
+ category: "lib"
313
+ };
314
+ }
315
+ if (sourcePath.startsWith("hooks/")) {
316
+ return {
317
+ destPath: sourcePath.replace("hooks/", ""),
318
+ category: "hooks"
319
+ };
320
+ }
321
+ return {
322
+ destPath: sourcePath,
323
+ category: "components"
324
+ };
325
+ }
326
+ function getFullDestinationPath(sourcePath, config, basePath) {
327
+ const { destPath, category } = getDestinationPath(sourcePath, config);
328
+ const alias = config.aliases[category];
329
+ let relativePath;
330
+ if (alias.startsWith("@/")) {
331
+ relativePath = alias.slice(2);
332
+ } else {
333
+ relativePath = alias;
334
+ }
335
+ return `${basePath}/src/${relativePath}/${destPath}`;
336
+ }
337
+
338
+ // cli/src/commands/init.ts
339
+ var CORE_DEPENDENCIES = ["@spaceinvoices/js-sdk", "@tanstack/react-query"];
340
+ var ESSENTIAL_FILES = [
341
+ "lib/utils.ts",
342
+ "lib/auth.ts",
343
+ "lib/cookies.ts",
344
+ "lib/browser-cookies.ts",
345
+ "lib/translation.ts",
346
+ "providers/sdk-provider.tsx",
347
+ "components/entities/keys.ts"
348
+ ];
349
+ async function init(options = {}) {
350
+ const cwd = options.cwd ?? process.cwd();
351
+ logger.break();
352
+ logger.info("Initializing Space Invoices React UI...");
353
+ logger.break();
354
+ if (configExists(cwd) && !options.force) {
355
+ const { overwrite } = await prompts({
356
+ type: "confirm",
357
+ name: "overwrite",
358
+ message: `${CONFIG_FILE} already exists. Overwrite?`,
359
+ initial: false
360
+ });
361
+ if (!overwrite) {
362
+ logger.info("Initialization cancelled.");
363
+ return;
364
+ }
365
+ }
366
+ let config;
367
+ if (options.yes) {
368
+ config = { ...DEFAULT_CONFIG };
369
+ } else {
370
+ const answers = await prompts([
371
+ {
372
+ type: "text",
373
+ name: "components",
374
+ message: "Where should feature components be installed?",
375
+ initial: DEFAULT_CONFIG.aliases.components
376
+ },
377
+ {
378
+ type: "text",
379
+ name: "ui",
380
+ message: "Where should UI primitives be installed?",
381
+ initial: DEFAULT_CONFIG.aliases.ui
382
+ },
383
+ {
384
+ type: "text",
385
+ name: "lib",
386
+ message: "Where should lib utilities be installed?",
387
+ initial: DEFAULT_CONFIG.aliases.lib
388
+ },
389
+ {
390
+ type: "text",
391
+ name: "hooks",
392
+ message: "Where should hooks be installed?",
393
+ initial: DEFAULT_CONFIG.aliases.hooks
394
+ },
395
+ {
396
+ type: "text",
397
+ name: "providers",
398
+ message: "Where should providers be installed?",
399
+ initial: DEFAULT_CONFIG.aliases.providers
400
+ }
401
+ ]);
402
+ if (!answers.components) {
403
+ logger.info("Initialization cancelled.");
404
+ return;
405
+ }
406
+ config = {
407
+ $schema: DEFAULT_CONFIG.$schema,
408
+ aliases: {
409
+ components: answers.components,
410
+ ui: answers.ui,
411
+ lib: answers.lib,
412
+ hooks: answers.hooks,
413
+ providers: answers.providers
414
+ }
415
+ };
416
+ }
417
+ const configSpinner = spinner("Writing configuration...");
418
+ configSpinner.start();
419
+ try {
420
+ writeConfig(config, cwd);
421
+ configSpinner.succeed(`Created ${highlight(CONFIG_FILE)}`);
422
+ } catch (error) {
423
+ configSpinner.fail(`Failed to create ${CONFIG_FILE}`);
424
+ throw error;
425
+ }
426
+ const dirSpinner = spinner("Creating directories...");
427
+ dirSpinner.start();
428
+ try {
429
+ const directories = [
430
+ config.aliases.components,
431
+ config.aliases.ui,
432
+ config.aliases.lib,
433
+ config.aliases.hooks,
434
+ config.aliases.providers
435
+ ];
436
+ for (const alias of directories) {
437
+ let dirPath;
438
+ if (alias.startsWith("@/")) {
439
+ dirPath = path4.join(cwd, "src", alias.slice(2));
440
+ } else {
441
+ dirPath = path4.join(cwd, alias);
442
+ }
443
+ fs4.mkdirSync(dirPath, { recursive: true });
444
+ }
445
+ dirSpinner.succeed("Created directories");
446
+ } catch (error) {
447
+ dirSpinner.fail("Failed to create directories");
448
+ throw error;
449
+ }
450
+ const filesSpinner = spinner("Copying essential files...");
451
+ filesSpinner.start();
452
+ try {
453
+ for (const filePath of ESSENTIAL_FILES) {
454
+ const content = await fetchFile(filePath);
455
+ const transformed = transformImports(content, config);
456
+ const destPath = getFullDestinationPath(filePath, config, cwd);
457
+ fs4.mkdirSync(path4.dirname(destPath), { recursive: true });
458
+ fs4.writeFileSync(destPath, transformed);
459
+ }
460
+ filesSpinner.succeed(`Copied ${ESSENTIAL_FILES.length} essential files`);
461
+ } catch (error) {
462
+ filesSpinner.fail("Failed to copy essential files");
463
+ throw error;
464
+ }
465
+ const pm = detectPackageManager(cwd);
466
+ const newDeps = filterNewPackages(CORE_DEPENDENCIES, cwd);
467
+ if (newDeps.length > 0) {
468
+ logger.break();
469
+ logger.info(`Installing dependencies with ${pm}...`);
470
+ logger.break();
471
+ try {
472
+ await installPackages(newDeps, { cwd });
473
+ logger.break();
474
+ logger.success("Installed core dependencies");
475
+ } catch (error) {
476
+ logger.error("Failed to install dependencies. Please install manually:");
477
+ logger.log(` ${newDeps.join(" ")}`);
478
+ }
479
+ }
480
+ logger.break();
481
+ logger.success("Space Invoices React UI initialized!");
482
+ logger.break();
483
+ logger.info("Next steps:");
484
+ logger.log(` 1. Add components: ${highlight("npx @spaceinvoices/react-ui add customers/create-customer-form")}`);
485
+ logger.log(` 2. Import and use in your app`);
486
+ logger.break();
487
+ }
488
+
489
+ // cli/src/commands/add.ts
490
+ import fs5 from "fs";
491
+ import path5 from "path";
492
+ import prompts2 from "prompts";
493
+
494
+ // cli/src/utils/resolver.ts
495
+ function resolveDependencies(registry, componentNames) {
496
+ const visited = /* @__PURE__ */ new Set();
497
+ const items = [];
498
+ const allFiles = [];
499
+ const npmDepsSet = /* @__PURE__ */ new Set();
500
+ function resolveComponent(name) {
501
+ const key = `component:${name}`;
502
+ if (visited.has(key)) return;
503
+ visited.add(key);
504
+ const component = getComponent(registry, name);
505
+ if (!component) {
506
+ throw new Error(`Component "${name}" not found in registry`);
507
+ }
508
+ if (component.dependencies) {
509
+ for (const dep of component.dependencies) {
510
+ resolveComponent(dep);
511
+ }
512
+ }
513
+ if (component.providers) {
514
+ for (const providerName of component.providers) {
515
+ resolveProvider(providerName);
516
+ }
517
+ }
518
+ if (component.utils) {
519
+ for (const utilName of component.utils) {
520
+ resolveUtil(utilName);
521
+ }
522
+ }
523
+ items.push({
524
+ type: "component",
525
+ key: name,
526
+ name: component.name,
527
+ files: component.files,
528
+ npmDependencies: component.npmDependencies ?? []
529
+ });
530
+ allFiles.push(...component.files);
531
+ if (component.npmDependencies) {
532
+ for (const dep of component.npmDependencies) {
533
+ npmDepsSet.add(dep);
534
+ }
535
+ }
536
+ }
537
+ function resolveProvider(name) {
538
+ const providerName = name.replace(/^providers\//, "");
539
+ const key = `provider:${providerName}`;
540
+ if (visited.has(key)) return;
541
+ visited.add(key);
542
+ const provider = getProvider(registry, providerName);
543
+ if (!provider) {
544
+ throw new Error(`Provider "${providerName}" not found in registry`);
545
+ }
546
+ if (provider.dependencies) {
547
+ for (const dep of provider.dependencies) {
548
+ resolveProvider(dep);
549
+ }
550
+ }
551
+ items.push({
552
+ type: "provider",
553
+ key: providerName,
554
+ name: provider.name,
555
+ files: provider.files,
556
+ npmDependencies: provider.npmDependencies ?? []
557
+ });
558
+ allFiles.push(...provider.files);
559
+ if (provider.npmDependencies) {
560
+ for (const dep of provider.npmDependencies) {
561
+ npmDepsSet.add(dep);
562
+ }
563
+ }
564
+ }
565
+ function resolveUtil(name) {
566
+ const utilName = name.replace(/^utils\//, "");
567
+ const key = `util:${utilName}`;
568
+ if (visited.has(key)) return;
569
+ visited.add(key);
570
+ const util = getUtil(registry, utilName);
571
+ if (!util) {
572
+ throw new Error(`Utility "${utilName}" not found in registry`);
573
+ }
574
+ if (util.dependencies) {
575
+ for (const dep of util.dependencies) {
576
+ if (dep.startsWith("providers/") || registry.providers[dep]) {
577
+ resolveProvider(dep);
578
+ } else {
579
+ resolveUtil(dep);
580
+ }
581
+ }
582
+ }
583
+ items.push({
584
+ type: "util",
585
+ key: utilName,
586
+ name: util.name,
587
+ files: util.files,
588
+ npmDependencies: util.npmDependencies ?? []
589
+ });
590
+ allFiles.push(...util.files);
591
+ if (util.npmDependencies) {
592
+ for (const dep of util.npmDependencies) {
593
+ npmDepsSet.add(dep);
594
+ }
595
+ }
596
+ }
597
+ for (const name of componentNames) {
598
+ resolveComponent(name);
599
+ }
600
+ const uniqueFiles = [...new Set(allFiles)];
601
+ return {
602
+ items,
603
+ allFiles: uniqueFiles,
604
+ allNpmDependencies: [...npmDepsSet]
605
+ };
606
+ }
607
+ function getInstallSummary(resolved) {
608
+ return {
609
+ components: resolved.items.filter((i) => i.type === "component").map((i) => i.name),
610
+ providers: resolved.items.filter((i) => i.type === "provider").map((i) => i.name),
611
+ utils: resolved.items.filter((i) => i.type === "util").map((i) => i.name),
612
+ npmPackages: resolved.allNpmDependencies,
613
+ fileCount: resolved.allFiles.length
614
+ };
615
+ }
616
+
617
+ // cli/src/commands/add.ts
618
+ async function add(componentNames, options = {}) {
619
+ const cwd = options.cwd ?? process.cwd();
620
+ if (!configExists(cwd)) {
621
+ logger.error(
622
+ "No spaceinvoices.json found. Run `npx @spaceinvoices/react-ui init` first."
623
+ );
624
+ process.exit(1);
625
+ }
626
+ const config = readConfig(cwd);
627
+ if (!config) {
628
+ logger.error("Failed to read spaceinvoices.json");
629
+ process.exit(1);
630
+ }
631
+ const registrySpinner = spinner("Fetching component registry...");
632
+ registrySpinner.start();
633
+ let registry;
634
+ try {
635
+ registry = await fetchRegistry();
636
+ registrySpinner.succeed("Fetched component registry");
637
+ } catch (error) {
638
+ registrySpinner.fail("Failed to fetch registry");
639
+ throw error;
640
+ }
641
+ if (options.all) {
642
+ componentNames = listComponents(registry);
643
+ }
644
+ if (componentNames.length === 0) {
645
+ const components = await selectComponents(registry);
646
+ if (components.length === 0) {
647
+ logger.info("No components selected.");
648
+ return;
649
+ }
650
+ componentNames = components;
651
+ }
652
+ const availableComponents = listComponents(registry);
653
+ for (const name of componentNames) {
654
+ if (!availableComponents.includes(name)) {
655
+ logger.error(`Component "${name}" not found in registry.`);
656
+ logger.info("Available components:");
657
+ for (const comp of availableComponents) {
658
+ logger.log(` - ${comp}`);
659
+ }
660
+ process.exit(1);
661
+ }
662
+ }
663
+ logger.break();
664
+ const resolveSpinner = spinner("Resolving dependencies...");
665
+ resolveSpinner.start();
666
+ let resolved;
667
+ try {
668
+ resolved = resolveDependencies(registry, componentNames);
669
+ const summary2 = getInstallSummary(resolved);
670
+ resolveSpinner.succeed(
671
+ `Resolved ${summary2.fileCount} files with ${summary2.npmPackages.length} npm packages`
672
+ );
673
+ } catch (error) {
674
+ resolveSpinner.fail("Failed to resolve dependencies");
675
+ throw error;
676
+ }
677
+ const summary = getInstallSummary(resolved);
678
+ logger.break();
679
+ logger.info("The following will be installed:");
680
+ logger.break();
681
+ if (summary.components.length > 0) {
682
+ logger.log(` ${highlight("Components:")} ${summary.components.join(", ")}`);
683
+ }
684
+ if (summary.providers.length > 0) {
685
+ logger.log(` ${highlight("Providers:")} ${summary.providers.join(", ")}`);
686
+ }
687
+ if (summary.utils.length > 0) {
688
+ logger.log(` ${highlight("Utilities:")} ${summary.utils.join(", ")}`);
689
+ }
690
+ if (summary.npmPackages.length > 0) {
691
+ logger.log(
692
+ ` ${highlight("npm packages:")} ${summary.npmPackages.join(", ")}`
693
+ );
694
+ }
695
+ logger.log(` ${highlight("Files:")} ${summary.fileCount} files`);
696
+ if (!options.yes) {
697
+ logger.break();
698
+ const { proceed } = await prompts2({
699
+ type: "confirm",
700
+ name: "proceed",
701
+ message: "Proceed with installation?",
702
+ initial: true
703
+ });
704
+ if (!proceed) {
705
+ logger.info("Installation cancelled.");
706
+ return;
707
+ }
708
+ }
709
+ logger.break();
710
+ const filesSpinner = spinner("Installing components...");
711
+ filesSpinner.start();
712
+ const existingFiles = [];
713
+ const filesToWrite = [];
714
+ try {
715
+ for (const filePath of resolved.allFiles) {
716
+ const destPath = getFullDestinationPath(filePath, config, cwd);
717
+ if (fs5.existsSync(destPath)) {
718
+ existingFiles.push(destPath);
719
+ }
720
+ const content = await fetchFile(filePath);
721
+ const transformed = transformImports(content, config);
722
+ filesToWrite.push({ path: destPath, content: transformed });
723
+ }
724
+ } catch (error) {
725
+ filesSpinner.fail("Failed to fetch component files");
726
+ throw error;
727
+ }
728
+ if (existingFiles.length > 0 && !options.overwrite) {
729
+ filesSpinner.stop();
730
+ logger.break();
731
+ logger.warn(`The following files already exist:`);
732
+ for (const file of existingFiles) {
733
+ logger.log(` ${dim(file)}`);
734
+ }
735
+ logger.break();
736
+ const { overwrite } = await prompts2({
737
+ type: "confirm",
738
+ name: "overwrite",
739
+ message: "Overwrite existing files?",
740
+ initial: false
741
+ });
742
+ if (!overwrite) {
743
+ const existingSet = new Set(existingFiles);
744
+ const filteredFiles = filesToWrite.filter((f) => !existingSet.has(f.path));
745
+ if (filteredFiles.length === 0) {
746
+ logger.info("No files to install.");
747
+ return;
748
+ }
749
+ filesToWrite.length = 0;
750
+ filesToWrite.push(...filteredFiles);
751
+ }
752
+ filesSpinner.start();
753
+ }
754
+ try {
755
+ for (const { path: filePath, content } of filesToWrite) {
756
+ fs5.mkdirSync(path5.dirname(filePath), { recursive: true });
757
+ fs5.writeFileSync(filePath, content);
758
+ }
759
+ filesSpinner.succeed(`Installed ${filesToWrite.length} files`);
760
+ } catch (error) {
761
+ filesSpinner.fail("Failed to write files");
762
+ throw error;
763
+ }
764
+ const pm = detectPackageManager(cwd);
765
+ const newDeps = filterNewPackages(resolved.allNpmDependencies, cwd);
766
+ if (newDeps.length > 0) {
767
+ logger.break();
768
+ logger.info(`Installing npm dependencies with ${pm}...`);
769
+ logger.break();
770
+ try {
771
+ await installPackages(newDeps, { cwd });
772
+ logger.break();
773
+ logger.success(`Installed ${newDeps.length} npm packages`);
774
+ } catch (error) {
775
+ logger.error("Failed to install npm packages. Please install manually:");
776
+ logger.log(` ${newDeps.join(" ")}`);
777
+ }
778
+ }
779
+ logger.break();
780
+ logger.success("Components installed successfully!");
781
+ logger.break();
782
+ }
783
+ async function selectComponents(registry) {
784
+ const byCategory = getComponentsByCategory(registry);
785
+ const choices = [];
786
+ for (const [categoryKey, category] of Object.entries(byCategory)) {
787
+ choices.push({
788
+ title: `\u2500\u2500 ${category.name} \u2500\u2500`,
789
+ value: `__category_${categoryKey}`,
790
+ description: ""
791
+ });
792
+ for (const comp of category.components) {
793
+ choices.push({
794
+ title: ` ${comp.name}`,
795
+ value: comp.key,
796
+ description: comp.key
797
+ });
798
+ }
799
+ }
800
+ const { selected } = await prompts2({
801
+ type: "multiselect",
802
+ name: "selected",
803
+ message: "Select components to install",
804
+ choices: choices.filter((c) => !c.value.startsWith("__category_")),
805
+ hint: "- Space to select. Return to submit"
806
+ });
807
+ return selected ?? [];
808
+ }
809
+
810
+ // cli/src/commands/list.ts
811
+ async function list(options = {}) {
812
+ const registrySpinner = spinner("Fetching component registry...");
813
+ registrySpinner.start();
814
+ try {
815
+ const registry = await fetchRegistry();
816
+ registrySpinner.succeed("Fetched component registry");
817
+ if (options.json) {
818
+ const output = {
819
+ components: Object.entries(registry.components).map(([key, comp]) => ({
820
+ key,
821
+ name: comp.name,
822
+ category: comp.category,
823
+ files: comp.files.length,
824
+ dependencies: comp.dependencies?.length ?? 0
825
+ })),
826
+ providers: Object.entries(registry.providers).map(([key, prov]) => ({
827
+ key,
828
+ name: prov.name,
829
+ files: prov.files.length
830
+ })),
831
+ utils: Object.entries(registry.utils).map(([key, util]) => ({
832
+ key,
833
+ name: util.name,
834
+ files: util.files.length
835
+ }))
836
+ };
837
+ console.log(JSON.stringify(output, null, 2));
838
+ return;
839
+ }
840
+ logger.break();
841
+ logger.info("Available components:");
842
+ logger.break();
843
+ const byCategory = getComponentsByCategory(registry);
844
+ for (const [categoryKey, category] of Object.entries(byCategory)) {
845
+ logger.log(highlight(`${category.name}:`));
846
+ for (const comp of category.components) {
847
+ logger.log(` ${comp.key.padEnd(40)} ${dim(comp.name)}`);
848
+ }
849
+ logger.break();
850
+ }
851
+ logger.log(highlight("Providers:"));
852
+ for (const [key, prov] of Object.entries(registry.providers)) {
853
+ logger.log(` ${key.padEnd(40)} ${dim(prov.name)}`);
854
+ }
855
+ logger.break();
856
+ logger.log(highlight("Utilities:"));
857
+ for (const [key, util] of Object.entries(registry.utils)) {
858
+ logger.log(` ${key.padEnd(40)} ${dim(util.name)}`);
859
+ }
860
+ logger.break();
861
+ logger.info("Usage:");
862
+ logger.log(` npx @spaceinvoices/react-ui add ${dim("<component-name>")}`);
863
+ logger.log(` npx @spaceinvoices/react-ui add ${dim("customers/create-customer-form")}`);
864
+ logger.break();
865
+ } catch (error) {
866
+ registrySpinner.fail("Failed to fetch registry");
867
+ throw error;
868
+ }
869
+ }
870
+
871
+ // cli/src/index.ts
872
+ var program = new Command();
873
+ program.name("spaceinvoices-ui").description("CLI for adding Space Invoices React UI components to your project").version("0.1.0");
874
+ program.option("--local <path>", "Use local registry from specified path (for development)");
875
+ program.command("init").description("Initialize Space Invoices UI in your project").option("-y, --yes", "Skip prompts and use defaults").option("-f, --force", "Overwrite existing configuration").option("--cwd <path>", "Working directory (defaults to current directory)").action(async (options) => {
876
+ const globalOpts = program.opts();
877
+ if (globalOpts.local) {
878
+ setLocalBasePath(globalOpts.local);
879
+ }
880
+ try {
881
+ await init({
882
+ cwd: options.cwd,
883
+ yes: options.yes,
884
+ force: options.force
885
+ });
886
+ } catch (error) {
887
+ console.error("Error:", error instanceof Error ? error.message : error);
888
+ process.exit(1);
889
+ }
890
+ });
891
+ program.command("add").description("Add components to your project").argument("[components...]", "Components to add").option("-y, --yes", "Skip confirmation prompts").option("-a, --all", "Add all available components").option("-o, --overwrite", "Overwrite existing files without asking").option("--cwd <path>", "Working directory (defaults to current directory)").action(async (components, options) => {
892
+ const globalOpts = program.opts();
893
+ if (globalOpts.local) {
894
+ setLocalBasePath(globalOpts.local);
895
+ }
896
+ try {
897
+ await add(components, {
898
+ cwd: options.cwd,
899
+ yes: options.yes,
900
+ all: options.all,
901
+ overwrite: options.overwrite
902
+ });
903
+ } catch (error) {
904
+ console.error("Error:", error instanceof Error ? error.message : error);
905
+ process.exit(1);
906
+ }
907
+ });
908
+ program.command("list").description("List available components").option("--json", "Output as JSON").action(async (options) => {
909
+ const globalOpts = program.opts();
910
+ if (globalOpts.local) {
911
+ setLocalBasePath(globalOpts.local);
912
+ }
913
+ try {
914
+ await list({
915
+ json: options.json
916
+ });
917
+ } catch (error) {
918
+ console.error("Error:", error instanceof Error ? error.message : error);
919
+ process.exit(1);
920
+ }
921
+ });
922
+ program.parse();