@salesforce/ui-bundle-template-app-angular-template-b2x 11.30.0
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.txt +82 -0
- package/dist/.forceignore +15 -0
- package/dist/.husky/pre-commit +4 -0
- package/dist/.prettierignore +11 -0
- package/dist/.prettierrc +17 -0
- package/dist/CHANGELOG.md +3966 -0
- package/dist/README.md +77 -0
- package/dist/config/project-scratch-def.json +13 -0
- package/dist/eslint.config.js +7 -0
- package/dist/force-app/main/default/classes/UIBundleAuthUtils.cls +68 -0
- package/dist/force-app/main/default/classes/UIBundleAuthUtils.cls-meta.xml +5 -0
- package/dist/force-app/main/default/classes/UIBundleChangePassword.cls +77 -0
- package/dist/force-app/main/default/classes/UIBundleChangePassword.cls-meta.xml +5 -0
- package/dist/force-app/main/default/classes/UIBundleForgotPassword.cls +71 -0
- package/dist/force-app/main/default/classes/UIBundleForgotPassword.cls-meta.xml +5 -0
- package/dist/force-app/main/default/classes/UIBundleLogin.cls +105 -0
- package/dist/force-app/main/default/classes/UIBundleLogin.cls-meta.xml +5 -0
- package/dist/force-app/main/default/classes/UIBundleRegistration.cls +162 -0
- package/dist/force-app/main/default/classes/UIBundleRegistration.cls-meta.xml +5 -0
- package/dist/force-app/main/default/digitalExperienceConfigs/angularexternalapp1.digitalExperienceConfig-meta.xml +8 -0
- package/dist/force-app/main/default/digitalExperiences/site/angularexternalapp1/angularexternalapp1.digitalExperience-meta.xml +11 -0
- package/dist/force-app/main/default/digitalExperiences/site/angularexternalapp1/sfdc_cms__site/angularexternalapp1/_meta.json +5 -0
- package/dist/force-app/main/default/digitalExperiences/site/angularexternalapp1/sfdc_cms__site/angularexternalapp1/content.json +10 -0
- package/dist/force-app/main/default/networks/angularexternalapp.network-meta.xml +60 -0
- package/dist/force-app/main/default/package.xml +24 -0
- package/dist/force-app/main/default/permissionsets/angularexternalapp_Guest_User_Api_Access.permissionset-meta.xml +9 -0
- package/dist/force-app/main/default/sites/angularexternalapp.site-meta.xml +31 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/.forceignore +15 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/.postcssrc.json +5 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/.prettierignore +8 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/.prettierrc +12 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/CHANGELOG.md +4 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/README.md +85 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/angular.json +98 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/angularexternalapp.uibundle-meta.xml +8 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/e2e/app.spec.ts +15 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/esbuild/api-version.mjs +17 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/eslint.config.js +60 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/middleware/html.mjs +3 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/middleware/proxy.mjs +12 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/package.json +56 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/playwright.config.ts +25 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/public/favicon.ico +0 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/scripts/rewrite-e2e-assets.mjs +30 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/account/account-search.service.ts +196 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/account/distinctAccountIndustries.ts +26 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/account/distinctAccountTypes.ts +26 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/account/getAccountDetail.ts +128 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/account/searchAccounts.ts +58 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/data-client.service.spec.ts +184 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/data-client.service.ts +74 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/user-profile.service.ts +126 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/app.config.ts +22 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/app.html +1 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/app.routes.ts +77 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/app.spec.ts +18 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/app.ts +15 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/layout/app-layout/app-layout.component.html +53 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/layout/app-layout/app-layout.component.spec.ts +89 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/layout/app-layout/app-layout.component.ts +81 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/_field-size.scss +42 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/alert/alert.component.html +31 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/alert/alert.component.ts +53 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/button/button.component.html +18 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/button/button.component.scss +112 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/button/button.component.ts +57 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/card/card.component.scss +34 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/card/card.component.ts +110 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/collapsible/collapsible.component.html +10 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/collapsible/collapsible.component.ts +26 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-picker/date-picker.component.html +25 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-picker/date-picker.component.scss +36 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-picker/date-picker.component.ts +41 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-range-picker/date-range-picker.component.html +34 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-range-picker/date-range-picker.component.scss +33 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-range-picker/date-range-picker.component.ts +66 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/dialog/dialog.component.html +37 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/dialog/dialog.component.ts +55 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/field/field.component.html +21 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/field/field.component.ts +36 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/field-size.ts +8 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/input/input.component.html +19 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/input/input.component.scss +35 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/input/input.component.ts +36 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/label/label.component.html +3 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/label/label.component.ts +17 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/paginator/paginator.component.html +10 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/paginator/paginator.component.ts +37 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/popover/popover.component.html +41 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/popover/popover.component.ts +40 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/select/select.component.html +24 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/select/select.component.scss +35 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/select/select.component.ts +72 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/separator/separator.component.html +1 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/separator/separator.component.scss +23 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/separator/separator.component.ts +56 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/skeleton/skeleton.component.html +1 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/skeleton/skeleton.component.scss +28 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/skeleton/skeleton.component.ts +24 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/spinner/spinner.component.ts +35 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/auth/auth.guard.ts +50 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/auth/auth.service.ts +126 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/components/status-alert/status-alert.component.html +7 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/components/status-alert/status-alert.component.ts +52 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/config/authentication.config.ts +75 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/auth-form/auth-form.component.html +38 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/auth-form/auth-form.component.ts +94 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/footer-link/footer-link.component.html +11 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/footer-link/footer-link.component.ts +26 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/reactive-form-page.base.ts +50 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/submit-button/submit-button.component.html +8 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/submit-button/submit-button.component.scss +17 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/submit-button/submit-button.component.ts +35 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/layouts/auth-app-layout/auth-app-layout.component.html +2 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/layouts/auth-app-layout/auth-app-layout.component.ts +30 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/layouts/card-layout/card-layout.component.html +17 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/layouts/card-layout/card-layout.component.ts +40 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/menu/auth-menu/auth-menu.component.html +26 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/menu/auth-menu/auth-menu.component.ts +46 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/session-timeout/session-time-servlet.ts +130 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/session-timeout/session-timeout-validator/session-timeout-validator.component.html +57 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/session-timeout/session-timeout-validator/session-timeout-validator.component.ts +205 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/session-timeout/session-timeout.config.ts +83 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/session-timeout/session-timeout.service.ts +201 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/utils/auth-helpers.ts +68 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/utils/auth-validators.ts +72 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/utils/helpers.ts +85 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/active-filters/active-filters.component.html +23 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/active-filters/active-filters.component.ts +78 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/date-filter/date-filter.component.html +18 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/date-filter/date-filter.component.ts +137 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/date-range-filter/date-range-filter.component.html +11 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/date-range-filter/date-range-filter.component.ts +69 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/filter-field-wrapper/filter-field-wrapper.component.html +11 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/filter-field-wrapper/filter-field-wrapper.component.ts +36 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/multi-select-filter/multi-select-filter.component.html +10 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/multi-select-filter/multi-select-filter.component.ts +58 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/numeric-range-filter/numeric-range-filter.component.html +22 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/numeric-range-filter/numeric-range-filter.component.ts +146 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/search-filter/search-filter.component.html +8 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/search-filter/search-filter.component.ts +82 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/select-filter/select-filter.component.html +12 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/select-filter/select-filter.component.ts +65 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/text-filter/text-filter.component.html +12 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/text-filter/text-filter.component.ts +90 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/object-breadcrumb/object-breadcrumb.component.html +55 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/object-breadcrumb/object-breadcrumb.component.ts +40 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/pagination-controls/pagination-controls.component.html +8 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/pagination-controls/pagination-controls.component.ts +61 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/search-bar/search-bar.component.html +59 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/search-bar/search-bar.component.ts +108 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/sort-control/sort-control.component.html +50 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/sort-control/sort-control.component.ts +72 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/object-search-state.service.ts +232 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/utils/debounce.ts +31 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/utils/field-utils.ts +54 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/utils/filter-utils.ts +354 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/utils/index.ts +12 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/utils/sort-utils.ts +46 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/account-detail/account-detail-page.component.html +174 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/account-detail/account-detail-page.component.scss +7 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/account-detail/account-detail-page.component.ts +199 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/account-search/account-search-page.component.html +111 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/account-search/account-search-page.component.ts +217 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/change-password/change-password-page.component.html +68 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/change-password/change-password-page.component.ts +82 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/forgot-password/forgot-password-page.component.html +35 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/forgot-password/forgot-password-page.component.ts +87 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/home/home-page.component.html +18 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/home/home-page.component.ts +47 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/home/home.component.html +6 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/home/home.component.spec.ts +23 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/home/home.component.ts +13 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/login/login-page.component.html +47 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/login/login-page.component.ts +89 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/not-found/not-found.component.html +12 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/not-found/not-found.component.spec.ts +33 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/not-found/not-found.component.ts +15 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/profile/profile-page.component.html +126 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/profile/profile-page.component.ts +194 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/register/register-page.component.html +80 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/register/register-page.component.ts +93 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/reset-password/reset-password-page.component.html +60 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/reset-password/reset-password-page.component.ts +96 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/utils/async-data.ts +99 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/index.html +14 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/main.ts +5 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/styles.scss +170 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/theme.scss +39 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/src/types/sf-globals.d.ts +9 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/tsconfig.app.json +11 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/tsconfig.json +33 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/tsconfig.spec.json +10 -0
- package/dist/force-app/main/default/uiBundles/angularexternalapp/ui-bundle.json +7 -0
- package/dist/jest.config.js +6 -0
- package/dist/package-lock.json +10066 -0
- package/dist/package.json +46 -0
- package/dist/scripts/apex/hello.apex +10 -0
- package/dist/scripts/gitignore-templates.json +4 -0
- package/dist/scripts/graphql-search.sh +191 -0
- package/dist/scripts/org-setup-config-schema.mjs +103 -0
- package/dist/scripts/org-setup-dev.mjs +87 -0
- package/dist/scripts/org-setup-utils.mjs +369 -0
- package/dist/scripts/org-setup-xml.mjs +182 -0
- package/dist/scripts/org-setup.config.json +9 -0
- package/dist/scripts/org-setup.mjs +1702 -0
- package/dist/scripts/sf-project-setup.mjs +103 -0
- package/dist/scripts/soql/account.soql +6 -0
- package/dist/scripts/validate-org-setup-config.mjs +38 -0
- package/dist/sfdx-project.json +12 -0
- package/package.json +40 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { ChangeDetectionStrategy, Component, inject, signal } from "@angular/core";
|
|
7
|
+
import { FormBuilder } from "@angular/forms";
|
|
8
|
+
import { ActivatedRoute, Router, RouterLink } from "@angular/router";
|
|
9
|
+
import { DataClient } from "../../api/data-client.service";
|
|
10
|
+
import { AuthFormComponent } from "../../features/authentication/forms/auth-form/auth-form.component";
|
|
11
|
+
import { ReactiveFormPageBase } from "../../features/authentication/forms/reactive-form-page.base";
|
|
12
|
+
import { FieldComponent } from "../../components/ui/field/field.component";
|
|
13
|
+
import { InputComponent } from "../../components/ui/input/input.component";
|
|
14
|
+
import {
|
|
15
|
+
API_ROUTES,
|
|
16
|
+
AUTH_PLACEHOLDERS,
|
|
17
|
+
ROUTES,
|
|
18
|
+
} from "../../features/authentication/config/authentication.config";
|
|
19
|
+
import { getStartUrl, type AuthResponse } from "../../features/authentication/utils/auth-helpers";
|
|
20
|
+
import { ApiError, handleApiResponse } from "../../features/authentication/utils/helpers";
|
|
21
|
+
import {
|
|
22
|
+
emailValidator,
|
|
23
|
+
requiredWithMessage,
|
|
24
|
+
} from "../../features/authentication/utils/auth-validators";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Login page — Angular port of the React `Login`. Reactive Forms replace
|
|
28
|
+
* `@tanstack/react-form` + zod; the login POST + redirect logic is identical.
|
|
29
|
+
* On success with a `redirectUrl` it hard-navigates (establishes the server
|
|
30
|
+
* session cookie); otherwise it client-navigates to `/`.
|
|
31
|
+
*/
|
|
32
|
+
@Component({
|
|
33
|
+
selector: "app-login-page",
|
|
34
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
35
|
+
imports: [RouterLink, AuthFormComponent, FieldComponent, InputComponent],
|
|
36
|
+
templateUrl: "./login-page.component.html",
|
|
37
|
+
})
|
|
38
|
+
export class LoginPageComponent extends ReactiveFormPageBase {
|
|
39
|
+
private readonly fb = inject(FormBuilder);
|
|
40
|
+
private readonly router = inject(Router);
|
|
41
|
+
private readonly route = inject(ActivatedRoute);
|
|
42
|
+
private readonly dataClient = inject(DataClient);
|
|
43
|
+
|
|
44
|
+
protected readonly placeholders = AUTH_PLACEHOLDERS;
|
|
45
|
+
protected readonly routes = ROUTES;
|
|
46
|
+
|
|
47
|
+
protected override readonly form = this.fb.group({
|
|
48
|
+
email: ["", [emailValidator]],
|
|
49
|
+
password: ["", [requiredWithMessage("Password is required")]],
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
protected readonly submitError = signal<string | string[] | null>(null);
|
|
53
|
+
protected readonly submitting = signal<boolean>(false);
|
|
54
|
+
|
|
55
|
+
protected async onSubmit(): Promise<void> {
|
|
56
|
+
this.form.markAllAsTouched();
|
|
57
|
+
if (this.form.invalid) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this.submitError.set(null);
|
|
61
|
+
this.submitting.set(true);
|
|
62
|
+
try {
|
|
63
|
+
const { email, password } = this.form.getRawValue();
|
|
64
|
+
const startUrl = getStartUrl(this.route.snapshot.queryParamMap);
|
|
65
|
+
const response = await this.dataClient.fetch(API_ROUTES.LOGIN, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
body: JSON.stringify({
|
|
68
|
+
email: (email ?? "").trim().toLowerCase(),
|
|
69
|
+
password: password ?? "",
|
|
70
|
+
startUrl,
|
|
71
|
+
}),
|
|
72
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
73
|
+
});
|
|
74
|
+
const result = await handleApiResponse<AuthResponse>(response);
|
|
75
|
+
if (result?.redirectUrl) {
|
|
76
|
+
// Hard navigate to the URL which establishes the server session cookie.
|
|
77
|
+
window.location.replace(result.redirectUrl);
|
|
78
|
+
} else {
|
|
79
|
+
// In case redirectUrl is null, navigate to home.
|
|
80
|
+
void this.router.navigate(["/"], { replaceUrl: true });
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.error("Login failed", err);
|
|
84
|
+
this.submitError.set(err instanceof ApiError ? err.errors : "Login failed");
|
|
85
|
+
} finally {
|
|
86
|
+
this.submitting.set(false);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
2
|
+
<div class="text-center">
|
|
3
|
+
<h1 class="text-4xl font-bold text-gray-900 mb-4">404</h1>
|
|
4
|
+
<p class="text-lg text-gray-600 mb-8">Page not found</p>
|
|
5
|
+
<a
|
|
6
|
+
routerLink="/"
|
|
7
|
+
class="inline-block px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
|
|
8
|
+
>
|
|
9
|
+
Go to Home
|
|
10
|
+
</a>
|
|
11
|
+
</div>
|
|
12
|
+
</main>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
import { provideRouter } from '@angular/router';
|
|
3
|
+
import { NotFoundComponent } from './not-found.component';
|
|
4
|
+
|
|
5
|
+
describe('NotFoundComponent', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await TestBed.configureTestingModule({
|
|
8
|
+
imports: [NotFoundComponent],
|
|
9
|
+
providers: [provideRouter([])],
|
|
10
|
+
}).compileComponents();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should create the component', () => {
|
|
14
|
+
const fixture = TestBed.createComponent(NotFoundComponent);
|
|
15
|
+
const component = fixture.componentInstance;
|
|
16
|
+
expect(component).toBeTruthy();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should render main landmark', async () => {
|
|
20
|
+
const fixture = TestBed.createComponent(NotFoundComponent);
|
|
21
|
+
await fixture.whenStable();
|
|
22
|
+
const compiled = fixture.nativeElement as HTMLElement;
|
|
23
|
+
expect(compiled.querySelector('main')).toBeTruthy();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should have a link to home', async () => {
|
|
27
|
+
const fixture = TestBed.createComponent(NotFoundComponent);
|
|
28
|
+
await fixture.whenStable();
|
|
29
|
+
const compiled = fixture.nativeElement as HTMLElement;
|
|
30
|
+
const link = compiled.querySelector('a');
|
|
31
|
+
expect(link?.getAttribute('href')).toBe('/');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
7
|
+
import { RouterLink } from '@angular/router';
|
|
8
|
+
|
|
9
|
+
@Component({
|
|
10
|
+
selector: 'app-not-found',
|
|
11
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
12
|
+
imports: [RouterLink],
|
|
13
|
+
templateUrl: './not-found.component.html',
|
|
14
|
+
})
|
|
15
|
+
export class NotFoundComponent {}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<main class="flex min-h-svh w-full justify-center p-6 md:p-10 items-start">
|
|
2
|
+
<div class="w-full max-w-md">
|
|
3
|
+
@if (loadError()) {
|
|
4
|
+
<app-status-alert variant="error" [message]="loadError()!" />
|
|
5
|
+
} @else {
|
|
6
|
+
<app-auth-form
|
|
7
|
+
title="Profile"
|
|
8
|
+
description="Update your account information"
|
|
9
|
+
[showAlreadyLoggedIn]="false"
|
|
10
|
+
[error]="submitError()"
|
|
11
|
+
[success]="success() ? 'Profile updated!' : null"
|
|
12
|
+
submitText="Save Changes"
|
|
13
|
+
submitLoadingText="Saving…"
|
|
14
|
+
[submitting]="submitting()"
|
|
15
|
+
[submitDisabled]="loading()"
|
|
16
|
+
[footer]="{ link: routes.CHANGE_PASSWORD.PATH, linkText: 'Change password' }"
|
|
17
|
+
(submitted)="onSubmit()"
|
|
18
|
+
>
|
|
19
|
+
@if (loading()) {
|
|
20
|
+
<div role="status" aria-live="polite">
|
|
21
|
+
<span class="sr-only">Loading profile…</span>
|
|
22
|
+
<app-field label="Email"><app-skeleton class="h-9 w-full rounded-md" /></app-field>
|
|
23
|
+
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 items-start">
|
|
24
|
+
<app-field label="First name"
|
|
25
|
+
><app-skeleton class="h-9 w-full rounded-md"
|
|
26
|
+
/></app-field>
|
|
27
|
+
<app-field label="Last name"
|
|
28
|
+
><app-skeleton class="h-9 w-full rounded-md"
|
|
29
|
+
/></app-field>
|
|
30
|
+
</div>
|
|
31
|
+
<app-field label="Phone"><app-skeleton class="h-9 w-full rounded-md" /></app-field>
|
|
32
|
+
<app-field label="Street"><app-skeleton class="h-9 w-full rounded-md" /></app-field>
|
|
33
|
+
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 items-start">
|
|
34
|
+
<app-field label="City"><app-skeleton class="h-9 w-full rounded-md" /></app-field>
|
|
35
|
+
<app-field label="State"><app-skeleton class="h-9 w-full rounded-md" /></app-field>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 items-start">
|
|
38
|
+
<app-field label="Postal Code"
|
|
39
|
+
><app-skeleton class="h-9 w-full rounded-md"
|
|
40
|
+
/></app-field>
|
|
41
|
+
<app-field label="Country"><app-skeleton class="h-9 w-full rounded-md" /></app-field>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
} @else {
|
|
45
|
+
<div class="mt-4 flex flex-col gap-4">
|
|
46
|
+
<app-field
|
|
47
|
+
label="Email"
|
|
48
|
+
htmlFor="profile-email"
|
|
49
|
+
[error]="controlError('Email', 'email')"
|
|
50
|
+
>
|
|
51
|
+
<app-input type="email" [value]="form.controls.Email.value ?? ''" [disabled]="true" />
|
|
52
|
+
</app-field>
|
|
53
|
+
|
|
54
|
+
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 items-start">
|
|
55
|
+
<app-field
|
|
56
|
+
label="First name"
|
|
57
|
+
htmlFor="profile-first-name"
|
|
58
|
+
[error]="controlError('FirstName', 'required')"
|
|
59
|
+
>
|
|
60
|
+
<app-input
|
|
61
|
+
[value]="form.controls.FirstName.value ?? ''"
|
|
62
|
+
(valueChange)="setControl('FirstName', $event)"
|
|
63
|
+
/>
|
|
64
|
+
</app-field>
|
|
65
|
+
<app-field
|
|
66
|
+
label="Last name"
|
|
67
|
+
htmlFor="profile-last-name"
|
|
68
|
+
[error]="controlError('LastName', 'required')"
|
|
69
|
+
>
|
|
70
|
+
<app-input
|
|
71
|
+
[value]="form.controls.LastName.value ?? ''"
|
|
72
|
+
(valueChange)="setControl('LastName', $event)"
|
|
73
|
+
/>
|
|
74
|
+
</app-field>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<app-field label="Phone" htmlFor="profile-phone">
|
|
78
|
+
<app-input
|
|
79
|
+
type="tel"
|
|
80
|
+
[value]="form.controls.Phone.value ?? ''"
|
|
81
|
+
(valueChange)="setControl('Phone', $event)"
|
|
82
|
+
/>
|
|
83
|
+
</app-field>
|
|
84
|
+
|
|
85
|
+
<app-field label="Street" htmlFor="profile-street">
|
|
86
|
+
<app-input
|
|
87
|
+
[value]="form.controls.Street.value ?? ''"
|
|
88
|
+
(valueChange)="setControl('Street', $event)"
|
|
89
|
+
/>
|
|
90
|
+
</app-field>
|
|
91
|
+
|
|
92
|
+
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 items-start">
|
|
93
|
+
<app-field label="City" htmlFor="profile-city">
|
|
94
|
+
<app-input
|
|
95
|
+
[value]="form.controls.City.value ?? ''"
|
|
96
|
+
(valueChange)="setControl('City', $event)"
|
|
97
|
+
/>
|
|
98
|
+
</app-field>
|
|
99
|
+
<app-field label="State" htmlFor="profile-state">
|
|
100
|
+
<app-input
|
|
101
|
+
[value]="form.controls.State.value ?? ''"
|
|
102
|
+
(valueChange)="setControl('State', $event)"
|
|
103
|
+
/>
|
|
104
|
+
</app-field>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 items-start">
|
|
108
|
+
<app-field label="Postal Code" htmlFor="profile-postal-code">
|
|
109
|
+
<app-input
|
|
110
|
+
[value]="form.controls.PostalCode.value ?? ''"
|
|
111
|
+
(valueChange)="setControl('PostalCode', $event)"
|
|
112
|
+
/>
|
|
113
|
+
</app-field>
|
|
114
|
+
<app-field label="Country" htmlFor="profile-country">
|
|
115
|
+
<app-input
|
|
116
|
+
[value]="form.controls.Country.value ?? ''"
|
|
117
|
+
(valueChange)="setControl('Country', $event)"
|
|
118
|
+
/>
|
|
119
|
+
</app-field>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
}
|
|
123
|
+
</app-auth-form>
|
|
124
|
+
}
|
|
125
|
+
</div>
|
|
126
|
+
</main>
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import {
|
|
7
|
+
ChangeDetectionStrategy,
|
|
8
|
+
Component,
|
|
9
|
+
computed,
|
|
10
|
+
inject,
|
|
11
|
+
OnInit,
|
|
12
|
+
signal,
|
|
13
|
+
} from "@angular/core";
|
|
14
|
+
import { FormBuilder } from "@angular/forms";
|
|
15
|
+
import { AuthFormComponent } from "../../features/authentication/forms/auth-form/auth-form.component";
|
|
16
|
+
import { ReactiveFormPageBase } from "../../features/authentication/forms/reactive-form-page.base";
|
|
17
|
+
import { StatusAlertComponent } from "../../features/authentication/components/status-alert/status-alert.component";
|
|
18
|
+
import { FieldComponent } from "../../components/ui/field/field.component";
|
|
19
|
+
import { InputComponent } from "../../components/ui/input/input.component";
|
|
20
|
+
import { SkeletonComponent } from "../../components/ui/skeleton/skeleton.component";
|
|
21
|
+
import { AuthService } from "../../features/authentication/auth/auth.service";
|
|
22
|
+
import { UserProfileService } from "../../api/user-profile.service";
|
|
23
|
+
import { ROUTES } from "../../features/authentication/config/authentication.config";
|
|
24
|
+
import {
|
|
25
|
+
emailValidator,
|
|
26
|
+
requiredWithMessage,
|
|
27
|
+
} from "../../features/authentication/utils/auth-validators";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The profile form value shape (mirrors the React `ProfileFormValues`).
|
|
31
|
+
*
|
|
32
|
+
* Declared as a `type` (not an `interface`) so it is assignable to
|
|
33
|
+
* `Record<string, unknown>` when passed to `UserProfileService.updateUserProfile`:
|
|
34
|
+
* an interface is not index-signature-assignable (it can be augmented via
|
|
35
|
+
* declaration merging), whereas a type-literal is.
|
|
36
|
+
*/
|
|
37
|
+
type ProfileFormValues = {
|
|
38
|
+
FirstName: string;
|
|
39
|
+
LastName: string;
|
|
40
|
+
Email: string;
|
|
41
|
+
Phone: string | null;
|
|
42
|
+
Street: string | null;
|
|
43
|
+
City: string | null;
|
|
44
|
+
State: string | null;
|
|
45
|
+
PostalCode: string | null;
|
|
46
|
+
Country: string | null;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Profile page — Angular port of the React `Profile`. Protected route.
|
|
51
|
+
*
|
|
52
|
+
* Loads the current user's profile via GraphQL (`UserProfileService`) on init,
|
|
53
|
+
* showing a skeleton while loading. Email is read-only. On submit it updates
|
|
54
|
+
* via GraphQL and merges the returned record with the submitted values so
|
|
55
|
+
* FLS-hidden fields don't blank the form — identical to the React merge logic.
|
|
56
|
+
* `showAlreadyLoggedIn` is false.
|
|
57
|
+
*/
|
|
58
|
+
@Component({
|
|
59
|
+
selector: "app-profile-page",
|
|
60
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
61
|
+
imports: [
|
|
62
|
+
AuthFormComponent,
|
|
63
|
+
StatusAlertComponent,
|
|
64
|
+
FieldComponent,
|
|
65
|
+
InputComponent,
|
|
66
|
+
SkeletonComponent,
|
|
67
|
+
],
|
|
68
|
+
templateUrl: "./profile-page.component.html",
|
|
69
|
+
})
|
|
70
|
+
export class ProfilePageComponent extends ReactiveFormPageBase implements OnInit {
|
|
71
|
+
private readonly fb = inject(FormBuilder);
|
|
72
|
+
private readonly authService = inject(AuthService);
|
|
73
|
+
private readonly profileService = inject(UserProfileService);
|
|
74
|
+
|
|
75
|
+
protected readonly routes = ROUTES;
|
|
76
|
+
private readonly user = this.authService.requireUser();
|
|
77
|
+
|
|
78
|
+
protected override readonly form = this.fb.group({
|
|
79
|
+
FirstName: ["", [requiredWithMessage("First name is required")]],
|
|
80
|
+
LastName: ["", [requiredWithMessage("Last name is required")]],
|
|
81
|
+
Email: [{ value: "", disabled: true }, [emailValidator]],
|
|
82
|
+
Phone: [""],
|
|
83
|
+
Street: [""],
|
|
84
|
+
City: [""],
|
|
85
|
+
State: [""],
|
|
86
|
+
PostalCode: [""],
|
|
87
|
+
Country: [""],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
protected readonly loadError = signal<string | null>(null);
|
|
91
|
+
protected readonly submitError = signal<string | null>(null);
|
|
92
|
+
protected readonly submitting = signal<boolean>(false);
|
|
93
|
+
protected readonly success = signal<boolean>(false);
|
|
94
|
+
private readonly loaded = signal<boolean>(false);
|
|
95
|
+
|
|
96
|
+
/** True until the profile has loaded and no load error occurred. */
|
|
97
|
+
protected readonly loading = computed(() => !this.loaded() && this.loadError() === null);
|
|
98
|
+
|
|
99
|
+
ngOnInit(): void {
|
|
100
|
+
void this.loadProfile();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private async loadProfile(): Promise<void> {
|
|
104
|
+
try {
|
|
105
|
+
const data = await this.profileService.fetchUserProfile<Partial<ProfileFormValues>>(
|
|
106
|
+
this.user.id,
|
|
107
|
+
);
|
|
108
|
+
// Merge with defaults so missing fields (e.g. due to FLS) don't break the form.
|
|
109
|
+
this.applyProfile({
|
|
110
|
+
FirstName: data.FirstName ?? "",
|
|
111
|
+
LastName: data.LastName ?? "",
|
|
112
|
+
Email: data.Email ?? "",
|
|
113
|
+
Phone: data.Phone ?? null,
|
|
114
|
+
Street: data.Street ?? null,
|
|
115
|
+
City: data.City ?? null,
|
|
116
|
+
State: data.State ?? null,
|
|
117
|
+
PostalCode: data.PostalCode ?? null,
|
|
118
|
+
Country: data.Country ?? null,
|
|
119
|
+
});
|
|
120
|
+
this.loaded.set(true);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
console.error("Failed to load profile", err);
|
|
123
|
+
this.loadError.set("Failed to load profile");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private applyProfile(profile: ProfileFormValues): void {
|
|
128
|
+
this.form.reset({
|
|
129
|
+
FirstName: profile.FirstName,
|
|
130
|
+
LastName: profile.LastName,
|
|
131
|
+
Email: profile.Email,
|
|
132
|
+
Phone: profile.Phone ?? "",
|
|
133
|
+
Street: profile.Street ?? "",
|
|
134
|
+
City: profile.City ?? "",
|
|
135
|
+
State: profile.State ?? "",
|
|
136
|
+
PostalCode: profile.PostalCode ?? "",
|
|
137
|
+
Country: profile.Country ?? "",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
protected async onSubmit(): Promise<void> {
|
|
142
|
+
this.form.markAllAsTouched();
|
|
143
|
+
if (this.form.invalid) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
this.submitError.set(null);
|
|
147
|
+
this.success.set(false);
|
|
148
|
+
this.submitting.set(true);
|
|
149
|
+
try {
|
|
150
|
+
// getRawValue includes the disabled Email control.
|
|
151
|
+
const raw = this.form.getRawValue();
|
|
152
|
+
const value: ProfileFormValues = {
|
|
153
|
+
FirstName: (raw.FirstName ?? "").trim(),
|
|
154
|
+
LastName: (raw.LastName ?? "").trim(),
|
|
155
|
+
Email: (raw.Email ?? "").trim(),
|
|
156
|
+
Phone: this.optional(raw.Phone),
|
|
157
|
+
Street: this.optional(raw.Street),
|
|
158
|
+
City: this.optional(raw.City),
|
|
159
|
+
State: this.optional(raw.State),
|
|
160
|
+
PostalCode: this.optional(raw.PostalCode),
|
|
161
|
+
Country: this.optional(raw.Country),
|
|
162
|
+
};
|
|
163
|
+
const updated = await this.profileService.updateUserProfile<Partial<ProfileFormValues>>(
|
|
164
|
+
this.user.id,
|
|
165
|
+
value,
|
|
166
|
+
);
|
|
167
|
+
// Merge with submitted values so missing fields (e.g. FLS) don't break the form.
|
|
168
|
+
this.applyProfile({
|
|
169
|
+
FirstName: updated.FirstName ?? value.FirstName ?? "",
|
|
170
|
+
LastName: updated.LastName ?? value.LastName ?? "",
|
|
171
|
+
Email: updated.Email ?? value.Email ?? "",
|
|
172
|
+
Phone: updated.Phone ?? value.Phone ?? null,
|
|
173
|
+
Street: updated.Street ?? value.Street ?? null,
|
|
174
|
+
City: updated.City ?? value.City ?? null,
|
|
175
|
+
State: updated.State ?? value.State ?? null,
|
|
176
|
+
PostalCode: updated.PostalCode ?? value.PostalCode ?? null,
|
|
177
|
+
Country: updated.Country ?? value.Country ?? null,
|
|
178
|
+
});
|
|
179
|
+
this.success.set(true);
|
|
180
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error("Failed to update profile", err);
|
|
183
|
+
this.submitError.set("Failed to update profile");
|
|
184
|
+
} finally {
|
|
185
|
+
this.submitting.set(false);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Mirrors the React `optionalString`: trim and coerce empty to null. */
|
|
190
|
+
private optional(value: string | null | undefined): string | null {
|
|
191
|
+
const trimmed = (value ?? "").trim();
|
|
192
|
+
return trimmed === "" ? null : trimmed;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<main class="flex min-h-svh w-full justify-center p-6 md:p-10 items-start">
|
|
2
|
+
<div class="w-full max-w-2xl">
|
|
3
|
+
<app-auth-form
|
|
4
|
+
title="Sign Up"
|
|
5
|
+
description="Enter your information to create an account"
|
|
6
|
+
[error]="submitError()"
|
|
7
|
+
submitText="Create an account"
|
|
8
|
+
submitLoadingText="Creating account…"
|
|
9
|
+
[submitting]="submitting()"
|
|
10
|
+
[footer]="{ text: 'Already have an account?', link: routes.LOGIN.PATH, linkText: 'Sign in' }"
|
|
11
|
+
(submitted)="onSubmit()"
|
|
12
|
+
>
|
|
13
|
+
<div class="mt-4 flex flex-col gap-4">
|
|
14
|
+
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 items-start">
|
|
15
|
+
<app-field
|
|
16
|
+
label="First name"
|
|
17
|
+
htmlFor="register-first-name"
|
|
18
|
+
[error]="controlError('firstName', 'required')"
|
|
19
|
+
>
|
|
20
|
+
<app-input
|
|
21
|
+
[value]="form.controls.firstName.value ?? ''"
|
|
22
|
+
(valueChange)="setControl('firstName', $event)"
|
|
23
|
+
[placeholder]="placeholders.FIRST_NAME"
|
|
24
|
+
/>
|
|
25
|
+
</app-field>
|
|
26
|
+
|
|
27
|
+
<app-field
|
|
28
|
+
label="Last name"
|
|
29
|
+
htmlFor="register-last-name"
|
|
30
|
+
[error]="controlError('lastName', 'required')"
|
|
31
|
+
>
|
|
32
|
+
<app-input
|
|
33
|
+
[value]="form.controls.lastName.value ?? ''"
|
|
34
|
+
(valueChange)="setControl('lastName', $event)"
|
|
35
|
+
[placeholder]="placeholders.LAST_NAME"
|
|
36
|
+
/>
|
|
37
|
+
</app-field>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<app-field label="Email" htmlFor="register-email" [error]="controlError('email', 'email')">
|
|
41
|
+
<app-input
|
|
42
|
+
type="email"
|
|
43
|
+
[value]="form.controls.email.value ?? ''"
|
|
44
|
+
(valueChange)="setControl('email', $event)"
|
|
45
|
+
[placeholder]="placeholders.EMAIL"
|
|
46
|
+
/>
|
|
47
|
+
</app-field>
|
|
48
|
+
|
|
49
|
+
<app-field
|
|
50
|
+
label="Password"
|
|
51
|
+
htmlFor="register-password"
|
|
52
|
+
[error]="controlError('password', 'password')"
|
|
53
|
+
>
|
|
54
|
+
<app-input
|
|
55
|
+
type="password"
|
|
56
|
+
[value]="form.controls.password.value ?? ''"
|
|
57
|
+
(valueChange)="setControl('password', $event)"
|
|
58
|
+
[placeholder]="placeholders.PASSWORD_CREATE"
|
|
59
|
+
/>
|
|
60
|
+
</app-field>
|
|
61
|
+
|
|
62
|
+
<app-field
|
|
63
|
+
label="Confirm Password"
|
|
64
|
+
htmlFor="register-confirm-password"
|
|
65
|
+
[error]="
|
|
66
|
+
controlError('confirmPassword', 'passwordMismatch') ??
|
|
67
|
+
controlError('confirmPassword', 'required')
|
|
68
|
+
"
|
|
69
|
+
>
|
|
70
|
+
<app-input
|
|
71
|
+
type="password"
|
|
72
|
+
[value]="form.controls.confirmPassword.value ?? ''"
|
|
73
|
+
(valueChange)="setControl('confirmPassword', $event)"
|
|
74
|
+
[placeholder]="placeholders.PASSWORD_CONFIRM"
|
|
75
|
+
/>
|
|
76
|
+
</app-field>
|
|
77
|
+
</div>
|
|
78
|
+
</app-auth-form>
|
|
79
|
+
</div>
|
|
80
|
+
</main>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { ChangeDetectionStrategy, Component, inject, signal } from "@angular/core";
|
|
7
|
+
import { FormBuilder } from "@angular/forms";
|
|
8
|
+
import { ActivatedRoute, Router } from "@angular/router";
|
|
9
|
+
import { DataClient } from "../../api/data-client.service";
|
|
10
|
+
import { AuthFormComponent } from "../../features/authentication/forms/auth-form/auth-form.component";
|
|
11
|
+
import { ReactiveFormPageBase } from "../../features/authentication/forms/reactive-form-page.base";
|
|
12
|
+
import { FieldComponent } from "../../components/ui/field/field.component";
|
|
13
|
+
import { InputComponent } from "../../components/ui/input/input.component";
|
|
14
|
+
import {
|
|
15
|
+
API_ROUTES,
|
|
16
|
+
AUTH_PLACEHOLDERS,
|
|
17
|
+
ROUTES,
|
|
18
|
+
} from "../../features/authentication/config/authentication.config";
|
|
19
|
+
import { getStartUrl, type AuthResponse } from "../../features/authentication/utils/auth-helpers";
|
|
20
|
+
import { ApiError, handleApiResponse } from "../../features/authentication/utils/helpers";
|
|
21
|
+
import {
|
|
22
|
+
emailValidator,
|
|
23
|
+
passwordMinValidator,
|
|
24
|
+
passwordsMatchValidator,
|
|
25
|
+
requiredWithMessage,
|
|
26
|
+
} from "../../features/authentication/utils/auth-validators";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Register page — Angular port of the React `Register`. Same field set, same
|
|
30
|
+
* cross-field password-match rule, same POST body shape (`{ request }` with
|
|
31
|
+
* `confirmPassword` stripped) and same success handling (redirectUrl hard-nav,
|
|
32
|
+
* else navigate to login).
|
|
33
|
+
*/
|
|
34
|
+
@Component({
|
|
35
|
+
selector: "app-register-page",
|
|
36
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
37
|
+
imports: [AuthFormComponent, FieldComponent, InputComponent],
|
|
38
|
+
templateUrl: "./register-page.component.html",
|
|
39
|
+
})
|
|
40
|
+
export class RegisterPageComponent extends ReactiveFormPageBase {
|
|
41
|
+
private readonly fb = inject(FormBuilder);
|
|
42
|
+
private readonly router = inject(Router);
|
|
43
|
+
private readonly route = inject(ActivatedRoute);
|
|
44
|
+
private readonly dataClient = inject(DataClient);
|
|
45
|
+
|
|
46
|
+
protected readonly placeholders = AUTH_PLACEHOLDERS;
|
|
47
|
+
protected readonly routes = ROUTES;
|
|
48
|
+
|
|
49
|
+
protected override readonly form = this.fb.group(
|
|
50
|
+
{
|
|
51
|
+
firstName: ["", [requiredWithMessage("First name is required")]],
|
|
52
|
+
lastName: ["", [requiredWithMessage("Last name is required")]],
|
|
53
|
+
email: ["", [emailValidator]],
|
|
54
|
+
password: ["", [passwordMinValidator]],
|
|
55
|
+
confirmPassword: ["", [requiredWithMessage("Please confirm your password")]],
|
|
56
|
+
startUrl: [getStartUrl(this.route.snapshot.queryParamMap) || ""],
|
|
57
|
+
},
|
|
58
|
+
{ validators: [passwordsMatchValidator("password", "confirmPassword")] },
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
protected readonly submitError = signal<string | string[] | null>(null);
|
|
62
|
+
protected readonly submitting = signal<boolean>(false);
|
|
63
|
+
|
|
64
|
+
protected async onSubmit(): Promise<void> {
|
|
65
|
+
this.form.markAllAsTouched();
|
|
66
|
+
if (this.form.invalid) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
this.submitError.set(null);
|
|
70
|
+
this.submitting.set(true);
|
|
71
|
+
try {
|
|
72
|
+
const { confirmPassword: _confirmPassword, ...request } = this.form.getRawValue();
|
|
73
|
+
const response = await this.dataClient.fetch(API_ROUTES.REGISTER, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
body: JSON.stringify({ request }),
|
|
76
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
77
|
+
});
|
|
78
|
+
const result = await handleApiResponse<AuthResponse>(response);
|
|
79
|
+
if (result?.redirectUrl) {
|
|
80
|
+
// Hard navigate to the URL which logs the new user in.
|
|
81
|
+
window.location.replace(result.redirectUrl);
|
|
82
|
+
} else {
|
|
83
|
+
// In case redirectUrl is null, redirect to the login page.
|
|
84
|
+
void this.router.navigate([ROUTES.LOGIN.PATH], { replaceUrl: true });
|
|
85
|
+
}
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.error("Registration failed", err);
|
|
88
|
+
this.submitError.set(err instanceof ApiError ? err.errors : "Registration failed");
|
|
89
|
+
} finally {
|
|
90
|
+
this.submitting.set(false);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|