realtimex-crm 0.1.2

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 (468) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +104 -0
  3. package/dist/assets/DealList-DqDrFeDV.js +59 -0
  4. package/dist/assets/DealList-DqDrFeDV.js.map +1 -0
  5. package/dist/assets/index-BiQoGq1P.css +1 -0
  6. package/dist/assets/index-CDIy4x-0.js +152 -0
  7. package/dist/assets/index-CDIy4x-0.js.map +1 -0
  8. package/dist/auth-callback.html +140 -0
  9. package/dist/favicon.ico +0 -0
  10. package/dist/img/adding-users.png +0 -0
  11. package/dist/img/empty.svg +42 -0
  12. package/dist/index.html +1 -0
  13. package/dist/logo192.png +0 -0
  14. package/dist/logo512.png +0 -0
  15. package/dist/logos/0.png +0 -0
  16. package/dist/logos/1.png +0 -0
  17. package/dist/logos/10.png +0 -0
  18. package/dist/logos/11.png +0 -0
  19. package/dist/logos/12.png +0 -0
  20. package/dist/logos/13.png +0 -0
  21. package/dist/logos/14.png +0 -0
  22. package/dist/logos/15.png +0 -0
  23. package/dist/logos/16.png +0 -0
  24. package/dist/logos/17.png +0 -0
  25. package/dist/logos/18.png +0 -0
  26. package/dist/logos/19.png +0 -0
  27. package/dist/logos/2.png +0 -0
  28. package/dist/logos/20.png +0 -0
  29. package/dist/logos/21.png +0 -0
  30. package/dist/logos/22.png +0 -0
  31. package/dist/logos/23.png +0 -0
  32. package/dist/logos/24.png +0 -0
  33. package/dist/logos/25.png +0 -0
  34. package/dist/logos/26.png +0 -0
  35. package/dist/logos/27.png +0 -0
  36. package/dist/logos/28.png +0 -0
  37. package/dist/logos/29.png +0 -0
  38. package/dist/logos/3.png +0 -0
  39. package/dist/logos/30.png +0 -0
  40. package/dist/logos/31.png +0 -0
  41. package/dist/logos/32.png +0 -0
  42. package/dist/logos/33.png +0 -0
  43. package/dist/logos/34.png +0 -0
  44. package/dist/logos/35.png +0 -0
  45. package/dist/logos/36.png +0 -0
  46. package/dist/logos/37.png +0 -0
  47. package/dist/logos/38.png +0 -0
  48. package/dist/logos/39.png +0 -0
  49. package/dist/logos/4.png +0 -0
  50. package/dist/logos/40.png +0 -0
  51. package/dist/logos/41.png +0 -0
  52. package/dist/logos/42.png +0 -0
  53. package/dist/logos/43.png +0 -0
  54. package/dist/logos/44.png +0 -0
  55. package/dist/logos/45.png +0 -0
  56. package/dist/logos/46.png +0 -0
  57. package/dist/logos/47.png +0 -0
  58. package/dist/logos/48.png +0 -0
  59. package/dist/logos/49.png +0 -0
  60. package/dist/logos/5.png +0 -0
  61. package/dist/logos/50.png +0 -0
  62. package/dist/logos/51.png +0 -0
  63. package/dist/logos/52.png +0 -0
  64. package/dist/logos/53.png +0 -0
  65. package/dist/logos/54.png +0 -0
  66. package/dist/logos/55.png +0 -0
  67. package/dist/logos/6.png +0 -0
  68. package/dist/logos/7.png +0 -0
  69. package/dist/logos/8.png +0 -0
  70. package/dist/logos/9.png +0 -0
  71. package/dist/logos/Readme.md +1 -0
  72. package/dist/logos/logo_atomic_crm.svg +14 -0
  73. package/dist/logos/logo_atomic_crm_dark.svg +14 -0
  74. package/dist/logos/logo_atomic_crm_light.svg +14 -0
  75. package/dist/manifest.json +25 -0
  76. package/dist/robots.txt +3 -0
  77. package/dist/stats.html +4949 -0
  78. package/package.json +152 -0
  79. package/public/auth-callback.html +140 -0
  80. package/public/favicon.ico +0 -0
  81. package/public/img/adding-users.png +0 -0
  82. package/public/img/empty.svg +42 -0
  83. package/public/logo192.png +0 -0
  84. package/public/logo512.png +0 -0
  85. package/public/logos/0.png +0 -0
  86. package/public/logos/1.png +0 -0
  87. package/public/logos/10.png +0 -0
  88. package/public/logos/11.png +0 -0
  89. package/public/logos/12.png +0 -0
  90. package/public/logos/13.png +0 -0
  91. package/public/logos/14.png +0 -0
  92. package/public/logos/15.png +0 -0
  93. package/public/logos/16.png +0 -0
  94. package/public/logos/17.png +0 -0
  95. package/public/logos/18.png +0 -0
  96. package/public/logos/19.png +0 -0
  97. package/public/logos/2.png +0 -0
  98. package/public/logos/20.png +0 -0
  99. package/public/logos/21.png +0 -0
  100. package/public/logos/22.png +0 -0
  101. package/public/logos/23.png +0 -0
  102. package/public/logos/24.png +0 -0
  103. package/public/logos/25.png +0 -0
  104. package/public/logos/26.png +0 -0
  105. package/public/logos/27.png +0 -0
  106. package/public/logos/28.png +0 -0
  107. package/public/logos/29.png +0 -0
  108. package/public/logos/3.png +0 -0
  109. package/public/logos/30.png +0 -0
  110. package/public/logos/31.png +0 -0
  111. package/public/logos/32.png +0 -0
  112. package/public/logos/33.png +0 -0
  113. package/public/logos/34.png +0 -0
  114. package/public/logos/35.png +0 -0
  115. package/public/logos/36.png +0 -0
  116. package/public/logos/37.png +0 -0
  117. package/public/logos/38.png +0 -0
  118. package/public/logos/39.png +0 -0
  119. package/public/logos/4.png +0 -0
  120. package/public/logos/40.png +0 -0
  121. package/public/logos/41.png +0 -0
  122. package/public/logos/42.png +0 -0
  123. package/public/logos/43.png +0 -0
  124. package/public/logos/44.png +0 -0
  125. package/public/logos/45.png +0 -0
  126. package/public/logos/46.png +0 -0
  127. package/public/logos/47.png +0 -0
  128. package/public/logos/48.png +0 -0
  129. package/public/logos/49.png +0 -0
  130. package/public/logos/5.png +0 -0
  131. package/public/logos/50.png +0 -0
  132. package/public/logos/51.png +0 -0
  133. package/public/logos/52.png +0 -0
  134. package/public/logos/53.png +0 -0
  135. package/public/logos/54.png +0 -0
  136. package/public/logos/55.png +0 -0
  137. package/public/logos/6.png +0 -0
  138. package/public/logos/7.png +0 -0
  139. package/public/logos/8.png +0 -0
  140. package/public/logos/9.png +0 -0
  141. package/public/logos/Readme.md +1 -0
  142. package/public/logos/logo_atomic_crm.svg +14 -0
  143. package/public/logos/logo_atomic_crm_dark.svg +14 -0
  144. package/public/logos/logo_atomic_crm_light.svg +14 -0
  145. package/public/manifest.json +25 -0
  146. package/public/robots.txt +3 -0
  147. package/src/App.css +42 -0
  148. package/src/App.tsx +58 -0
  149. package/src/assets/react.svg +1 -0
  150. package/src/components/admin/Readme.md +40 -0
  151. package/src/components/admin/admin.tsx +132 -0
  152. package/src/components/admin/app-sidebar.tsx +166 -0
  153. package/src/components/admin/array-field.tsx +59 -0
  154. package/src/components/admin/array-input.tsx +201 -0
  155. package/src/components/admin/authentication.tsx +86 -0
  156. package/src/components/admin/autocomplete-array-input.tsx +254 -0
  157. package/src/components/admin/autocomplete-input.tsx +296 -0
  158. package/src/components/admin/badge-field.tsx +65 -0
  159. package/src/components/admin/boolean-input.tsx +116 -0
  160. package/src/components/admin/breadcrumb.tsx +135 -0
  161. package/src/components/admin/bulk-actions-toolbar.tsx +83 -0
  162. package/src/components/admin/bulk-delete-button.tsx +70 -0
  163. package/src/components/admin/bulk-export-button.tsx +76 -0
  164. package/src/components/admin/cancel-button.tsx +46 -0
  165. package/src/components/admin/columns-button.tsx +345 -0
  166. package/src/components/admin/confirm.tsx +166 -0
  167. package/src/components/admin/count.tsx +94 -0
  168. package/src/components/admin/create-button.tsx +58 -0
  169. package/src/components/admin/create.tsx +132 -0
  170. package/src/components/admin/data-table.tsx +520 -0
  171. package/src/components/admin/date-field.tsx +136 -0
  172. package/src/components/admin/date-input.tsx +317 -0
  173. package/src/components/admin/date-time-input.tsx +331 -0
  174. package/src/components/admin/delete-button.tsx +113 -0
  175. package/src/components/admin/edit-button.tsx +64 -0
  176. package/src/components/admin/edit-guesser.tsx +157 -0
  177. package/src/components/admin/edit.tsx +152 -0
  178. package/src/components/admin/email-field.tsx +74 -0
  179. package/src/components/admin/error.tsx +111 -0
  180. package/src/components/admin/export-button.tsx +126 -0
  181. package/src/components/admin/field-toggle.tsx +164 -0
  182. package/src/components/admin/file-field.tsx +123 -0
  183. package/src/components/admin/file-input.tsx +361 -0
  184. package/src/components/admin/filter-form.tsx +510 -0
  185. package/src/components/admin/form.tsx +312 -0
  186. package/src/components/admin/icon-button-with-tooltip.tsx +85 -0
  187. package/src/components/admin/index.ts +73 -0
  188. package/src/components/admin/input-helper-text.tsx +29 -0
  189. package/src/components/admin/layout.tsx +69 -0
  190. package/src/components/admin/list-guesser.tsx +239 -0
  191. package/src/components/admin/list-pagination.tsx +247 -0
  192. package/src/components/admin/list.tsx +178 -0
  193. package/src/components/admin/loading.tsx +40 -0
  194. package/src/components/admin/locales-menu-button.tsx +60 -0
  195. package/src/components/admin/login-page.tsx +104 -0
  196. package/src/components/admin/notification.tsx +114 -0
  197. package/src/components/admin/number-field.tsx +84 -0
  198. package/src/components/admin/number-input.tsx +124 -0
  199. package/src/components/admin/radio-button-group-input.tsx +184 -0
  200. package/src/components/admin/ready.tsx +55 -0
  201. package/src/components/admin/record-field.tsx +132 -0
  202. package/src/components/admin/reference-array-field.tsx +152 -0
  203. package/src/components/admin/reference-array-input.tsx +68 -0
  204. package/src/components/admin/reference-field.tsx +153 -0
  205. package/src/components/admin/reference-input.tsx +46 -0
  206. package/src/components/admin/reference-many-count.tsx +92 -0
  207. package/src/components/admin/reference-many-field.tsx +132 -0
  208. package/src/components/admin/refresh-button.tsx +31 -0
  209. package/src/components/admin/saved-queries.tsx +174 -0
  210. package/src/components/admin/search-input.tsx +57 -0
  211. package/src/components/admin/select-field.tsx +111 -0
  212. package/src/components/admin/select-input.tsx +323 -0
  213. package/src/components/admin/show-button.tsx +57 -0
  214. package/src/components/admin/show-guesser.tsx +215 -0
  215. package/src/components/admin/show.tsx +184 -0
  216. package/src/components/admin/simple-form-iterator.tsx +582 -0
  217. package/src/components/admin/simple-form.tsx +95 -0
  218. package/src/components/admin/simple-show-layout.tsx +8 -0
  219. package/src/components/admin/single-field-list.tsx +67 -0
  220. package/src/components/admin/sort-button.tsx +152 -0
  221. package/src/components/admin/spinner.tsx +46 -0
  222. package/src/components/admin/text-field.tsx +60 -0
  223. package/src/components/admin/text-input.tsx +77 -0
  224. package/src/components/admin/theme-mode-toggle.tsx +48 -0
  225. package/src/components/admin/theme-provider.tsx +74 -0
  226. package/src/components/admin/toggle-filter-button.tsx +77 -0
  227. package/src/components/admin/url-field.tsx +83 -0
  228. package/src/components/admin/user-menu.tsx +84 -0
  229. package/src/components/atomic-crm/activity/ActivityLog.tsx +54 -0
  230. package/src/components/atomic-crm/activity/ActivityLogCompanyCreated.tsx +50 -0
  231. package/src/components/atomic-crm/activity/ActivityLogContactCreated.tsx +42 -0
  232. package/src/components/atomic-crm/activity/ActivityLogContactNoteCreated.tsx +71 -0
  233. package/src/components/atomic-crm/activity/ActivityLogContext.tsx +11 -0
  234. package/src/components/atomic-crm/activity/ActivityLogDealCreated.tsx +41 -0
  235. package/src/components/atomic-crm/activity/ActivityLogDealNoteCreated.tsx +84 -0
  236. package/src/components/atomic-crm/activity/ActivityLogIterator.tsx +80 -0
  237. package/src/components/atomic-crm/activity/ActivityLogNote.tsx +36 -0
  238. package/src/components/atomic-crm/companies/AutocompleteCompanyInput.tsx +43 -0
  239. package/src/components/atomic-crm/companies/CompanyAside.tsx +207 -0
  240. package/src/components/atomic-crm/companies/CompanyAvatar.tsx +29 -0
  241. package/src/components/atomic-crm/companies/CompanyCard.tsx +88 -0
  242. package/src/components/atomic-crm/companies/CompanyCreate.tsx +41 -0
  243. package/src/components/atomic-crm/companies/CompanyEdit.tsx +33 -0
  244. package/src/components/atomic-crm/companies/CompanyEmpty.tsx +26 -0
  245. package/src/components/atomic-crm/companies/CompanyInputs.tsx +160 -0
  246. package/src/components/atomic-crm/companies/CompanyList.tsx +54 -0
  247. package/src/components/atomic-crm/companies/CompanyListFilter.tsx +55 -0
  248. package/src/components/atomic-crm/companies/CompanyShow.tsx +241 -0
  249. package/src/components/atomic-crm/companies/GridList.tsx +46 -0
  250. package/src/components/atomic-crm/companies/index.ts +11 -0
  251. package/src/components/atomic-crm/companies/sizes.ts +7 -0
  252. package/src/components/atomic-crm/consts.ts +5 -0
  253. package/src/components/atomic-crm/contacts/Avatar.tsx +40 -0
  254. package/src/components/atomic-crm/contacts/ContactAside.tsx +187 -0
  255. package/src/components/atomic-crm/contacts/ContactCreate.tsx +34 -0
  256. package/src/components/atomic-crm/contacts/ContactEdit.tsx +32 -0
  257. package/src/components/atomic-crm/contacts/ContactEmpty.tsx +28 -0
  258. package/src/components/atomic-crm/contacts/ContactImportButton.tsx +213 -0
  259. package/src/components/atomic-crm/contacts/ContactInputs.tsx +209 -0
  260. package/src/components/atomic-crm/contacts/ContactList.tsx +116 -0
  261. package/src/components/atomic-crm/contacts/ContactListContent.tsx +107 -0
  262. package/src/components/atomic-crm/contacts/ContactListFilter.tsx +126 -0
  263. package/src/components/atomic-crm/contacts/ContactMergeButton.tsx +263 -0
  264. package/src/components/atomic-crm/contacts/ContactShow.tsx +76 -0
  265. package/src/components/atomic-crm/contacts/ExportVCardButton.tsx +79 -0
  266. package/src/components/atomic-crm/contacts/TagsList.tsx +33 -0
  267. package/src/components/atomic-crm/contacts/TagsListEdit.tsx +155 -0
  268. package/src/components/atomic-crm/contacts/contacts_export.csv +3 -0
  269. package/src/components/atomic-crm/contacts/exportToVCard.ts +104 -0
  270. package/src/components/atomic-crm/contacts/index.tsx +14 -0
  271. package/src/components/atomic-crm/contacts/useContactImport.tsx +206 -0
  272. package/src/components/atomic-crm/dashboard/Dashboard.tsx +66 -0
  273. package/src/components/atomic-crm/dashboard/DashboardActivityLog.tsx +22 -0
  274. package/src/components/atomic-crm/dashboard/DashboardStepper.tsx +72 -0
  275. package/src/components/atomic-crm/dashboard/DealsChart.tsx +202 -0
  276. package/src/components/atomic-crm/dashboard/DealsPipeline.tsx +90 -0
  277. package/src/components/atomic-crm/dashboard/HotContacts.tsx +92 -0
  278. package/src/components/atomic-crm/dashboard/LatestNotes.tsx +116 -0
  279. package/src/components/atomic-crm/dashboard/TasksList.tsx +69 -0
  280. package/src/components/atomic-crm/dashboard/TasksListEmpty.tsx +22 -0
  281. package/src/components/atomic-crm/dashboard/TasksListFilter.tsx +72 -0
  282. package/src/components/atomic-crm/dashboard/Welcome.tsx +41 -0
  283. package/src/components/atomic-crm/deals/ContactList.tsx +31 -0
  284. package/src/components/atomic-crm/deals/DealArchivedList.tsx +105 -0
  285. package/src/components/atomic-crm/deals/DealCard.tsx +78 -0
  286. package/src/components/atomic-crm/deals/DealColumn.tsx +52 -0
  287. package/src/components/atomic-crm/deals/DealCreate.tsx +95 -0
  288. package/src/components/atomic-crm/deals/DealEdit.tsx +81 -0
  289. package/src/components/atomic-crm/deals/DealEmpty.tsx +63 -0
  290. package/src/components/atomic-crm/deals/DealInputs.tsx +103 -0
  291. package/src/components/atomic-crm/deals/DealList.tsx +95 -0
  292. package/src/components/atomic-crm/deals/DealListContent.tsx +245 -0
  293. package/src/components/atomic-crm/deals/DealShow.tsx +260 -0
  294. package/src/components/atomic-crm/deals/OnlyMineInput.tsx +30 -0
  295. package/src/components/atomic-crm/deals/deal.ts +5 -0
  296. package/src/components/atomic-crm/deals/dealUtils.ts +26 -0
  297. package/src/components/atomic-crm/deals/index.ts +6 -0
  298. package/src/components/atomic-crm/deals/stages.ts +28 -0
  299. package/src/components/atomic-crm/filters/FilterCategory.tsx +20 -0
  300. package/src/components/atomic-crm/layout/FormToolbar.tsx +12 -0
  301. package/src/components/atomic-crm/layout/Header.tsx +134 -0
  302. package/src/components/atomic-crm/layout/Layout.tsx +21 -0
  303. package/src/components/atomic-crm/layout/TopToolbar.tsx +24 -0
  304. package/src/components/atomic-crm/login/LoginSkeleton.tsx +18 -0
  305. package/src/components/atomic-crm/login/SignupPage.tsx +150 -0
  306. package/src/components/atomic-crm/login/StartPage.tsx +27 -0
  307. package/src/components/atomic-crm/misc/AsideSection.tsx +21 -0
  308. package/src/components/atomic-crm/misc/ContactOption.tsx +26 -0
  309. package/src/components/atomic-crm/misc/ImageEditorField.tsx +206 -0
  310. package/src/components/atomic-crm/misc/RelativeDate.tsx +5 -0
  311. package/src/components/atomic-crm/misc/Status.tsx +28 -0
  312. package/src/components/atomic-crm/misc/fetchWithTimeout.ts +19 -0
  313. package/src/components/atomic-crm/misc/isLinkedInUrl.ts +15 -0
  314. package/src/components/atomic-crm/misc/unsupportedDomains.const.ts +105 -0
  315. package/src/components/atomic-crm/misc/useAppBarHeight.ts +9 -0
  316. package/src/components/atomic-crm/misc/usePapaParse.tsx +144 -0
  317. package/src/components/atomic-crm/notes/Note.tsx +187 -0
  318. package/src/components/atomic-crm/notes/NoteAttachments.tsx +56 -0
  319. package/src/components/atomic-crm/notes/NoteCreate.tsx +112 -0
  320. package/src/components/atomic-crm/notes/NoteInputs.tsx +92 -0
  321. package/src/components/atomic-crm/notes/NotesIterator.tsx +37 -0
  322. package/src/components/atomic-crm/notes/StatusSelector.tsx +39 -0
  323. package/src/components/atomic-crm/notes/index.ts +3 -0
  324. package/src/components/atomic-crm/notes/utils.ts +13 -0
  325. package/src/components/atomic-crm/providers/commons/activity.ts +174 -0
  326. package/src/components/atomic-crm/providers/commons/canAccess.ts +26 -0
  327. package/src/components/atomic-crm/providers/commons/getCompanyAvatar.spec.ts +20 -0
  328. package/src/components/atomic-crm/providers/commons/getCompanyAvatar.ts +21 -0
  329. package/src/components/atomic-crm/providers/commons/getContactAvatar.spec.ts +80 -0
  330. package/src/components/atomic-crm/providers/commons/getContactAvatar.ts +70 -0
  331. package/src/components/atomic-crm/providers/commons/mergeContacts.ts +185 -0
  332. package/src/components/atomic-crm/providers/fakerest/authProvider.ts +74 -0
  333. package/src/components/atomic-crm/providers/fakerest/dataGenerator/companies.ts +53 -0
  334. package/src/components/atomic-crm/providers/fakerest/dataGenerator/contactNotes.ts +25 -0
  335. package/src/components/atomic-crm/providers/fakerest/dataGenerator/contacts.ts +103 -0
  336. package/src/components/atomic-crm/providers/fakerest/dataGenerator/dealNotes.ts +19 -0
  337. package/src/components/atomic-crm/providers/fakerest/dataGenerator/deals.ts +53 -0
  338. package/src/components/atomic-crm/providers/fakerest/dataGenerator/finalize.ts +10 -0
  339. package/src/components/atomic-crm/providers/fakerest/dataGenerator/index.ts +25 -0
  340. package/src/components/atomic-crm/providers/fakerest/dataGenerator/sales.ts +37 -0
  341. package/src/components/atomic-crm/providers/fakerest/dataGenerator/tags.ts +14 -0
  342. package/src/components/atomic-crm/providers/fakerest/dataGenerator/tasks.ts +55 -0
  343. package/src/components/atomic-crm/providers/fakerest/dataGenerator/types.ts +21 -0
  344. package/src/components/atomic-crm/providers/fakerest/dataGenerator/utils.ts +28 -0
  345. package/src/components/atomic-crm/providers/fakerest/dataProvider.ts +518 -0
  346. package/src/components/atomic-crm/providers/fakerest/index.ts +2 -0
  347. package/src/components/atomic-crm/providers/fakerest/internal/listParser.ts +48 -0
  348. package/src/components/atomic-crm/providers/fakerest/internal/supabaseAdapter.spec.ts +721 -0
  349. package/src/components/atomic-crm/providers/fakerest/internal/supabaseAdapter.ts +49 -0
  350. package/src/components/atomic-crm/providers/fakerest/internal/transformContainsFilter.spec.ts +35 -0
  351. package/src/components/atomic-crm/providers/fakerest/internal/transformContainsFilter.ts +17 -0
  352. package/src/components/atomic-crm/providers/fakerest/internal/transformFilter.ts +57 -0
  353. package/src/components/atomic-crm/providers/fakerest/internal/transformInFilter.spec.ts +32 -0
  354. package/src/components/atomic-crm/providers/fakerest/internal/transformInFilter.ts +17 -0
  355. package/src/components/atomic-crm/providers/fakerest/internal/transformOrFilter.spec.ts +23 -0
  356. package/src/components/atomic-crm/providers/fakerest/internal/transformOrFilter.ts +17 -0
  357. package/src/components/atomic-crm/providers/supabase/authProvider.ts +121 -0
  358. package/src/components/atomic-crm/providers/supabase/dataProvider.ts +407 -0
  359. package/src/components/atomic-crm/providers/supabase/index.ts +2 -0
  360. package/src/components/atomic-crm/providers/supabase/supabase.ts +34 -0
  361. package/src/components/atomic-crm/providers/types.ts +1 -0
  362. package/src/components/atomic-crm/root/CRM.tsx +167 -0
  363. package/src/components/atomic-crm/root/ConfigurationContext.tsx +80 -0
  364. package/src/components/atomic-crm/root/defaultConfiguration.ts +64 -0
  365. package/src/components/atomic-crm/root/i18nProvider.tsx +25 -0
  366. package/src/components/atomic-crm/sales/SaleName.tsx +13 -0
  367. package/src/components/atomic-crm/sales/SalesCreate.tsx +51 -0
  368. package/src/components/atomic-crm/sales/SalesEdit.tsx +82 -0
  369. package/src/components/atomic-crm/sales/SalesInputs.tsx +31 -0
  370. package/src/components/atomic-crm/sales/SalesList.tsx +62 -0
  371. package/src/components/atomic-crm/sales/index.ts +12 -0
  372. package/src/components/atomic-crm/settings/DatabaseSettings.tsx +169 -0
  373. package/src/components/atomic-crm/settings/SettingsPage.tsx +259 -0
  374. package/src/components/atomic-crm/setup/SupabaseSetupWizard.tsx +215 -0
  375. package/src/components/atomic-crm/simple-list/ListNoResults.tsx +53 -0
  376. package/src/components/atomic-crm/simple-list/ListPlaceholder.tsx +9 -0
  377. package/src/components/atomic-crm/simple-list/SimpleList.tsx +245 -0
  378. package/src/components/atomic-crm/simple-list/SimpleListItem.tsx +138 -0
  379. package/src/components/atomic-crm/simple-list/SimpleListLoading.tsx +60 -0
  380. package/src/components/atomic-crm/tags/RoundButton.tsx +10 -0
  381. package/src/components/atomic-crm/tags/TagChip.tsx +45 -0
  382. package/src/components/atomic-crm/tags/TagCreateModal.tsx +39 -0
  383. package/src/components/atomic-crm/tags/TagDialog.tsx +118 -0
  384. package/src/components/atomic-crm/tags/TagEditModal.tsx +42 -0
  385. package/src/components/atomic-crm/tags/colors.ts +12 -0
  386. package/src/components/atomic-crm/tasks/AddTask.tsx +191 -0
  387. package/src/components/atomic-crm/tasks/Task.tsx +184 -0
  388. package/src/components/atomic-crm/tasks/TaskEdit.tsx +96 -0
  389. package/src/components/atomic-crm/tasks/TasksIterator.tsx +30 -0
  390. package/src/components/atomic-crm/types.ts +226 -0
  391. package/src/components/supabase/forgot-password-page.tsx +86 -0
  392. package/src/components/supabase/layout.tsx +27 -0
  393. package/src/components/supabase/set-password-page.tsx +119 -0
  394. package/src/components/ui/README.md +34 -0
  395. package/src/components/ui/accordion.tsx +64 -0
  396. package/src/components/ui/alert.tsx +66 -0
  397. package/src/components/ui/avatar.tsx +99 -0
  398. package/src/components/ui/badge.tsx +46 -0
  399. package/src/components/ui/breadcrumb.tsx +109 -0
  400. package/src/components/ui/button.tsx +59 -0
  401. package/src/components/ui/card.tsx +92 -0
  402. package/src/components/ui/checkbox.tsx +30 -0
  403. package/src/components/ui/command.tsx +175 -0
  404. package/src/components/ui/dialog.tsx +133 -0
  405. package/src/components/ui/drawer.tsx +133 -0
  406. package/src/components/ui/dropdown-menu.tsx +255 -0
  407. package/src/components/ui/input.tsx +21 -0
  408. package/src/components/ui/label.tsx +24 -0
  409. package/src/components/ui/navigation-menu.tsx +168 -0
  410. package/src/components/ui/pagination.tsx +127 -0
  411. package/src/components/ui/popover.tsx +46 -0
  412. package/src/components/ui/progress.tsx +29 -0
  413. package/src/components/ui/radio-group.tsx +43 -0
  414. package/src/components/ui/select.tsx +183 -0
  415. package/src/components/ui/separator.tsx +26 -0
  416. package/src/components/ui/sheet.tsx +137 -0
  417. package/src/components/ui/sidebar.tsx +724 -0
  418. package/src/components/ui/skeleton.tsx +13 -0
  419. package/src/components/ui/sonner.tsx +38 -0
  420. package/src/components/ui/spinner.tsx +51 -0
  421. package/src/components/ui/switch.tsx +29 -0
  422. package/src/components/ui/table.tsx +114 -0
  423. package/src/components/ui/tabs.tsx +64 -0
  424. package/src/components/ui/textarea.tsx +18 -0
  425. package/src/components/ui/tooltip.tsx +61 -0
  426. package/src/hooks/saved-queries.tsx +67 -0
  427. package/src/hooks/simple-form-iterator-context.tsx +70 -0
  428. package/src/hooks/use-mobile.ts +21 -0
  429. package/src/hooks/useBulkExport.tsx +61 -0
  430. package/src/hooks/useSupportCreateSuggestion.tsx +188 -0
  431. package/src/hooks/user-menu-context.tsx +24 -0
  432. package/src/index.css +170 -0
  433. package/src/lib/field.type.ts +22 -0
  434. package/src/lib/genericMemo.ts +18 -0
  435. package/src/lib/i18nProvider.ts +9 -0
  436. package/src/lib/sanitizeInputRestProps.ts +46 -0
  437. package/src/lib/supabase-config.ts +123 -0
  438. package/src/lib/utils.ts +6 -0
  439. package/src/main.tsx +10 -0
  440. package/src/setupTests.js +5 -0
  441. package/src/vite-env.d.ts +1 -0
  442. package/supabase/config.toml +157 -0
  443. package/supabase/functions/.env.development +7 -0
  444. package/supabase/functions/_shared/db.ts +187 -0
  445. package/supabase/functions/_shared/supabaseAdmin.ts +13 -0
  446. package/supabase/functions/_shared/utils.ts +13 -0
  447. package/supabase/functions/mergeContacts/index.ts +215 -0
  448. package/supabase/functions/postmark/addNoteToContact.ts +129 -0
  449. package/supabase/functions/postmark/extractMailContactData.ts +41 -0
  450. package/supabase/functions/postmark/getExpectedAuthorization.ts +4 -0
  451. package/supabase/functions/postmark/getNoteContent.ts +6 -0
  452. package/supabase/functions/postmark/index.ts +210 -0
  453. package/supabase/functions/updatePassword/index.ts +50 -0
  454. package/supabase/functions/users/index.ts +206 -0
  455. package/supabase/migrations/20240730075029_init_db.sql +600 -0
  456. package/supabase/migrations/20240730075425_init_triggers.sql +57 -0
  457. package/supabase/migrations/20240806124555_task_sales_id.sql +1 -0
  458. package/supabase/migrations/20240807082449_remove-aquisition.sql +20 -0
  459. package/supabase/migrations/20240808141826_init_state_configure.sql +9 -0
  460. package/supabase/migrations/20240813084010_tags_policy.sql +18 -0
  461. package/supabase/migrations/20241104153231_sales_policies.sql +7 -0
  462. package/supabase/migrations/20250109152531_email_jsonb.sql +43 -0
  463. package/supabase/migrations/20250113132531_phone_jsonb.sql +67 -0
  464. package/supabase/migrations/20251204172855_merge_contacts_function.sql +153 -0
  465. package/supabase/migrations/20251204201317_drop_merge_contacts_function.sql +2 -0
  466. package/supabase/seed.sql +0 -0
  467. package/supabase/templates/invite.html +70 -0
  468. package/supabase/templates/recovery.html +75 -0
@@ -0,0 +1,582 @@
1
+ import get from "lodash/get";
2
+ import * as React from "react";
3
+ import type { ReactElement, ReactNode } from "react";
4
+ import { Children, useCallback, useMemo, useRef, useState } from "react";
5
+ import type {
6
+ ArrayInputContextValue,
7
+ RaRecord,
8
+ SimpleFormIteratorItemContextValue,
9
+ } from "ra-core";
10
+ import {
11
+ FormDataConsumer,
12
+ RecordContextProvider,
13
+ SimpleFormIteratorContext,
14
+ SimpleFormIteratorItemContext,
15
+ SourceContextProvider,
16
+ useArrayInput,
17
+ useRecordContext,
18
+ useResourceContext,
19
+ useSimpleFormIterator,
20
+ useSimpleFormIteratorItem,
21
+ useSourceContext,
22
+ useTranslate,
23
+ useWrappedSource,
24
+ } from "ra-core";
25
+ import type { UseFieldArrayReturn } from "react-hook-form";
26
+ import { useFormContext } from "react-hook-form";
27
+ import {
28
+ ArrowDownCircle,
29
+ ArrowUpCircle,
30
+ PlusCircle,
31
+ Trash,
32
+ XCircle,
33
+ } from "lucide-react";
34
+ import { cn } from "@/lib/utils";
35
+ import { Button } from "@/components/ui/button";
36
+ import {
37
+ Tooltip,
38
+ TooltipContent,
39
+ TooltipProvider,
40
+ TooltipTrigger,
41
+ } from "@/components/ui/tooltip";
42
+ import { Confirm } from "@/components/admin/confirm";
43
+ import { IconButtonWithTooltip } from "@/components/admin/icon-button-with-tooltip";
44
+
45
+ type GetItemLabelFunc = (index: number) => string | ReactElement;
46
+
47
+ /**
48
+ * An array input iterator for managing dynamic lists of items in forms.
49
+ *
50
+ * Renders a list of form items with add, remove, and reorder controls. Use inside ArrayInput
51
+ * for arrays of objects or scalar values. Supports inline layouts and custom buttons.
52
+ *
53
+ * @see {@link https://marmelab.com/shadcn-admin-kit/docs/arrayinput/ ArrayInput documentation}
54
+ *
55
+ * @example
56
+ * import { ArrayInput, SimpleFormIterator, TextInput } from '@/components/admin';
57
+ *
58
+ * const PostEdit = () => (
59
+ * <ArrayInput source="authors">
60
+ * <SimpleFormIterator>
61
+ * <TextInput source="name" />
62
+ * <TextInput source="email" />
63
+ * </SimpleFormIterator>
64
+ * </ArrayInput>
65
+ * );
66
+ */
67
+ export const SimpleFormIterator = (props: SimpleFormIteratorProps) => {
68
+ const {
69
+ addButton = defaultAddItemButton,
70
+ removeButton,
71
+ reOrderButtons,
72
+ children,
73
+ className,
74
+ resource,
75
+ disabled,
76
+ disableAdd = false,
77
+ disableClear,
78
+ disableRemove = false,
79
+ disableReordering,
80
+ inline,
81
+ getItemLabel = false,
82
+ } = props;
83
+
84
+ const finalSource = useWrappedSource("");
85
+ if (!finalSource) {
86
+ throw new Error(
87
+ "SimpleFormIterator can only be called within an iterator input like ArrayInput",
88
+ );
89
+ }
90
+
91
+ const [confirmIsOpen, setConfirmIsOpen] = useState<boolean>(false);
92
+ const { append, fields, move, remove, replace } = useArrayInput(props);
93
+ const { trigger, getValues } = useFormContext();
94
+ const translate = useTranslate();
95
+ const record = useRecordContext(props);
96
+ const initialDefaultValue = useRef({});
97
+
98
+ const removeField = useCallback(
99
+ (index: number) => {
100
+ remove(index);
101
+ const isScalarArray = getValues(finalSource).every(
102
+ (value: any) => typeof value !== "object",
103
+ );
104
+ if (isScalarArray) {
105
+ // Trigger validation on the Array to avoid ghost errors.
106
+ // Otherwise, validation errors on removed fields might still be displayed
107
+ trigger(finalSource);
108
+ }
109
+ },
110
+ [remove, trigger, finalSource, getValues],
111
+ );
112
+
113
+ if (fields.length > 0) {
114
+ const { id: _id, ...rest } = fields[0];
115
+ initialDefaultValue.current = rest;
116
+ for (const k in initialDefaultValue.current) {
117
+ // @ts-expect-error: reset fields
118
+ initialDefaultValue.current[k] = null;
119
+ }
120
+ }
121
+
122
+ const addField = useCallback(
123
+ (item: any = undefined) => {
124
+ let defaultValue = item;
125
+ if (item == null) {
126
+ defaultValue = initialDefaultValue.current;
127
+ if (
128
+ Children.count(children) === 1 &&
129
+ React.isValidElement(Children.only(children)) &&
130
+ // @ts-expect-error: Check if the child has a source prop
131
+ !Children.only(children).props.source &&
132
+ // Make sure it's not a FormDataConsumer
133
+ // @ts-expect-error: Check if the child is a FormDataConsumer
134
+ Children.only(children).type !== FormDataConsumer
135
+ ) {
136
+ // ArrayInput used for an array of scalar values
137
+ // (e.g. tags: ['foo', 'bar'])
138
+ defaultValue = "";
139
+ } else {
140
+ // ArrayInput used for an array of objects
141
+ // (e.g. authors: [{ firstName: 'John', lastName: 'Doe' }, { firstName: 'Jane', lastName: 'Doe' }])
142
+ defaultValue = defaultValue || ({} as Record<string, unknown>);
143
+ Children.forEach(children, (input) => {
144
+ if (
145
+ React.isValidElement(input) &&
146
+ input.type !== FormDataConsumer &&
147
+ // @ts-expect-error: Check if the child has a source prop
148
+ input.props.source
149
+ ) {
150
+ // @ts-expect-error: Check if the child has a source prop
151
+ defaultValue[input.props.source] =
152
+ // @ts-expect-error: Check if the child has a source prop
153
+ input.props.defaultValue ?? null;
154
+ }
155
+ });
156
+ }
157
+ }
158
+ append(defaultValue);
159
+ },
160
+ [append, children],
161
+ );
162
+
163
+ const handleReorder = useCallback(
164
+ (origin: number, destination: number) => {
165
+ move(origin, destination);
166
+ },
167
+ [move],
168
+ );
169
+
170
+ const handleArrayClear = useCallback(() => {
171
+ replace([]);
172
+ setConfirmIsOpen(false);
173
+ }, [replace]);
174
+
175
+ const records = get(record, finalSource);
176
+
177
+ const context = useMemo(
178
+ () => ({
179
+ total: fields.length,
180
+ add: addField,
181
+ remove: removeField,
182
+ clear: handleArrayClear,
183
+ reOrder: handleReorder,
184
+ source: finalSource,
185
+ }),
186
+ [
187
+ fields.length,
188
+ addField,
189
+ removeField,
190
+ handleArrayClear,
191
+ handleReorder,
192
+ finalSource,
193
+ ],
194
+ );
195
+ return fields ? (
196
+ <SimpleFormIteratorContext.Provider value={context}>
197
+ <div className={cn("w-full", disabled && "disabled", className)}>
198
+ <ul className="p-0 m-0 flex flex-col gap-2">
199
+ {fields.map((member, index) => (
200
+ <SimpleFormIteratorItem
201
+ key={member.id}
202
+ disabled={disabled}
203
+ disableRemove={disableRemove}
204
+ disableReordering={disableReordering}
205
+ fields={fields}
206
+ getItemLabel={getItemLabel}
207
+ index={index}
208
+ onRemoveField={removeField}
209
+ onReorder={handleReorder}
210
+ record={(records && records[index]) || {}}
211
+ removeButton={removeButton}
212
+ reOrderButtons={reOrderButtons}
213
+ resource={resource}
214
+ inline={inline}
215
+ >
216
+ {children}
217
+ </SimpleFormIteratorItem>
218
+ ))}
219
+ </ul>
220
+ {!disabled && !(disableAdd && (disableClear || disableRemove)) && (
221
+ <div className="flex flex-row items-center gap-2">
222
+ {!disableAdd && addButton}
223
+ {fields.length > 0 && !disableClear && !disableRemove && (
224
+ <>
225
+ <Confirm
226
+ isOpen={confirmIsOpen}
227
+ title={translate("ra.action.clear_array_input")}
228
+ content={translate("ra.message.clear_array_input")}
229
+ onConfirm={handleArrayClear}
230
+ onClose={() => setConfirmIsOpen(false)}
231
+ />
232
+ <ClearArrayButton onClick={() => setConfirmIsOpen(true)} />
233
+ </>
234
+ )}
235
+ </div>
236
+ )}
237
+ </div>
238
+ </SimpleFormIteratorContext.Provider>
239
+ ) : null;
240
+ };
241
+
242
+ export interface SimpleFormIteratorProps extends Partial<UseFieldArrayReturn> {
243
+ addButton?: ReactElement;
244
+ children?: ReactElement | ReactElement[];
245
+ className?: string;
246
+ readOnly?: boolean;
247
+ disabled?: boolean;
248
+ disableAdd?: boolean;
249
+ disableClear?: boolean;
250
+ disableRemove?: boolean | DisableRemoveFunction;
251
+ disableReordering?: boolean;
252
+ fullWidth?: boolean;
253
+ getItemLabel?: boolean | GetItemLabelFunc;
254
+ inline?: boolean;
255
+ meta?: {
256
+ // the type defined in FieldArrayRenderProps says error is boolean, which is wrong.
257
+ error?: any;
258
+ submitFailed?: boolean;
259
+ };
260
+ record?: RaRecord;
261
+ removeButton?: ReactElement;
262
+ reOrderButtons?: ReactElement;
263
+ resource?: string;
264
+ source?: string;
265
+ }
266
+
267
+ /**
268
+ * A single item in a SimpleFormIterator list with controls.
269
+ *
270
+ * Renders one item from an array with its input fields and action buttons (remove, reorder).
271
+ * Usually used internally by SimpleFormIterator but can be customized.
272
+ *
273
+ * @example
274
+ * import { SimpleFormIteratorItem } from '@/components/admin';
275
+ *
276
+ * // Typically used internally by SimpleFormIterator
277
+ */
278
+ export const SimpleFormIteratorItem = React.forwardRef(
279
+ (
280
+ props: SimpleFormIteratorItemProps,
281
+ ref: React.ForwardedRef<HTMLLIElement>,
282
+ ) => {
283
+ const {
284
+ children,
285
+ disabled,
286
+ disableReordering,
287
+ disableRemove,
288
+ getItemLabel,
289
+ index,
290
+ inline,
291
+ record,
292
+ removeButton = defaultRemoveItemButton,
293
+ reOrderButtons = defaultReOrderButtons,
294
+ } = props;
295
+ const resource = useResourceContext(props);
296
+ if (!resource) {
297
+ throw new Error(
298
+ "SimpleFormIteratorItem must be used in a ResourceContextProvider or be passed a resource prop.",
299
+ );
300
+ }
301
+ const { total, reOrder, remove } = useSimpleFormIterator();
302
+ // Returns a boolean to indicate whether to disable the remove button for certain fields.
303
+ // If disableRemove is a function, then call the function with the current record to
304
+ // determining if the button should be disabled. Otherwise, use a boolean property that
305
+ // enables or disables the button for all of the fields.
306
+ const disableRemoveField = (record: RaRecord) => {
307
+ if (typeof disableRemove === "boolean") {
308
+ return disableRemove;
309
+ }
310
+ return disableRemove && disableRemove(record);
311
+ };
312
+
313
+ const context = useMemo<SimpleFormIteratorItemContextValue>(
314
+ () => ({
315
+ index,
316
+ total,
317
+ reOrder: (newIndex) => reOrder(index, newIndex),
318
+ remove: () => remove(index),
319
+ }),
320
+ [index, total, reOrder, remove],
321
+ );
322
+
323
+ const label =
324
+ typeof getItemLabel === "function" ? getItemLabel(index) : getItemLabel;
325
+
326
+ const parentSourceContext = useSourceContext();
327
+ const sourceContext = useMemo(
328
+ () => ({
329
+ getSource: (source: string) => {
330
+ if (!source) {
331
+ // source can be empty for scalar values, e.g.
332
+ // <ArrayInput source="tags" /> => SourceContext is "tags"
333
+ // <SimpleFormIterator> => SourceContext is "tags.0"
334
+ // <TextInput /> => use its parent's getSource so finalSource = "tags.0"
335
+ // </SimpleFormIterator>
336
+ // </ArrayInput>
337
+ return parentSourceContext.getSource(`${index}`);
338
+ } else {
339
+ // Normal input with source, e.g.
340
+ // <ArrayInput source="orders" /> => SourceContext is "orders"
341
+ // <SimpleFormIterator> => SourceContext is "orders.0"
342
+ // <DateInput source="date" /> => use its parent's getSource so finalSource = "orders.0.date"
343
+ // </SimpleFormIterator>
344
+ // </ArrayInput>
345
+ return parentSourceContext.getSource(`${index}.${source}`);
346
+ }
347
+ },
348
+ getLabel: (source: string) => {
349
+ // <ArrayInput source="orders" /> => LabelContext is "orders"
350
+ // <SimpleFormIterator> => LabelContext is ALSO "orders"
351
+ // <DateInput source="date" /> => use its parent's getLabel so finalLabel = "orders.date"
352
+ // </SimpleFormIterator>
353
+ // </ArrayInput>
354
+ //
355
+ // we don't prefix with the index to avoid that translation keys contain it
356
+ return parentSourceContext.getLabel(source);
357
+ },
358
+ }),
359
+ [index, parentSourceContext],
360
+ );
361
+
362
+ return (
363
+ <SimpleFormIteratorItemContext.Provider value={context}>
364
+ <li
365
+ ref={ref}
366
+ className={cn(
367
+ "flex flex-row items-start justify-between gap-2 pb-2 border-b border-border last:border-b-0",
368
+ // Align the buttons with the input
369
+ "[&:has(label)>.simple-form-iterator-item-actions]:pt-10",
370
+ )}
371
+ >
372
+ {label != null && label !== false && (
373
+ <p className="text-sm text-muted-foreground mb-2">{label}</p>
374
+ )}
375
+ <SourceContextProvider value={sourceContext}>
376
+ <RecordContextProvider value={record}>
377
+ <div
378
+ className={cn(
379
+ "flex flex-1 gap-2",
380
+ inline ? "flex-col sm:flex-row gap-2" : "flex-col",
381
+ )}
382
+ >
383
+ {children}
384
+ </div>
385
+ </RecordContextProvider>
386
+ </SourceContextProvider>
387
+ {!disabled && (
388
+ <div className="simple-form-iterator-item-actions flex flex-row h-9 items-center gap-1">
389
+ {!disableReordering && reOrderButtons}
390
+ {!disableRemoveField(record) && removeButton}
391
+ </div>
392
+ )}
393
+ </li>
394
+ </SimpleFormIteratorItemContext.Provider>
395
+ );
396
+ },
397
+ );
398
+
399
+ export type DisableRemoveFunction = (record: RaRecord) => boolean;
400
+
401
+ export type SimpleFormIteratorItemProps = Partial<ArrayInputContextValue> & {
402
+ children?: ReactNode;
403
+ disabled?: boolean;
404
+ disableRemove?: boolean | DisableRemoveFunction;
405
+ disableReordering?: boolean;
406
+ getItemLabel?: boolean | GetItemLabelFunc;
407
+ index: number;
408
+ inline?: boolean;
409
+ onRemoveField: (index: number) => void;
410
+ onReorder: (origin: number, destination: number) => void;
411
+ record: RaRecord;
412
+ removeButton?: ReactElement;
413
+ reOrderButtons?: ReactElement;
414
+ resource?: string;
415
+ source?: string;
416
+ };
417
+
418
+ /**
419
+ * A button to add new items to a SimpleFormIterator.
420
+ *
421
+ * Renders a plus icon button that appends a new item to the array. Works with the
422
+ * SimpleFormIterator context to add items with default values.
423
+ *
424
+ * @example
425
+ * import { ArrayInput, SimpleFormIterator, AddItemButton } from '@/components/admin';
426
+ *
427
+ * const PostEdit = () => (
428
+ * <ArrayInput source="tags">
429
+ * <SimpleFormIterator addButton={<AddItemButton />}>
430
+ * ...
431
+ * </SimpleFormIterator>
432
+ * </ArrayInput>
433
+ * );
434
+ */
435
+ export const AddItemButton = (props: React.ComponentProps<"button">) => {
436
+ const { add, source } = useSimpleFormIterator();
437
+ const { className, ...rest } = props;
438
+ const translate = useTranslate();
439
+ return (
440
+ <TooltipProvider>
441
+ <Tooltip>
442
+ <TooltipTrigger asChild>
443
+ <Button
444
+ type="button"
445
+ variant="ghost"
446
+ size="icon"
447
+ onClick={() => add()}
448
+ className={cn("button-add", `button-add-${source}`, className)}
449
+ {...rest}
450
+ >
451
+ <PlusCircle className="h-5 w-5" />
452
+ </Button>
453
+ </TooltipTrigger>
454
+ <TooltipContent>{translate("ra.action.add")}</TooltipContent>
455
+ </Tooltip>
456
+ </TooltipProvider>
457
+ );
458
+ };
459
+
460
+ /**
461
+ * Up and down buttons for reordering items in a SimpleFormIterator.
462
+ *
463
+ * Renders arrow buttons that move an item up or down in the list. Used internally
464
+ * by SimpleFormIterator but can be customized.
465
+ *
466
+ * @example
467
+ * import { SimpleFormIterator, ReOrderButtons } from '@/components/admin';
468
+ *
469
+ * const PostEdit = () => (
470
+ * <SimpleFormIterator reOrderButtons={<ReOrderButtons />}>
471
+ * ...
472
+ * </SimpleFormIterator>
473
+ * );
474
+ */
475
+ export const ReOrderButtons = ({ className }: { className?: string }) => {
476
+ const { index, total, reOrder } = useSimpleFormIteratorItem();
477
+ const { source } = useSimpleFormIterator();
478
+
479
+ return (
480
+ <span
481
+ className={cn(
482
+ "button-reorder",
483
+ `button-reorder-${source}-${index}`,
484
+ className,
485
+ )}
486
+ >
487
+ <IconButtonWithTooltip
488
+ label="ra.action.move_up"
489
+ onClick={() => reOrder(index - 1)}
490
+ disabled={index <= 0}
491
+ >
492
+ <ArrowUpCircle className="h-4 w-4" />
493
+ </IconButtonWithTooltip>
494
+ <IconButtonWithTooltip
495
+ label="ra.action.move_down"
496
+ onClick={() => reOrder(index + 1)}
497
+ disabled={total == null || index >= total - 1}
498
+ >
499
+ <ArrowDownCircle className="h-4 w-4" />
500
+ </IconButtonWithTooltip>
501
+ </span>
502
+ );
503
+ };
504
+
505
+ /**
506
+ * A button to clear all items from a SimpleFormIterator.
507
+ *
508
+ * Renders a trash icon button that removes all items from the array after confirmation.
509
+ * Used internally by SimpleFormIterator when disableClear is false.
510
+ *
511
+ * @example
512
+ * import { ClearArrayButton } from '@/components/admin';
513
+ *
514
+ * // Typically used internally by SimpleFormIterator
515
+ */
516
+ export const ClearArrayButton = (props: React.ComponentProps<"button">) => {
517
+ const translate = useTranslate();
518
+ return (
519
+ <TooltipProvider>
520
+ <Tooltip>
521
+ <TooltipTrigger asChild>
522
+ <Button type="button" variant="ghost" size="icon" {...props}>
523
+ <Trash className="h-5 w-5 text-red-500" />
524
+ </Button>
525
+ </TooltipTrigger>
526
+ <TooltipContent>
527
+ {translate("ra.action.clear_array_input")}
528
+ </TooltipContent>
529
+ </Tooltip>
530
+ </TooltipProvider>
531
+ );
532
+ };
533
+
534
+ /**
535
+ * A button to remove a single item from a SimpleFormIterator.
536
+ *
537
+ * Renders a close icon button that removes the current item from the array. Used internally
538
+ * by SimpleFormIterator for each item when disableRemove is false.
539
+ *
540
+ * @example
541
+ * import { SimpleFormIterator, RemoveItemButton } from '@/components/admin';
542
+ *
543
+ * const PostEdit = () => (
544
+ * <SimpleFormIterator removeButton={<RemoveItemButton />}>
545
+ * ...
546
+ * </SimpleFormIterator>
547
+ * );
548
+ */
549
+ export const RemoveItemButton = (props: React.ComponentProps<"button">) => {
550
+ const { remove, index } = useSimpleFormIteratorItem();
551
+ const { source } = useSimpleFormIterator();
552
+ const { className, ...rest } = props;
553
+ const translate = useTranslate();
554
+
555
+ return (
556
+ <TooltipProvider>
557
+ <Tooltip>
558
+ <TooltipTrigger asChild>
559
+ <Button
560
+ type="button"
561
+ variant="ghost"
562
+ size="icon"
563
+ onClick={() => remove()}
564
+ className={cn(
565
+ "button-remove",
566
+ `button-remove-${source}-${index}`,
567
+ className,
568
+ )}
569
+ {...rest}
570
+ >
571
+ <XCircle className="h-5 w-5 text-red-500" />
572
+ </Button>
573
+ </TooltipTrigger>
574
+ <TooltipContent>{translate("ra.action.remove")}</TooltipContent>
575
+ </Tooltip>
576
+ </TooltipProvider>
577
+ );
578
+ };
579
+
580
+ const defaultAddItemButton = <AddItemButton />;
581
+ const defaultRemoveItemButton = <RemoveItemButton />;
582
+ const defaultReOrderButtons = <ReOrderButtons />;
@@ -0,0 +1,95 @@
1
+ import * as React from "react";
2
+ import type { ReactNode } from "react";
3
+ import { Children } from "react";
4
+ import type { FormProps } from "ra-core";
5
+ import { Form } from "ra-core";
6
+ import { cn } from "@/lib/utils";
7
+ import { CancelButton } from "@/components/admin/cancel-button";
8
+ import { SaveButton } from "@/components/admin/form";
9
+
10
+ /**
11
+ * A simple form layout with vertical stacking, validation, and default toolbar.
12
+ *
13
+ * Automatically includes a toolbar with Cancel and Save buttons unless you provide a custom toolbar.
14
+ *
15
+ * @see {@link https://marmelab.com/shadcn-admin-kit/docs/simpleform/ SimpleForm documentation}
16
+ *
17
+ * @example
18
+ * import { Create, SimpleForm, TextInput } from '@/components/admin';
19
+ *
20
+ * const PostCreate = () => (
21
+ * <Create>
22
+ * <SimpleForm>
23
+ * <TextInput source="title" />
24
+ * <TextInput source="body" />
25
+ * </SimpleForm>
26
+ * </Create>
27
+ * );
28
+ */
29
+ export const SimpleForm = ({
30
+ children,
31
+ className,
32
+ toolbar = defaultFormToolbar,
33
+ ...rest
34
+ }: SimpleFormProps) => (
35
+ <Form
36
+ className={cn(`flex flex-col gap-4 w-full max-w-lg`, className)}
37
+ {...rest}
38
+ >
39
+ {children}
40
+ {toolbar}
41
+ </Form>
42
+ );
43
+
44
+ /**
45
+ * A sticky form toolbar with default Cancel and Save buttons.
46
+ *
47
+ * Provides a consistent action bar for forms that sticks to the bottom of the viewport. By default,
48
+ * renders Cancel and Save buttons, but you can provide custom buttons as children.
49
+ *
50
+ * @example
51
+ * import { FormToolbar, CancelButton, SaveButton } from '@/components/admin';
52
+ *
53
+ * const CustomToolbar = () => (
54
+ * <FormToolbar>
55
+ * <CancelButton />
56
+ * <SaveButton label="Publish" />
57
+ * </FormToolbar>
58
+ * );
59
+ */
60
+ export const FormToolbar = ({
61
+ children,
62
+ className,
63
+ ...rest
64
+ }: FormToolbarProps) => (
65
+ <div
66
+ {...rest}
67
+ className={cn(
68
+ "sticky pt-4 pb-4 md:block md:pt-2 md:pb-0 bottom-0 bg-linear-to-b from-transparent to-background to-10%",
69
+ className,
70
+ )}
71
+ role="toolbar"
72
+ >
73
+ {Children.count(children) === 0 ? (
74
+ <div className="flex flex-row gap-2 justify-end">
75
+ <CancelButton />
76
+ <SaveButton />
77
+ </div>
78
+ ) : (
79
+ children
80
+ )}
81
+ </div>
82
+ );
83
+
84
+ export type SimpleFormProps = {
85
+ children: ReactNode;
86
+ className?: string;
87
+ toolbar?: ReactNode;
88
+ } & FormProps;
89
+
90
+ export interface FormToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
91
+ children?: ReactNode;
92
+ className?: string;
93
+ }
94
+
95
+ const defaultFormToolbar = <FormToolbar />;
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ /**
4
+ * @deprecated Use a simple div with flex and flex-col instead
5
+ */
6
+ export const SimpleShowLayout = ({ children }: { children: ReactNode }) => (
7
+ <div className="flex flex-col gap-4">{children}</div>
8
+ );