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,67 @@
1
+ import {
2
+ RecordContextProvider,
3
+ RecordRepresentation,
4
+ useListContext,
5
+ } from "ra-core";
6
+ import { Badge } from "@/components/ui/badge";
7
+
8
+ /**
9
+ * Renders a horizontal list of records from a ListContext, displaying each as a badge.
10
+ *
11
+ * This component is used inside ArrayField, ReferenceArrayField, or ReferenceManyField to display
12
+ * records in a compact inline format. By default, it renders each record with its record representation.
13
+ *
14
+ * @see {@link https://marmelab.com/shadcn-admin-kit/docs/singlefieldlist/ SingleFieldList documentation}
15
+ *
16
+ * @example
17
+ * import {
18
+ * Show,
19
+ * RecordField,
20
+ * TextField,
21
+ * ReferenceArrayField,
22
+ * SingleFieldList,
23
+ * } from '@/components/admin';
24
+ *
25
+ * const PostShow = () => (
26
+ * <Show>
27
+ * <div className="flex flex-col gap-4">
28
+ * <RecordField source="title" />
29
+ * <RecordField label="Tags">
30
+ * <ReferenceArrayField reference="tags" source="tag_ids">
31
+ * <SingleFieldList>
32
+ * <TextField source="name" />
33
+ * </SingleFieldList>
34
+ * </ReferenceArrayField>
35
+ * </RecordField>
36
+ * </div>
37
+ * </Show>
38
+ * );
39
+ */
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
+ export const SingleFieldList = <RecordType = any,>({
42
+ children,
43
+ render,
44
+ className,
45
+ }: {
46
+ children?: React.ReactNode;
47
+ render?: (record: RecordType, index: number) => React.ReactNode;
48
+ className?: string;
49
+ }) => {
50
+ const { data } = useListContext();
51
+
52
+ return (
53
+ <div className={`flex gap-2 ${className}`}>
54
+ {data?.map((record, index) => (
55
+ <RecordContextProvider key={index} value={record}>
56
+ {render ? render(record, index) : children || <DefaultChildren />}
57
+ </RecordContextProvider>
58
+ ))}
59
+ </div>
60
+ );
61
+ };
62
+
63
+ const DefaultChildren = () => (
64
+ <Badge variant="outline">
65
+ <RecordRepresentation />
66
+ </Badge>
67
+ );
@@ -0,0 +1,152 @@
1
+ import * as React from "react";
2
+ import { memo } from "react";
3
+ import { ArrowUpDown, ChevronDown } from "lucide-react";
4
+ import {
5
+ shallowEqual,
6
+ useListSortContext,
7
+ useTranslate,
8
+ useTranslateLabel,
9
+ } from "ra-core";
10
+ import { useIsMobile } from "@/hooks/use-mobile";
11
+ import { Button } from "@/components/ui/button";
12
+ import {
13
+ DropdownMenu,
14
+ DropdownMenuContent,
15
+ DropdownMenuItem,
16
+ DropdownMenuTrigger,
17
+ } from "@/components/ui/dropdown-menu";
18
+ import {
19
+ Tooltip,
20
+ TooltipContent,
21
+ TooltipProvider,
22
+ TooltipTrigger,
23
+ } from "@/components/ui/tooltip";
24
+
25
+ type ButtonProps = React.ComponentProps<typeof Button>;
26
+
27
+ /**
28
+ * A button that opens a dropdown menu to change list sorting.
29
+ *
30
+ * Displays current sort field and order, and toggles between ASC and DESC when clicking the same field again.
31
+ *
32
+ * @see {@link https://marmelab.com/shadcn-admin-kit/docs/sortbutton/ SortButton documentation}
33
+ *
34
+ * @example
35
+ * import { SortButton } from '@/components/admin';
36
+ *
37
+ * const PostList = () => (
38
+ * <List render={({ data }) => (
39
+ * <div>
40
+ * <SortButton fields={["title", "published_at"]} />
41
+ * <ul>
42
+ * {data.map(post => (
43
+ * <li key={post.id}>{post.title}</li>
44
+ * ))}
45
+ * </ul>
46
+ * </div>
47
+ * )}>
48
+ * );
49
+ */
50
+ const SortButtonComponent = (props: SortButtonProps) => {
51
+ const {
52
+ fields,
53
+ label = "ra.sort.sort_by",
54
+ icon = defaultIcon,
55
+ resource: resourceProp,
56
+ ...rest
57
+ } = props;
58
+ const { resource: resourceFromContext, sort, setSort } = useListSortContext();
59
+ const resource = resourceProp || resourceFromContext;
60
+ const translate = useTranslate();
61
+ const translateLabel = useTranslateLabel();
62
+ const isMobile = useIsMobile();
63
+ const [open, setOpen] = React.useState(false);
64
+
65
+ const handleChangeSort = (field: string) => {
66
+ setSort({
67
+ field,
68
+ order: field === sort.field ? inverseOrder(sort.order) : "ASC",
69
+ });
70
+ setOpen(false);
71
+ };
72
+
73
+ const fieldLabel = translateLabel({
74
+ resource,
75
+ source: sort.field,
76
+ });
77
+ const buttonLabel = translate(label, {
78
+ field: fieldLabel,
79
+ field_lower_first:
80
+ typeof fieldLabel === "string"
81
+ ? fieldLabel.charAt(0).toLowerCase() + fieldLabel.slice(1)
82
+ : undefined,
83
+ order: translate(`ra.sort.${sort.order}`),
84
+ _: label,
85
+ });
86
+
87
+ return (
88
+ <DropdownMenu open={open} onOpenChange={setOpen}>
89
+ {isMobile ? (
90
+ <TooltipProvider>
91
+ <Tooltip>
92
+ <DropdownMenuTrigger asChild>
93
+ <TooltipTrigger asChild>
94
+ <Button
95
+ variant="outline"
96
+ size="icon"
97
+ aria-label={buttonLabel}
98
+ {...rest}
99
+ >
100
+ {icon}
101
+ </Button>
102
+ </TooltipTrigger>
103
+ </DropdownMenuTrigger>
104
+ <TooltipContent>
105
+ <p>{buttonLabel}</p>
106
+ </TooltipContent>
107
+ </Tooltip>
108
+ </TooltipProvider>
109
+ ) : (
110
+ <DropdownMenuTrigger asChild>
111
+ <Button variant="outline" size="sm" className="h-9" {...rest}>
112
+ {icon}
113
+ <span className="ml-2">{buttonLabel}</span>
114
+ <ChevronDown className="ml-2 h-4 w-4" />
115
+ </Button>
116
+ </DropdownMenuTrigger>
117
+ )}
118
+ <DropdownMenuContent align="start">
119
+ {fields.map((field) => (
120
+ <DropdownMenuItem key={field} onClick={() => handleChangeSort(field)}>
121
+ {translateLabel({
122
+ resource,
123
+ source: field,
124
+ })}{" "}
125
+ {translate(
126
+ `ra.sort.${
127
+ sort.field === field ? inverseOrder(sort.order) : "ASC"
128
+ }`,
129
+ )}
130
+ </DropdownMenuItem>
131
+ ))}
132
+ </DropdownMenuContent>
133
+ </DropdownMenu>
134
+ );
135
+ };
136
+
137
+ const defaultIcon = <ArrowUpDown className="h-4 w-4" />;
138
+
139
+ const inverseOrder = (sort: string) => (sort === "ASC" ? "DESC" : "ASC");
140
+
141
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
+ const arePropsEqual = (prevProps: any, nextProps: any) =>
143
+ shallowEqual(prevProps.fields, nextProps.fields);
144
+
145
+ export interface SortButtonProps extends ButtonProps {
146
+ fields: string[];
147
+ icon?: React.ReactNode;
148
+ label?: string;
149
+ resource?: string;
150
+ }
151
+
152
+ export const SortButton = memo(SortButtonComponent, arePropsEqual);
@@ -0,0 +1,46 @@
1
+ import { cn } from "@/lib/utils";
2
+ import type { VariantProps } from "class-variance-authority";
3
+ import { cva } from "class-variance-authority";
4
+ import { Loader2 } from "lucide-react";
5
+
6
+ const spinnerVariants = cva("flex-col items-center justify-center", {
7
+ variants: {
8
+ show: {
9
+ true: "flex",
10
+ false: "hidden",
11
+ },
12
+ },
13
+ defaultVariants: {
14
+ show: true,
15
+ },
16
+ });
17
+
18
+ const loaderVariants = cva("animate-spin text-primary", {
19
+ variants: {
20
+ size: {
21
+ small: "size-6",
22
+ medium: "size-8",
23
+ large: "size-12",
24
+ },
25
+ },
26
+ defaultVariants: {
27
+ size: "medium",
28
+ },
29
+ });
30
+
31
+ interface SpinnerContentProps
32
+ extends VariantProps<typeof spinnerVariants>,
33
+ VariantProps<typeof loaderVariants> {
34
+ className?: string;
35
+ }
36
+
37
+ /**
38
+ * Animated spinner component for loading states.
39
+ */
40
+ export function Spinner({ size, show, className }: SpinnerContentProps) {
41
+ return (
42
+ <span className={spinnerVariants({ show })}>
43
+ <Loader2 className={cn(loaderVariants({ size }), className)} />
44
+ </span>
45
+ );
46
+ }
@@ -0,0 +1,60 @@
1
+ import type { HTMLAttributes } from "react";
2
+ import { useFieldValue, useTranslate } from "ra-core";
3
+ import type { FieldProps } from "@/lib/field.type";
4
+
5
+ /**
6
+ * Displays a text value from a record field inside a span element.
7
+ *
8
+ * This is the default field component used in DataTable columns and RecordField components.
9
+ *
10
+ * @see {@link https://marmelab.com/shadcn-admin-kit/docs/textfield/ TextField documentation}
11
+ *
12
+ * @example
13
+ * import { List, DataTable, TextField } from '@/components/admin';
14
+ *
15
+ * export const UserList = () => (
16
+ * <List>
17
+ * <DataTable>
18
+ * <DataTable.Col source="id" />
19
+ * <DataTable.Col>
20
+ * <TextField source="name" empty="resources.users.fields.name.empty" />
21
+ * </DataTable.Col>
22
+ * </DataTable>
23
+ * </List>
24
+ * );
25
+ */
26
+ export const TextField = <
27
+ RecordType extends Record<string, any> = Record<string, any>,
28
+ >({
29
+ defaultValue,
30
+ source,
31
+ record,
32
+ empty,
33
+ ...rest
34
+ }: TextFieldProps<RecordType>) => {
35
+ const value = useFieldValue({ defaultValue, source, record });
36
+ const translate = useTranslate();
37
+
38
+ if (value == null) {
39
+ if (!empty) {
40
+ return null;
41
+ }
42
+
43
+ return (
44
+ <span {...rest}>
45
+ {typeof empty === "string" ? translate(empty, { _: empty }) : empty}
46
+ </span>
47
+ );
48
+ }
49
+
50
+ return (
51
+ <span {...rest}>
52
+ {typeof value !== "string" ? value.toString() : value}
53
+ </span>
54
+ );
55
+ };
56
+
57
+ export interface TextFieldProps<
58
+ RecordType extends Record<string, any> = Record<string, any>,
59
+ > extends FieldProps<RecordType>,
60
+ HTMLAttributes<HTMLSpanElement> {}
@@ -0,0 +1,77 @@
1
+ import type { InputProps } from "ra-core";
2
+ import { useInput, useResourceContext, FieldTitle } from "ra-core";
3
+ import {
4
+ FormControl,
5
+ FormError,
6
+ FormField,
7
+ FormLabel,
8
+ } from "@/components/admin/form";
9
+ import { Input } from "@/components/ui/input";
10
+ import { Textarea } from "@/components/ui/textarea";
11
+ import { InputHelperText } from "@/components/admin/input-helper-text";
12
+
13
+ export type TextInputProps = InputProps & {
14
+ multiline?: boolean;
15
+ inputClassName?: string;
16
+ } & React.ComponentProps<"textarea"> &
17
+ React.ComponentProps<"input">;
18
+
19
+ /**
20
+ * Single-line or multiline text input for string values.
21
+ *
22
+ * Use `<TextInput>` for short text fields like titles or names. Set `multiline` to `true`
23
+ * for longer content like descriptions or comments. Wraps shadcn's `<Input>` or `<Textarea>`
24
+ * component depending on the `multiline` prop.
25
+ *
26
+ * @see {@link https://marmelab.com/shadcn-admin-kit/docs/textinput/ TextInput documentation}
27
+ *
28
+ * @example
29
+ * import { Edit, SimpleForm, TextInput } from '@/components/admin';
30
+ *
31
+ * const PostEdit = () => (
32
+ * <Edit>
33
+ * <SimpleForm>
34
+ * <TextInput source="title" />
35
+ * <TextInput source="description" multiline rows={4} />
36
+ * </SimpleForm>
37
+ * </Edit>
38
+ * );
39
+ */
40
+ export const TextInput = (props: TextInputProps) => {
41
+ const resource = useResourceContext(props);
42
+ const {
43
+ label,
44
+ source,
45
+ multiline,
46
+ className,
47
+ inputClassName,
48
+ validate: _validateProp,
49
+ format: _formatProp,
50
+ ...rest
51
+ } = props;
52
+ const { id, field, isRequired } = useInput(props);
53
+
54
+ return (
55
+ <FormField id={id} className={className} name={field.name}>
56
+ {label !== false && (
57
+ <FormLabel>
58
+ <FieldTitle
59
+ label={label}
60
+ source={source}
61
+ resource={resource}
62
+ isRequired={isRequired}
63
+ />
64
+ </FormLabel>
65
+ )}
66
+ <FormControl>
67
+ {multiline ? (
68
+ <Textarea {...rest} {...field} className={inputClassName} />
69
+ ) : (
70
+ <Input {...rest} {...field} className={inputClassName} />
71
+ )}
72
+ </FormControl>
73
+ <InputHelperText helperText={props.helperText} />
74
+ <FormError />
75
+ </FormField>
76
+ );
77
+ };
@@ -0,0 +1,48 @@
1
+ import { Check, Moon, Sun } from "lucide-react";
2
+ import { Button } from "@/components/ui/button";
3
+ import {
4
+ DropdownMenu,
5
+ DropdownMenuContent,
6
+ DropdownMenuItem,
7
+ DropdownMenuTrigger,
8
+ } from "@/components/ui/dropdown-menu";
9
+ import { cn } from "@/lib/utils";
10
+ import { useTheme } from "@/components/admin/theme-provider";
11
+
12
+ /**
13
+ * Toggle button that lets users switch between light, dark, and system UI themes.
14
+ *
15
+ * User's selection is persisted using the store.
16
+ * Automatically included in the default Layout component header.
17
+ *
18
+ * @see {@link https://marmelab.com/shadcn-admin-kit/docs/thememodetoggle ThemeModeToggle documentation}
19
+ */
20
+ export function ThemeModeToggle() {
21
+ const { theme, setTheme } = useTheme();
22
+
23
+ return (
24
+ <DropdownMenu modal={false}>
25
+ <DropdownMenuTrigger asChild>
26
+ <Button variant="ghost" size="icon" className="hidden sm:inline-flex">
27
+ <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
28
+ <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
29
+ <span className="sr-only">Toggle theme</span>
30
+ </Button>
31
+ </DropdownMenuTrigger>
32
+ <DropdownMenuContent align="end">
33
+ <DropdownMenuItem onClick={() => setTheme("light")}>
34
+ Light
35
+ <Check className={cn("ml-auto", theme !== "light" && "hidden")} />
36
+ </DropdownMenuItem>
37
+ <DropdownMenuItem onClick={() => setTheme("dark")}>
38
+ Dark
39
+ <Check className={cn("ml-auto", theme !== "dark" && "hidden")} />
40
+ </DropdownMenuItem>
41
+ <DropdownMenuItem onClick={() => setTheme("system")}>
42
+ System
43
+ <Check className={cn("ml-auto", theme !== "system" && "hidden")} />
44
+ </DropdownMenuItem>
45
+ </DropdownMenuContent>
46
+ </DropdownMenu>
47
+ );
48
+ }
@@ -0,0 +1,74 @@
1
+ import { createContext, useContext, useEffect } from "react";
2
+ import { useStore } from "ra-core";
3
+
4
+ type Theme = "dark" | "light" | "system";
5
+
6
+ type ThemeProviderProps = {
7
+ children: React.ReactNode;
8
+ defaultTheme?: Theme;
9
+ storageKey?: string;
10
+ };
11
+
12
+ type ThemeProviderState = {
13
+ theme: Theme;
14
+ setTheme: (theme: Theme) => void;
15
+ };
16
+
17
+ const initialState: ThemeProviderState = {
18
+ theme: "system",
19
+ setTheme: () => null,
20
+ };
21
+
22
+ const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
23
+
24
+ /**
25
+ * Theme provider that enables light, dark, and system theme modes.
26
+ *
27
+ * @internal
28
+ */
29
+ export function ThemeProvider({
30
+ children,
31
+ defaultTheme = "system",
32
+ storageKey = "theme",
33
+ ...props
34
+ }: ThemeProviderProps) {
35
+ const [theme, setTheme] = useStore<Theme>(storageKey, defaultTheme);
36
+
37
+ useEffect(() => {
38
+ const root = window.document.documentElement;
39
+
40
+ root.classList.remove("light", "dark");
41
+
42
+ if (theme === "system") {
43
+ const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
44
+ .matches
45
+ ? "dark"
46
+ : "light";
47
+
48
+ root.classList.add(systemTheme);
49
+ return;
50
+ }
51
+
52
+ root.classList.add(theme);
53
+ }, [theme]);
54
+
55
+ const value = {
56
+ theme,
57
+ setTheme,
58
+ };
59
+
60
+ return (
61
+ <ThemeProviderContext.Provider {...props} value={value}>
62
+ {children}
63
+ </ThemeProviderContext.Provider>
64
+ );
65
+ }
66
+
67
+ export const useTheme = () => {
68
+ const context = useContext(ThemeProviderContext);
69
+
70
+ if (context === undefined)
71
+ throw new Error("useTheme must be used within a ThemeProvider");
72
+
73
+ return context;
74
+ };
@@ -0,0 +1,77 @@
1
+ import React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { Button } from "@/components/ui/button";
4
+ import { useListContext, useTranslate } from "ra-core";
5
+ import matches from "lodash/matches";
6
+ import pickBy from "lodash/pickBy";
7
+ import { CircleX } from "lucide-react";
8
+
9
+ /**
10
+ * A button that toggles a specific filter value on/off.
11
+ *
12
+ * Renders a button that applies or removes a filter when clicked. Shows a close icon when the
13
+ * filter is active. Useful for quick filter presets like status or category.
14
+ *
15
+ * @see {@link https://marmelab.com/shadcn-admin-kit/docs/togglefilterbutton/ ToggleFilterButton documentation}
16
+ *
17
+ * @example
18
+ * import { ToggleFilterButton } from '@/components/admin';
19
+ *
20
+ * const PostFilters = () => (
21
+ * <div className="flex flex-row gap-2">
22
+ * <ToggleFilterButton label="Published" value={{ status: 'published' }} />
23
+ * <ToggleFilterButton label="Draft" value={{ status: 'draft' }} />
24
+ * </div>
25
+ * );
26
+ */
27
+ export const ToggleFilterButton = ({
28
+ label,
29
+ size = "sm",
30
+ value,
31
+ className,
32
+ }: {
33
+ label: React.ReactElement | string;
34
+ value: any;
35
+ className?: string;
36
+ size?: "default" | "sm" | "lg" | "icon" | null | undefined;
37
+ }) => {
38
+ const { filterValues, setFilters } = useListContext();
39
+ const translate = useTranslate();
40
+ const isSelected = getIsSelected(value, filterValues);
41
+ const handleClick = () => setFilters(toggleFilter(value, filterValues));
42
+ return (
43
+ <Button
44
+ variant={isSelected ? "secondary" : "ghost"}
45
+ onClick={handleClick}
46
+ className={cn(
47
+ "cursor-pointer",
48
+ "flex flex-row items-center justify-between gap-2 px-2.5 w-full",
49
+ className,
50
+ )}
51
+ size={size}
52
+ >
53
+ {typeof label === "string" ? translate(label, { _: label }) : label}
54
+ {isSelected && <CircleX className="opacity-50" />}
55
+ </Button>
56
+ );
57
+ };
58
+
59
+ const toggleFilter = (value: any, filters: any) => {
60
+ const isSelected = matches(
61
+ pickBy(value, (val) => typeof val !== "undefined"),
62
+ )(filters);
63
+
64
+ if (isSelected) {
65
+ const keysToRemove = Object.keys(value);
66
+ return Object.keys(filters).reduce(
67
+ (acc, key) =>
68
+ keysToRemove.includes(key) ? acc : { ...acc, [key]: filters[key] },
69
+ {},
70
+ );
71
+ }
72
+
73
+ return { ...filters, ...value };
74
+ };
75
+
76
+ const getIsSelected = (value: any, filters: any) =>
77
+ matches(pickBy(value, (val) => typeof val !== "undefined"))(filters);
@@ -0,0 +1,83 @@
1
+ import { useFieldValue, useTranslate } from "ra-core";
2
+ import type { AnchorHTMLAttributes } from "react";
3
+ import * as React from "react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+ import { genericMemo } from "@/lib/genericMemo";
7
+ import { FieldProps } from "@/lib/field.type";
8
+
9
+ /**
10
+ * Displays a URL as a clickable hyperlink.
11
+ *
12
+ * Click events are prevented from bubbling up, making it safe to use in DataTable rows with rowClick.
13
+ *
14
+ * @see {@link https://marmelab.com/shadcn-admin-kit/docs/urlfield/ UrlField documentation}
15
+ *
16
+ * @example
17
+ * import { List, DataTable, UrlField } from '@/components/admin';
18
+ *
19
+ * const WebsiteList = () => (
20
+ * <List>
21
+ * <DataTable>
22
+ * <DataTable.Col source="id" />
23
+ * <DataTable.Col source="website">
24
+ * <UrlField source="website" empty="No website available" />
25
+ * </DataTable.Col>
26
+ * </DataTable>
27
+ * </List>
28
+ * );
29
+ */
30
+ const UrlFieldImpl = <
31
+ //eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ RecordType extends Record<string, any> = Record<string, any>,
33
+ >(
34
+ inProps: UrlFieldProps<RecordType>,
35
+ ) => {
36
+ const {
37
+ empty,
38
+ className,
39
+ defaultValue,
40
+ source,
41
+ record,
42
+ resource: _,
43
+ ...rest
44
+ } = inProps;
45
+ const value = useFieldValue({ defaultValue, source, record });
46
+ const translate = useTranslate();
47
+
48
+ if (value == null) {
49
+ if (!empty) {
50
+ return null;
51
+ }
52
+
53
+ return (
54
+ <span className={className} {...rest}>
55
+ {typeof empty === "string" ? translate(empty, { _: empty }) : empty}
56
+ </span>
57
+ );
58
+ }
59
+
60
+ return (
61
+ <a
62
+ className={cn("underline hover:no-underline", className)}
63
+ href={value}
64
+ onClick={stopPropagation}
65
+ {...rest}
66
+ >
67
+ {value}
68
+ </a>
69
+ );
70
+ };
71
+ UrlFieldImpl.displayName = "UrlFieldImpl";
72
+
73
+ export const UrlField = genericMemo(UrlFieldImpl);
74
+
75
+ export interface UrlFieldProps<
76
+ //eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ RecordType extends Record<string, any> = Record<string, any>,
78
+ > extends FieldProps<RecordType>,
79
+ AnchorHTMLAttributes<HTMLAnchorElement> {}
80
+
81
+ // useful to prevent click bubbling in a DataTable with rowClick
82
+ const stopPropagation = (e: React.MouseEvent<HTMLAnchorElement>) =>
83
+ e.stopPropagation();