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.
- package/LICENSE.md +21 -0
- package/README.md +104 -0
- package/dist/assets/DealList-DqDrFeDV.js +59 -0
- package/dist/assets/DealList-DqDrFeDV.js.map +1 -0
- package/dist/assets/index-BiQoGq1P.css +1 -0
- package/dist/assets/index-CDIy4x-0.js +152 -0
- package/dist/assets/index-CDIy4x-0.js.map +1 -0
- package/dist/auth-callback.html +140 -0
- package/dist/favicon.ico +0 -0
- package/dist/img/adding-users.png +0 -0
- package/dist/img/empty.svg +42 -0
- package/dist/index.html +1 -0
- package/dist/logo192.png +0 -0
- package/dist/logo512.png +0 -0
- package/dist/logos/0.png +0 -0
- package/dist/logos/1.png +0 -0
- package/dist/logos/10.png +0 -0
- package/dist/logos/11.png +0 -0
- package/dist/logos/12.png +0 -0
- package/dist/logos/13.png +0 -0
- package/dist/logos/14.png +0 -0
- package/dist/logos/15.png +0 -0
- package/dist/logos/16.png +0 -0
- package/dist/logos/17.png +0 -0
- package/dist/logos/18.png +0 -0
- package/dist/logos/19.png +0 -0
- package/dist/logos/2.png +0 -0
- package/dist/logos/20.png +0 -0
- package/dist/logos/21.png +0 -0
- package/dist/logos/22.png +0 -0
- package/dist/logos/23.png +0 -0
- package/dist/logos/24.png +0 -0
- package/dist/logos/25.png +0 -0
- package/dist/logos/26.png +0 -0
- package/dist/logos/27.png +0 -0
- package/dist/logos/28.png +0 -0
- package/dist/logos/29.png +0 -0
- package/dist/logos/3.png +0 -0
- package/dist/logos/30.png +0 -0
- package/dist/logos/31.png +0 -0
- package/dist/logos/32.png +0 -0
- package/dist/logos/33.png +0 -0
- package/dist/logos/34.png +0 -0
- package/dist/logos/35.png +0 -0
- package/dist/logos/36.png +0 -0
- package/dist/logos/37.png +0 -0
- package/dist/logos/38.png +0 -0
- package/dist/logos/39.png +0 -0
- package/dist/logos/4.png +0 -0
- package/dist/logos/40.png +0 -0
- package/dist/logos/41.png +0 -0
- package/dist/logos/42.png +0 -0
- package/dist/logos/43.png +0 -0
- package/dist/logos/44.png +0 -0
- package/dist/logos/45.png +0 -0
- package/dist/logos/46.png +0 -0
- package/dist/logos/47.png +0 -0
- package/dist/logos/48.png +0 -0
- package/dist/logos/49.png +0 -0
- package/dist/logos/5.png +0 -0
- package/dist/logos/50.png +0 -0
- package/dist/logos/51.png +0 -0
- package/dist/logos/52.png +0 -0
- package/dist/logos/53.png +0 -0
- package/dist/logos/54.png +0 -0
- package/dist/logos/55.png +0 -0
- package/dist/logos/6.png +0 -0
- package/dist/logos/7.png +0 -0
- package/dist/logos/8.png +0 -0
- package/dist/logos/9.png +0 -0
- package/dist/logos/Readme.md +1 -0
- package/dist/logos/logo_atomic_crm.svg +14 -0
- package/dist/logos/logo_atomic_crm_dark.svg +14 -0
- package/dist/logos/logo_atomic_crm_light.svg +14 -0
- package/dist/manifest.json +25 -0
- package/dist/robots.txt +3 -0
- package/dist/stats.html +4949 -0
- package/package.json +152 -0
- package/public/auth-callback.html +140 -0
- package/public/favicon.ico +0 -0
- package/public/img/adding-users.png +0 -0
- package/public/img/empty.svg +42 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/logos/0.png +0 -0
- package/public/logos/1.png +0 -0
- package/public/logos/10.png +0 -0
- package/public/logos/11.png +0 -0
- package/public/logos/12.png +0 -0
- package/public/logos/13.png +0 -0
- package/public/logos/14.png +0 -0
- package/public/logos/15.png +0 -0
- package/public/logos/16.png +0 -0
- package/public/logos/17.png +0 -0
- package/public/logos/18.png +0 -0
- package/public/logos/19.png +0 -0
- package/public/logos/2.png +0 -0
- package/public/logos/20.png +0 -0
- package/public/logos/21.png +0 -0
- package/public/logos/22.png +0 -0
- package/public/logos/23.png +0 -0
- package/public/logos/24.png +0 -0
- package/public/logos/25.png +0 -0
- package/public/logos/26.png +0 -0
- package/public/logos/27.png +0 -0
- package/public/logos/28.png +0 -0
- package/public/logos/29.png +0 -0
- package/public/logos/3.png +0 -0
- package/public/logos/30.png +0 -0
- package/public/logos/31.png +0 -0
- package/public/logos/32.png +0 -0
- package/public/logos/33.png +0 -0
- package/public/logos/34.png +0 -0
- package/public/logos/35.png +0 -0
- package/public/logos/36.png +0 -0
- package/public/logos/37.png +0 -0
- package/public/logos/38.png +0 -0
- package/public/logos/39.png +0 -0
- package/public/logos/4.png +0 -0
- package/public/logos/40.png +0 -0
- package/public/logos/41.png +0 -0
- package/public/logos/42.png +0 -0
- package/public/logos/43.png +0 -0
- package/public/logos/44.png +0 -0
- package/public/logos/45.png +0 -0
- package/public/logos/46.png +0 -0
- package/public/logos/47.png +0 -0
- package/public/logos/48.png +0 -0
- package/public/logos/49.png +0 -0
- package/public/logos/5.png +0 -0
- package/public/logos/50.png +0 -0
- package/public/logos/51.png +0 -0
- package/public/logos/52.png +0 -0
- package/public/logos/53.png +0 -0
- package/public/logos/54.png +0 -0
- package/public/logos/55.png +0 -0
- package/public/logos/6.png +0 -0
- package/public/logos/7.png +0 -0
- package/public/logos/8.png +0 -0
- package/public/logos/9.png +0 -0
- package/public/logos/Readme.md +1 -0
- package/public/logos/logo_atomic_crm.svg +14 -0
- package/public/logos/logo_atomic_crm_dark.svg +14 -0
- package/public/logos/logo_atomic_crm_light.svg +14 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/src/App.css +42 -0
- package/src/App.tsx +58 -0
- package/src/assets/react.svg +1 -0
- package/src/components/admin/Readme.md +40 -0
- package/src/components/admin/admin.tsx +132 -0
- package/src/components/admin/app-sidebar.tsx +166 -0
- package/src/components/admin/array-field.tsx +59 -0
- package/src/components/admin/array-input.tsx +201 -0
- package/src/components/admin/authentication.tsx +86 -0
- package/src/components/admin/autocomplete-array-input.tsx +254 -0
- package/src/components/admin/autocomplete-input.tsx +296 -0
- package/src/components/admin/badge-field.tsx +65 -0
- package/src/components/admin/boolean-input.tsx +116 -0
- package/src/components/admin/breadcrumb.tsx +135 -0
- package/src/components/admin/bulk-actions-toolbar.tsx +83 -0
- package/src/components/admin/bulk-delete-button.tsx +70 -0
- package/src/components/admin/bulk-export-button.tsx +76 -0
- package/src/components/admin/cancel-button.tsx +46 -0
- package/src/components/admin/columns-button.tsx +345 -0
- package/src/components/admin/confirm.tsx +166 -0
- package/src/components/admin/count.tsx +94 -0
- package/src/components/admin/create-button.tsx +58 -0
- package/src/components/admin/create.tsx +132 -0
- package/src/components/admin/data-table.tsx +520 -0
- package/src/components/admin/date-field.tsx +136 -0
- package/src/components/admin/date-input.tsx +317 -0
- package/src/components/admin/date-time-input.tsx +331 -0
- package/src/components/admin/delete-button.tsx +113 -0
- package/src/components/admin/edit-button.tsx +64 -0
- package/src/components/admin/edit-guesser.tsx +157 -0
- package/src/components/admin/edit.tsx +152 -0
- package/src/components/admin/email-field.tsx +74 -0
- package/src/components/admin/error.tsx +111 -0
- package/src/components/admin/export-button.tsx +126 -0
- package/src/components/admin/field-toggle.tsx +164 -0
- package/src/components/admin/file-field.tsx +123 -0
- package/src/components/admin/file-input.tsx +361 -0
- package/src/components/admin/filter-form.tsx +510 -0
- package/src/components/admin/form.tsx +312 -0
- package/src/components/admin/icon-button-with-tooltip.tsx +85 -0
- package/src/components/admin/index.ts +73 -0
- package/src/components/admin/input-helper-text.tsx +29 -0
- package/src/components/admin/layout.tsx +69 -0
- package/src/components/admin/list-guesser.tsx +239 -0
- package/src/components/admin/list-pagination.tsx +247 -0
- package/src/components/admin/list.tsx +178 -0
- package/src/components/admin/loading.tsx +40 -0
- package/src/components/admin/locales-menu-button.tsx +60 -0
- package/src/components/admin/login-page.tsx +104 -0
- package/src/components/admin/notification.tsx +114 -0
- package/src/components/admin/number-field.tsx +84 -0
- package/src/components/admin/number-input.tsx +124 -0
- package/src/components/admin/radio-button-group-input.tsx +184 -0
- package/src/components/admin/ready.tsx +55 -0
- package/src/components/admin/record-field.tsx +132 -0
- package/src/components/admin/reference-array-field.tsx +152 -0
- package/src/components/admin/reference-array-input.tsx +68 -0
- package/src/components/admin/reference-field.tsx +153 -0
- package/src/components/admin/reference-input.tsx +46 -0
- package/src/components/admin/reference-many-count.tsx +92 -0
- package/src/components/admin/reference-many-field.tsx +132 -0
- package/src/components/admin/refresh-button.tsx +31 -0
- package/src/components/admin/saved-queries.tsx +174 -0
- package/src/components/admin/search-input.tsx +57 -0
- package/src/components/admin/select-field.tsx +111 -0
- package/src/components/admin/select-input.tsx +323 -0
- package/src/components/admin/show-button.tsx +57 -0
- package/src/components/admin/show-guesser.tsx +215 -0
- package/src/components/admin/show.tsx +184 -0
- package/src/components/admin/simple-form-iterator.tsx +582 -0
- package/src/components/admin/simple-form.tsx +95 -0
- package/src/components/admin/simple-show-layout.tsx +8 -0
- package/src/components/admin/single-field-list.tsx +67 -0
- package/src/components/admin/sort-button.tsx +152 -0
- package/src/components/admin/spinner.tsx +46 -0
- package/src/components/admin/text-field.tsx +60 -0
- package/src/components/admin/text-input.tsx +77 -0
- package/src/components/admin/theme-mode-toggle.tsx +48 -0
- package/src/components/admin/theme-provider.tsx +74 -0
- package/src/components/admin/toggle-filter-button.tsx +77 -0
- package/src/components/admin/url-field.tsx +83 -0
- package/src/components/admin/user-menu.tsx +84 -0
- package/src/components/atomic-crm/activity/ActivityLog.tsx +54 -0
- package/src/components/atomic-crm/activity/ActivityLogCompanyCreated.tsx +50 -0
- package/src/components/atomic-crm/activity/ActivityLogContactCreated.tsx +42 -0
- package/src/components/atomic-crm/activity/ActivityLogContactNoteCreated.tsx +71 -0
- package/src/components/atomic-crm/activity/ActivityLogContext.tsx +11 -0
- package/src/components/atomic-crm/activity/ActivityLogDealCreated.tsx +41 -0
- package/src/components/atomic-crm/activity/ActivityLogDealNoteCreated.tsx +84 -0
- package/src/components/atomic-crm/activity/ActivityLogIterator.tsx +80 -0
- package/src/components/atomic-crm/activity/ActivityLogNote.tsx +36 -0
- package/src/components/atomic-crm/companies/AutocompleteCompanyInput.tsx +43 -0
- package/src/components/atomic-crm/companies/CompanyAside.tsx +207 -0
- package/src/components/atomic-crm/companies/CompanyAvatar.tsx +29 -0
- package/src/components/atomic-crm/companies/CompanyCard.tsx +88 -0
- package/src/components/atomic-crm/companies/CompanyCreate.tsx +41 -0
- package/src/components/atomic-crm/companies/CompanyEdit.tsx +33 -0
- package/src/components/atomic-crm/companies/CompanyEmpty.tsx +26 -0
- package/src/components/atomic-crm/companies/CompanyInputs.tsx +160 -0
- package/src/components/atomic-crm/companies/CompanyList.tsx +54 -0
- package/src/components/atomic-crm/companies/CompanyListFilter.tsx +55 -0
- package/src/components/atomic-crm/companies/CompanyShow.tsx +241 -0
- package/src/components/atomic-crm/companies/GridList.tsx +46 -0
- package/src/components/atomic-crm/companies/index.ts +11 -0
- package/src/components/atomic-crm/companies/sizes.ts +7 -0
- package/src/components/atomic-crm/consts.ts +5 -0
- package/src/components/atomic-crm/contacts/Avatar.tsx +40 -0
- package/src/components/atomic-crm/contacts/ContactAside.tsx +187 -0
- package/src/components/atomic-crm/contacts/ContactCreate.tsx +34 -0
- package/src/components/atomic-crm/contacts/ContactEdit.tsx +32 -0
- package/src/components/atomic-crm/contacts/ContactEmpty.tsx +28 -0
- package/src/components/atomic-crm/contacts/ContactImportButton.tsx +213 -0
- package/src/components/atomic-crm/contacts/ContactInputs.tsx +209 -0
- package/src/components/atomic-crm/contacts/ContactList.tsx +116 -0
- package/src/components/atomic-crm/contacts/ContactListContent.tsx +107 -0
- package/src/components/atomic-crm/contacts/ContactListFilter.tsx +126 -0
- package/src/components/atomic-crm/contacts/ContactMergeButton.tsx +263 -0
- package/src/components/atomic-crm/contacts/ContactShow.tsx +76 -0
- package/src/components/atomic-crm/contacts/ExportVCardButton.tsx +79 -0
- package/src/components/atomic-crm/contacts/TagsList.tsx +33 -0
- package/src/components/atomic-crm/contacts/TagsListEdit.tsx +155 -0
- package/src/components/atomic-crm/contacts/contacts_export.csv +3 -0
- package/src/components/atomic-crm/contacts/exportToVCard.ts +104 -0
- package/src/components/atomic-crm/contacts/index.tsx +14 -0
- package/src/components/atomic-crm/contacts/useContactImport.tsx +206 -0
- package/src/components/atomic-crm/dashboard/Dashboard.tsx +66 -0
- package/src/components/atomic-crm/dashboard/DashboardActivityLog.tsx +22 -0
- package/src/components/atomic-crm/dashboard/DashboardStepper.tsx +72 -0
- package/src/components/atomic-crm/dashboard/DealsChart.tsx +202 -0
- package/src/components/atomic-crm/dashboard/DealsPipeline.tsx +90 -0
- package/src/components/atomic-crm/dashboard/HotContacts.tsx +92 -0
- package/src/components/atomic-crm/dashboard/LatestNotes.tsx +116 -0
- package/src/components/atomic-crm/dashboard/TasksList.tsx +69 -0
- package/src/components/atomic-crm/dashboard/TasksListEmpty.tsx +22 -0
- package/src/components/atomic-crm/dashboard/TasksListFilter.tsx +72 -0
- package/src/components/atomic-crm/dashboard/Welcome.tsx +41 -0
- package/src/components/atomic-crm/deals/ContactList.tsx +31 -0
- package/src/components/atomic-crm/deals/DealArchivedList.tsx +105 -0
- package/src/components/atomic-crm/deals/DealCard.tsx +78 -0
- package/src/components/atomic-crm/deals/DealColumn.tsx +52 -0
- package/src/components/atomic-crm/deals/DealCreate.tsx +95 -0
- package/src/components/atomic-crm/deals/DealEdit.tsx +81 -0
- package/src/components/atomic-crm/deals/DealEmpty.tsx +63 -0
- package/src/components/atomic-crm/deals/DealInputs.tsx +103 -0
- package/src/components/atomic-crm/deals/DealList.tsx +95 -0
- package/src/components/atomic-crm/deals/DealListContent.tsx +245 -0
- package/src/components/atomic-crm/deals/DealShow.tsx +260 -0
- package/src/components/atomic-crm/deals/OnlyMineInput.tsx +30 -0
- package/src/components/atomic-crm/deals/deal.ts +5 -0
- package/src/components/atomic-crm/deals/dealUtils.ts +26 -0
- package/src/components/atomic-crm/deals/index.ts +6 -0
- package/src/components/atomic-crm/deals/stages.ts +28 -0
- package/src/components/atomic-crm/filters/FilterCategory.tsx +20 -0
- package/src/components/atomic-crm/layout/FormToolbar.tsx +12 -0
- package/src/components/atomic-crm/layout/Header.tsx +134 -0
- package/src/components/atomic-crm/layout/Layout.tsx +21 -0
- package/src/components/atomic-crm/layout/TopToolbar.tsx +24 -0
- package/src/components/atomic-crm/login/LoginSkeleton.tsx +18 -0
- package/src/components/atomic-crm/login/SignupPage.tsx +150 -0
- package/src/components/atomic-crm/login/StartPage.tsx +27 -0
- package/src/components/atomic-crm/misc/AsideSection.tsx +21 -0
- package/src/components/atomic-crm/misc/ContactOption.tsx +26 -0
- package/src/components/atomic-crm/misc/ImageEditorField.tsx +206 -0
- package/src/components/atomic-crm/misc/RelativeDate.tsx +5 -0
- package/src/components/atomic-crm/misc/Status.tsx +28 -0
- package/src/components/atomic-crm/misc/fetchWithTimeout.ts +19 -0
- package/src/components/atomic-crm/misc/isLinkedInUrl.ts +15 -0
- package/src/components/atomic-crm/misc/unsupportedDomains.const.ts +105 -0
- package/src/components/atomic-crm/misc/useAppBarHeight.ts +9 -0
- package/src/components/atomic-crm/misc/usePapaParse.tsx +144 -0
- package/src/components/atomic-crm/notes/Note.tsx +187 -0
- package/src/components/atomic-crm/notes/NoteAttachments.tsx +56 -0
- package/src/components/atomic-crm/notes/NoteCreate.tsx +112 -0
- package/src/components/atomic-crm/notes/NoteInputs.tsx +92 -0
- package/src/components/atomic-crm/notes/NotesIterator.tsx +37 -0
- package/src/components/atomic-crm/notes/StatusSelector.tsx +39 -0
- package/src/components/atomic-crm/notes/index.ts +3 -0
- package/src/components/atomic-crm/notes/utils.ts +13 -0
- package/src/components/atomic-crm/providers/commons/activity.ts +174 -0
- package/src/components/atomic-crm/providers/commons/canAccess.ts +26 -0
- package/src/components/atomic-crm/providers/commons/getCompanyAvatar.spec.ts +20 -0
- package/src/components/atomic-crm/providers/commons/getCompanyAvatar.ts +21 -0
- package/src/components/atomic-crm/providers/commons/getContactAvatar.spec.ts +80 -0
- package/src/components/atomic-crm/providers/commons/getContactAvatar.ts +70 -0
- package/src/components/atomic-crm/providers/commons/mergeContacts.ts +185 -0
- package/src/components/atomic-crm/providers/fakerest/authProvider.ts +74 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/companies.ts +53 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/contactNotes.ts +25 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/contacts.ts +103 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/dealNotes.ts +19 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/deals.ts +53 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/finalize.ts +10 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/index.ts +25 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/sales.ts +37 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/tags.ts +14 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/tasks.ts +55 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/types.ts +21 -0
- package/src/components/atomic-crm/providers/fakerest/dataGenerator/utils.ts +28 -0
- package/src/components/atomic-crm/providers/fakerest/dataProvider.ts +518 -0
- package/src/components/atomic-crm/providers/fakerest/index.ts +2 -0
- package/src/components/atomic-crm/providers/fakerest/internal/listParser.ts +48 -0
- package/src/components/atomic-crm/providers/fakerest/internal/supabaseAdapter.spec.ts +721 -0
- package/src/components/atomic-crm/providers/fakerest/internal/supabaseAdapter.ts +49 -0
- package/src/components/atomic-crm/providers/fakerest/internal/transformContainsFilter.spec.ts +35 -0
- package/src/components/atomic-crm/providers/fakerest/internal/transformContainsFilter.ts +17 -0
- package/src/components/atomic-crm/providers/fakerest/internal/transformFilter.ts +57 -0
- package/src/components/atomic-crm/providers/fakerest/internal/transformInFilter.spec.ts +32 -0
- package/src/components/atomic-crm/providers/fakerest/internal/transformInFilter.ts +17 -0
- package/src/components/atomic-crm/providers/fakerest/internal/transformOrFilter.spec.ts +23 -0
- package/src/components/atomic-crm/providers/fakerest/internal/transformOrFilter.ts +17 -0
- package/src/components/atomic-crm/providers/supabase/authProvider.ts +121 -0
- package/src/components/atomic-crm/providers/supabase/dataProvider.ts +407 -0
- package/src/components/atomic-crm/providers/supabase/index.ts +2 -0
- package/src/components/atomic-crm/providers/supabase/supabase.ts +34 -0
- package/src/components/atomic-crm/providers/types.ts +1 -0
- package/src/components/atomic-crm/root/CRM.tsx +167 -0
- package/src/components/atomic-crm/root/ConfigurationContext.tsx +80 -0
- package/src/components/atomic-crm/root/defaultConfiguration.ts +64 -0
- package/src/components/atomic-crm/root/i18nProvider.tsx +25 -0
- package/src/components/atomic-crm/sales/SaleName.tsx +13 -0
- package/src/components/atomic-crm/sales/SalesCreate.tsx +51 -0
- package/src/components/atomic-crm/sales/SalesEdit.tsx +82 -0
- package/src/components/atomic-crm/sales/SalesInputs.tsx +31 -0
- package/src/components/atomic-crm/sales/SalesList.tsx +62 -0
- package/src/components/atomic-crm/sales/index.ts +12 -0
- package/src/components/atomic-crm/settings/DatabaseSettings.tsx +169 -0
- package/src/components/atomic-crm/settings/SettingsPage.tsx +259 -0
- package/src/components/atomic-crm/setup/SupabaseSetupWizard.tsx +215 -0
- package/src/components/atomic-crm/simple-list/ListNoResults.tsx +53 -0
- package/src/components/atomic-crm/simple-list/ListPlaceholder.tsx +9 -0
- package/src/components/atomic-crm/simple-list/SimpleList.tsx +245 -0
- package/src/components/atomic-crm/simple-list/SimpleListItem.tsx +138 -0
- package/src/components/atomic-crm/simple-list/SimpleListLoading.tsx +60 -0
- package/src/components/atomic-crm/tags/RoundButton.tsx +10 -0
- package/src/components/atomic-crm/tags/TagChip.tsx +45 -0
- package/src/components/atomic-crm/tags/TagCreateModal.tsx +39 -0
- package/src/components/atomic-crm/tags/TagDialog.tsx +118 -0
- package/src/components/atomic-crm/tags/TagEditModal.tsx +42 -0
- package/src/components/atomic-crm/tags/colors.ts +12 -0
- package/src/components/atomic-crm/tasks/AddTask.tsx +191 -0
- package/src/components/atomic-crm/tasks/Task.tsx +184 -0
- package/src/components/atomic-crm/tasks/TaskEdit.tsx +96 -0
- package/src/components/atomic-crm/tasks/TasksIterator.tsx +30 -0
- package/src/components/atomic-crm/types.ts +226 -0
- package/src/components/supabase/forgot-password-page.tsx +86 -0
- package/src/components/supabase/layout.tsx +27 -0
- package/src/components/supabase/set-password-page.tsx +119 -0
- package/src/components/ui/README.md +34 -0
- package/src/components/ui/accordion.tsx +64 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/avatar.tsx +99 -0
- package/src/components/ui/badge.tsx +46 -0
- package/src/components/ui/breadcrumb.tsx +109 -0
- package/src/components/ui/button.tsx +59 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/command.tsx +175 -0
- package/src/components/ui/dialog.tsx +133 -0
- package/src/components/ui/drawer.tsx +133 -0
- package/src/components/ui/dropdown-menu.tsx +255 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/pagination.tsx +127 -0
- package/src/components/ui/popover.tsx +46 -0
- package/src/components/ui/progress.tsx +29 -0
- package/src/components/ui/radio-group.tsx +43 -0
- package/src/components/ui/select.tsx +183 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/sheet.tsx +137 -0
- package/src/components/ui/sidebar.tsx +724 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +38 -0
- package/src/components/ui/spinner.tsx +51 -0
- package/src/components/ui/switch.tsx +29 -0
- package/src/components/ui/table.tsx +114 -0
- package/src/components/ui/tabs.tsx +64 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/hooks/saved-queries.tsx +67 -0
- package/src/hooks/simple-form-iterator-context.tsx +70 -0
- package/src/hooks/use-mobile.ts +21 -0
- package/src/hooks/useBulkExport.tsx +61 -0
- package/src/hooks/useSupportCreateSuggestion.tsx +188 -0
- package/src/hooks/user-menu-context.tsx +24 -0
- package/src/index.css +170 -0
- package/src/lib/field.type.ts +22 -0
- package/src/lib/genericMemo.ts +18 -0
- package/src/lib/i18nProvider.ts +9 -0
- package/src/lib/sanitizeInputRestProps.ts +46 -0
- package/src/lib/supabase-config.ts +123 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +10 -0
- package/src/setupTests.js +5 -0
- package/src/vite-env.d.ts +1 -0
- package/supabase/config.toml +157 -0
- package/supabase/functions/.env.development +7 -0
- package/supabase/functions/_shared/db.ts +187 -0
- package/supabase/functions/_shared/supabaseAdmin.ts +13 -0
- package/supabase/functions/_shared/utils.ts +13 -0
- package/supabase/functions/mergeContacts/index.ts +215 -0
- package/supabase/functions/postmark/addNoteToContact.ts +129 -0
- package/supabase/functions/postmark/extractMailContactData.ts +41 -0
- package/supabase/functions/postmark/getExpectedAuthorization.ts +4 -0
- package/supabase/functions/postmark/getNoteContent.ts +6 -0
- package/supabase/functions/postmark/index.ts +210 -0
- package/supabase/functions/updatePassword/index.ts +50 -0
- package/supabase/functions/users/index.ts +206 -0
- package/supabase/migrations/20240730075029_init_db.sql +600 -0
- package/supabase/migrations/20240730075425_init_triggers.sql +57 -0
- package/supabase/migrations/20240806124555_task_sales_id.sql +1 -0
- package/supabase/migrations/20240807082449_remove-aquisition.sql +20 -0
- package/supabase/migrations/20240808141826_init_state_configure.sql +9 -0
- package/supabase/migrations/20240813084010_tags_policy.sql +18 -0
- package/supabase/migrations/20241104153231_sales_policies.sql +7 -0
- package/supabase/migrations/20250109152531_email_jsonb.sql +43 -0
- package/supabase/migrations/20250113132531_phone_jsonb.sql +67 -0
- package/supabase/migrations/20251204172855_merge_contacts_function.sql +153 -0
- package/supabase/migrations/20251204201317_drop_merge_contacts_function.sql +2 -0
- package/supabase/seed.sql +0 -0
- package/supabase/templates/invite.html +70 -0
- 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 />;
|