@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.
Files changed (211) hide show
  1. package/LICENSE.txt +82 -0
  2. package/dist/.forceignore +15 -0
  3. package/dist/.husky/pre-commit +4 -0
  4. package/dist/.prettierignore +11 -0
  5. package/dist/.prettierrc +17 -0
  6. package/dist/CHANGELOG.md +3966 -0
  7. package/dist/README.md +77 -0
  8. package/dist/config/project-scratch-def.json +13 -0
  9. package/dist/eslint.config.js +7 -0
  10. package/dist/force-app/main/default/classes/UIBundleAuthUtils.cls +68 -0
  11. package/dist/force-app/main/default/classes/UIBundleAuthUtils.cls-meta.xml +5 -0
  12. package/dist/force-app/main/default/classes/UIBundleChangePassword.cls +77 -0
  13. package/dist/force-app/main/default/classes/UIBundleChangePassword.cls-meta.xml +5 -0
  14. package/dist/force-app/main/default/classes/UIBundleForgotPassword.cls +71 -0
  15. package/dist/force-app/main/default/classes/UIBundleForgotPassword.cls-meta.xml +5 -0
  16. package/dist/force-app/main/default/classes/UIBundleLogin.cls +105 -0
  17. package/dist/force-app/main/default/classes/UIBundleLogin.cls-meta.xml +5 -0
  18. package/dist/force-app/main/default/classes/UIBundleRegistration.cls +162 -0
  19. package/dist/force-app/main/default/classes/UIBundleRegistration.cls-meta.xml +5 -0
  20. package/dist/force-app/main/default/digitalExperienceConfigs/angularexternalapp1.digitalExperienceConfig-meta.xml +8 -0
  21. package/dist/force-app/main/default/digitalExperiences/site/angularexternalapp1/angularexternalapp1.digitalExperience-meta.xml +11 -0
  22. package/dist/force-app/main/default/digitalExperiences/site/angularexternalapp1/sfdc_cms__site/angularexternalapp1/_meta.json +5 -0
  23. package/dist/force-app/main/default/digitalExperiences/site/angularexternalapp1/sfdc_cms__site/angularexternalapp1/content.json +10 -0
  24. package/dist/force-app/main/default/networks/angularexternalapp.network-meta.xml +60 -0
  25. package/dist/force-app/main/default/package.xml +24 -0
  26. package/dist/force-app/main/default/permissionsets/angularexternalapp_Guest_User_Api_Access.permissionset-meta.xml +9 -0
  27. package/dist/force-app/main/default/sites/angularexternalapp.site-meta.xml +31 -0
  28. package/dist/force-app/main/default/uiBundles/angularexternalapp/.forceignore +15 -0
  29. package/dist/force-app/main/default/uiBundles/angularexternalapp/.postcssrc.json +5 -0
  30. package/dist/force-app/main/default/uiBundles/angularexternalapp/.prettierignore +8 -0
  31. package/dist/force-app/main/default/uiBundles/angularexternalapp/.prettierrc +12 -0
  32. package/dist/force-app/main/default/uiBundles/angularexternalapp/CHANGELOG.md +4 -0
  33. package/dist/force-app/main/default/uiBundles/angularexternalapp/README.md +85 -0
  34. package/dist/force-app/main/default/uiBundles/angularexternalapp/angular.json +98 -0
  35. package/dist/force-app/main/default/uiBundles/angularexternalapp/angularexternalapp.uibundle-meta.xml +8 -0
  36. package/dist/force-app/main/default/uiBundles/angularexternalapp/e2e/app.spec.ts +15 -0
  37. package/dist/force-app/main/default/uiBundles/angularexternalapp/esbuild/api-version.mjs +17 -0
  38. package/dist/force-app/main/default/uiBundles/angularexternalapp/eslint.config.js +60 -0
  39. package/dist/force-app/main/default/uiBundles/angularexternalapp/middleware/html.mjs +3 -0
  40. package/dist/force-app/main/default/uiBundles/angularexternalapp/middleware/proxy.mjs +12 -0
  41. package/dist/force-app/main/default/uiBundles/angularexternalapp/package.json +56 -0
  42. package/dist/force-app/main/default/uiBundles/angularexternalapp/playwright.config.ts +25 -0
  43. package/dist/force-app/main/default/uiBundles/angularexternalapp/public/favicon.ico +0 -0
  44. package/dist/force-app/main/default/uiBundles/angularexternalapp/scripts/rewrite-e2e-assets.mjs +30 -0
  45. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/account/account-search.service.ts +196 -0
  46. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/account/distinctAccountIndustries.ts +26 -0
  47. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/account/distinctAccountTypes.ts +26 -0
  48. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/account/getAccountDetail.ts +128 -0
  49. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/account/searchAccounts.ts +58 -0
  50. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/data-client.service.spec.ts +184 -0
  51. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/data-client.service.ts +74 -0
  52. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/api/user-profile.service.ts +126 -0
  53. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/app.config.ts +22 -0
  54. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/app.html +1 -0
  55. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/app.routes.ts +77 -0
  56. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/app.spec.ts +18 -0
  57. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/app.ts +15 -0
  58. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/layout/app-layout/app-layout.component.html +53 -0
  59. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/layout/app-layout/app-layout.component.spec.ts +89 -0
  60. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/layout/app-layout/app-layout.component.ts +81 -0
  61. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/_field-size.scss +42 -0
  62. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/alert/alert.component.html +31 -0
  63. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/alert/alert.component.ts +53 -0
  64. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/button/button.component.html +18 -0
  65. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/button/button.component.scss +112 -0
  66. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/button/button.component.ts +57 -0
  67. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/card/card.component.scss +34 -0
  68. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/card/card.component.ts +110 -0
  69. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/collapsible/collapsible.component.html +10 -0
  70. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/collapsible/collapsible.component.ts +26 -0
  71. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-picker/date-picker.component.html +25 -0
  72. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-picker/date-picker.component.scss +36 -0
  73. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-picker/date-picker.component.ts +41 -0
  74. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-range-picker/date-range-picker.component.html +34 -0
  75. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-range-picker/date-range-picker.component.scss +33 -0
  76. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/date-range-picker/date-range-picker.component.ts +66 -0
  77. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/dialog/dialog.component.html +37 -0
  78. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/dialog/dialog.component.ts +55 -0
  79. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/field/field.component.html +21 -0
  80. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/field/field.component.ts +36 -0
  81. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/field-size.ts +8 -0
  82. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/input/input.component.html +19 -0
  83. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/input/input.component.scss +35 -0
  84. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/input/input.component.ts +36 -0
  85. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/label/label.component.html +3 -0
  86. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/label/label.component.ts +17 -0
  87. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/paginator/paginator.component.html +10 -0
  88. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/paginator/paginator.component.ts +37 -0
  89. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/popover/popover.component.html +41 -0
  90. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/popover/popover.component.ts +40 -0
  91. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/select/select.component.html +24 -0
  92. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/select/select.component.scss +35 -0
  93. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/select/select.component.ts +72 -0
  94. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/separator/separator.component.html +1 -0
  95. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/separator/separator.component.scss +23 -0
  96. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/separator/separator.component.ts +56 -0
  97. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/skeleton/skeleton.component.html +1 -0
  98. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/skeleton/skeleton.component.scss +28 -0
  99. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/skeleton/skeleton.component.ts +24 -0
  100. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/components/ui/spinner/spinner.component.ts +35 -0
  101. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/auth/auth.guard.ts +50 -0
  102. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/auth/auth.service.ts +126 -0
  103. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/components/status-alert/status-alert.component.html +7 -0
  104. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/components/status-alert/status-alert.component.ts +52 -0
  105. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/config/authentication.config.ts +75 -0
  106. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/auth-form/auth-form.component.html +38 -0
  107. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/auth-form/auth-form.component.ts +94 -0
  108. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/footer-link/footer-link.component.html +11 -0
  109. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/footer-link/footer-link.component.ts +26 -0
  110. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/reactive-form-page.base.ts +50 -0
  111. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/submit-button/submit-button.component.html +8 -0
  112. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/submit-button/submit-button.component.scss +17 -0
  113. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/forms/submit-button/submit-button.component.ts +35 -0
  114. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/layouts/auth-app-layout/auth-app-layout.component.html +2 -0
  115. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/layouts/auth-app-layout/auth-app-layout.component.ts +30 -0
  116. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/layouts/card-layout/card-layout.component.html +17 -0
  117. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/layouts/card-layout/card-layout.component.ts +40 -0
  118. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/menu/auth-menu/auth-menu.component.html +26 -0
  119. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/menu/auth-menu/auth-menu.component.ts +46 -0
  120. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/session-timeout/session-time-servlet.ts +130 -0
  121. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/session-timeout/session-timeout-validator/session-timeout-validator.component.html +57 -0
  122. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/session-timeout/session-timeout-validator/session-timeout-validator.component.ts +205 -0
  123. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/session-timeout/session-timeout.config.ts +83 -0
  124. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/session-timeout/session-timeout.service.ts +201 -0
  125. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/utils/auth-helpers.ts +68 -0
  126. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/utils/auth-validators.ts +72 -0
  127. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/authentication/utils/helpers.ts +85 -0
  128. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/active-filters/active-filters.component.html +23 -0
  129. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/active-filters/active-filters.component.ts +78 -0
  130. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/date-filter/date-filter.component.html +18 -0
  131. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/date-filter/date-filter.component.ts +137 -0
  132. 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
  133. 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
  134. 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
  135. 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
  136. 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
  137. 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
  138. 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
  139. 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
  140. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/search-filter/search-filter.component.html +8 -0
  141. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/search-filter/search-filter.component.ts +82 -0
  142. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/select-filter/select-filter.component.html +12 -0
  143. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/select-filter/select-filter.component.ts +65 -0
  144. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/text-filter/text-filter.component.html +12 -0
  145. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/filters/text-filter/text-filter.component.ts +90 -0
  146. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/object-breadcrumb/object-breadcrumb.component.html +55 -0
  147. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/object-breadcrumb/object-breadcrumb.component.ts +40 -0
  148. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/pagination-controls/pagination-controls.component.html +8 -0
  149. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/pagination-controls/pagination-controls.component.ts +61 -0
  150. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/search-bar/search-bar.component.html +59 -0
  151. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/search-bar/search-bar.component.ts +108 -0
  152. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/sort-control/sort-control.component.html +50 -0
  153. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/components/sort-control/sort-control.component.ts +72 -0
  154. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/object-search-state.service.ts +232 -0
  155. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/utils/debounce.ts +31 -0
  156. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/utils/field-utils.ts +54 -0
  157. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/utils/filter-utils.ts +354 -0
  158. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/utils/index.ts +12 -0
  159. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/features/object-search/utils/sort-utils.ts +46 -0
  160. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/account-detail/account-detail-page.component.html +174 -0
  161. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/account-detail/account-detail-page.component.scss +7 -0
  162. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/account-detail/account-detail-page.component.ts +199 -0
  163. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/account-search/account-search-page.component.html +111 -0
  164. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/account-search/account-search-page.component.ts +217 -0
  165. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/change-password/change-password-page.component.html +68 -0
  166. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/change-password/change-password-page.component.ts +82 -0
  167. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/forgot-password/forgot-password-page.component.html +35 -0
  168. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/forgot-password/forgot-password-page.component.ts +87 -0
  169. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/home/home-page.component.html +18 -0
  170. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/home/home-page.component.ts +47 -0
  171. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/home/home.component.html +6 -0
  172. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/home/home.component.spec.ts +23 -0
  173. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/home/home.component.ts +13 -0
  174. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/login/login-page.component.html +47 -0
  175. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/login/login-page.component.ts +89 -0
  176. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/not-found/not-found.component.html +12 -0
  177. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/not-found/not-found.component.spec.ts +33 -0
  178. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/not-found/not-found.component.ts +15 -0
  179. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/profile/profile-page.component.html +126 -0
  180. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/profile/profile-page.component.ts +194 -0
  181. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/register/register-page.component.html +80 -0
  182. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/register/register-page.component.ts +93 -0
  183. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/reset-password/reset-password-page.component.html +60 -0
  184. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/pages/reset-password/reset-password-page.component.ts +96 -0
  185. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/app/utils/async-data.ts +99 -0
  186. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/index.html +14 -0
  187. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/main.ts +5 -0
  188. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/styles.scss +170 -0
  189. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/theme.scss +39 -0
  190. package/dist/force-app/main/default/uiBundles/angularexternalapp/src/types/sf-globals.d.ts +9 -0
  191. package/dist/force-app/main/default/uiBundles/angularexternalapp/tsconfig.app.json +11 -0
  192. package/dist/force-app/main/default/uiBundles/angularexternalapp/tsconfig.json +33 -0
  193. package/dist/force-app/main/default/uiBundles/angularexternalapp/tsconfig.spec.json +10 -0
  194. package/dist/force-app/main/default/uiBundles/angularexternalapp/ui-bundle.json +7 -0
  195. package/dist/jest.config.js +6 -0
  196. package/dist/package-lock.json +10066 -0
  197. package/dist/package.json +46 -0
  198. package/dist/scripts/apex/hello.apex +10 -0
  199. package/dist/scripts/gitignore-templates.json +4 -0
  200. package/dist/scripts/graphql-search.sh +191 -0
  201. package/dist/scripts/org-setup-config-schema.mjs +103 -0
  202. package/dist/scripts/org-setup-dev.mjs +87 -0
  203. package/dist/scripts/org-setup-utils.mjs +369 -0
  204. package/dist/scripts/org-setup-xml.mjs +182 -0
  205. package/dist/scripts/org-setup.config.json +9 -0
  206. package/dist/scripts/org-setup.mjs +1702 -0
  207. package/dist/scripts/sf-project-setup.mjs +103 -0
  208. package/dist/scripts/soql/account.soql +6 -0
  209. package/dist/scripts/validate-org-setup-config.mjs +38 -0
  210. package/dist/sfdx-project.json +12 -0
  211. package/package.json +40 -0
@@ -0,0 +1,35 @@
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, input } from '@angular/core';
7
+ import { MatProgressSpinnerModule, ProgressSpinnerMode } from '@angular/material/progress-spinner';
8
+
9
+ /**
10
+ * Thin wrapper over Material's `mat-progress-spinner`. Defaults to an
11
+ * indeterminate 24px spinner. `ariaLabel` names the `role="progressbar"`.
12
+ */
13
+ @Component({
14
+ selector: 'app-spinner',
15
+ changeDetection: ChangeDetectionStrategy.OnPush,
16
+ imports: [MatProgressSpinnerModule],
17
+ template: `
18
+ <mat-progress-spinner
19
+ [mode]="mode()"
20
+ [diameter]="diameter()"
21
+ [value]="value()"
22
+ [attr.aria-label]="ariaLabel()"
23
+ />
24
+ `,
25
+ })
26
+ export class SpinnerComponent {
27
+ readonly diameter = input<number>(24);
28
+ readonly mode = input<ProgressSpinnerMode>('indeterminate');
29
+
30
+ /** Progress value (0–100) used only when `mode` is `determinate`. */
31
+ readonly value = input<number>(0);
32
+
33
+ /** Accessible name announced by assistive tech. */
34
+ readonly ariaLabel = input<string>('Loading', { alias: 'aria-label' });
35
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { inject } from "@angular/core";
7
+ import { CanActivateFn, Router, UrlTree } from "@angular/router";
8
+ import { AuthService } from "./auth.service";
9
+ import { AUTH_REDIRECT_PARAM, ROUTES } from "../config/authentication.config";
10
+
11
+ /**
12
+ * Route guard — the Angular analogue of the React `PrivateRoute` layout.
13
+ *
14
+ * Allows activation when the user is authenticated. Otherwise redirects to the
15
+ * login page with a `startUrl` query param capturing the requested destination,
16
+ * so the user is returned there after a successful login.
17
+ *
18
+ * Because `AuthService` runs its initial check on construction, the guard first
19
+ * awaits any in-flight `loading` state (equivalent to the React guard returning
20
+ * `null` while loading, then re-rendering once auth resolves). It resolves to a
21
+ * boolean `true` or a `UrlTree` redirect — never leaves the user on a blank
22
+ * screen.
23
+ */
24
+ export const authGuard: CanActivateFn = async (route, state): Promise<boolean | UrlTree> => {
25
+ const authService = inject(AuthService);
26
+ const router = inject(Router);
27
+
28
+ // Wait for the initial auth check to settle before deciding. If a check is
29
+ // already resolved this returns immediately.
30
+ if (authService.loading()) {
31
+ await authService.checkAuth();
32
+ } else if (authService.user() === null && authService.error() !== null) {
33
+ // The prior check finished unauthenticated *because of an error* (e.g. a
34
+ // transient network failure), not a confirmed guest. Without this, a single
35
+ // failed initial check would poison protected-route access until a full page
36
+ // reload. Re-run the check on guard entry so recovery doesn't require reload.
37
+ await authService.checkAuth();
38
+ }
39
+
40
+ if (authService.isAuthenticated()) {
41
+ return true;
42
+ }
43
+
44
+ // Capture the current destination (path + query) to return to after login.
45
+ // `state.url` is already the resolved absolute URL for the attempted route.
46
+ const destination = state.url;
47
+ return router.createUrlTree([ROUTES.LOGIN.PATH], {
48
+ queryParams: { [AUTH_REDIRECT_PARAM]: destination },
49
+ });
50
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { computed, inject, Injectable, signal } from "@angular/core";
7
+ import { DataClient } from "../../../api/data-client.service";
8
+ import { API_ROUTES } from "../config/authentication.config";
9
+
10
+ /** The authenticated user shape (mirrors the React `AuthContext` `User`). */
11
+ export interface AuthUser {
12
+ readonly id: string;
13
+ readonly name: string;
14
+ }
15
+
16
+ // UI API version used for the Chatter current-user lookup. Mirrors the constant
17
+ // baked into @salesforce/ui-bundle's getCurrentUser helper.
18
+ const API_VERSION = "65.0";
19
+
20
+ /**
21
+ * Angular signal-based replacement for the React `AuthProvider` / `useAuth`.
22
+ *
23
+ * `providedIn: 'root'` makes the service a single app-wide singleton, so it
24
+ * replaces the React context+provider wrapper without any `<AuthProvider>` in
25
+ * the tree (the coordinator-approved design). State is exposed as readonly
26
+ * signals; `isAuthenticated` is a computed mirror of `user !== null`.
27
+ *
28
+ * Authentication is checked on construction (equivalent to the React mount
29
+ * `useEffect`). `checkAuth()` re-runs the lookup on demand.
30
+ *
31
+ * The current-user lookup goes through the base app's `DataClient.fetch` to the
32
+ * Chatter `/users/me` endpoint — the exact call `@salesforce/ui-bundle`'s
33
+ * `getCurrentUser` makes — so the feature reuses the base app's existing
34
+ * `@salesforce/platform-sdk` dependency and adds no new package.
35
+ */
36
+ @Injectable({ providedIn: "root" })
37
+ export class AuthService {
38
+ // DataClient is provided by the base-angular-app; consumed via the
39
+ // `__inherit__` stub under app/api/ (patches-cli strips the prefix at compose
40
+ // so it resolves to the seeded base data-client.service.ts).
41
+ private readonly dataClient = inject(DataClient);
42
+
43
+ private readonly _user = signal<AuthUser | null>(null);
44
+ private readonly _loading = signal<boolean>(true);
45
+ private readonly _error = signal<string | null>(null);
46
+
47
+ /** The current authenticated user, or null when unauthenticated. */
48
+ readonly user = this._user.asReadonly();
49
+ /** True while the initial (or a manual) auth check is in flight. */
50
+ readonly loading = this._loading.asReadonly();
51
+ /** Error message from the most recent failed auth check, else null. */
52
+ readonly error = this._error.asReadonly();
53
+ /** Mirrors React `isAuthenticated = user !== null`. */
54
+ readonly isAuthenticated = computed(() => this._user() !== null);
55
+
56
+ constructor() {
57
+ // Kick off the initial check on construction (React mount `useEffect`).
58
+ void this.checkAuth();
59
+ }
60
+
61
+ /**
62
+ * Fetches the current user and updates state. Mirrors the React `checkAuth`.
63
+ */
64
+ async checkAuth(): Promise<void> {
65
+ this._loading.set(true);
66
+ this._error.set(null);
67
+ try {
68
+ const userData = await this.getCurrentUser();
69
+ this._user.set(userData);
70
+ } catch (err) {
71
+ console.error("Authentication failed", err);
72
+ this._error.set("Authentication failed");
73
+ this._user.set(null);
74
+ } finally {
75
+ this._loading.set(false);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Returns the current user, throwing when unauthenticated. The Angular
81
+ * analogue of the React `useUser()` hook — used by the Profile page which
82
+ * only renders behind the auth guard.
83
+ */
84
+ requireUser(): AuthUser {
85
+ const user = this._user();
86
+ if (!user) {
87
+ throw new Error("Authenticated context not established");
88
+ }
89
+ return user;
90
+ }
91
+
92
+ /**
93
+ * Navigate to the server-side logout endpoint. Uses `replace` to prevent the
94
+ * back button from returning to an authenticated session. Direct port of the
95
+ * React `logout`.
96
+ */
97
+ logout(startURL?: string): void {
98
+ const finalLogoutUrl = startURL
99
+ ? `${API_ROUTES.LOGOUT}?startURL=${encodeURIComponent(startURL)}`
100
+ : API_ROUTES.LOGOUT;
101
+ window.location.replace(finalLogoutUrl);
102
+ }
103
+
104
+ /**
105
+ * Fetches the current user via the Chatter `/users/me` endpoint. Reproduces
106
+ * `@salesforce/ui-bundle`'s `getCurrentUser` so we don't add ui-bundle as a
107
+ * dependency (base app only ships @salesforce/platform-sdk).
108
+ */
109
+ private async getCurrentUser(): Promise<AuthUser> {
110
+ const response = await this.dataClient.fetch(
111
+ `/services/data/v${API_VERSION}/chatter/users/me`,
112
+ {
113
+ method: "GET",
114
+ headers: { Accept: "application/json" },
115
+ },
116
+ );
117
+ if (!response.ok) {
118
+ throw new Error(`Failed to fetch current user: HTTP ${response.status}`);
119
+ }
120
+ const data = (await response.json()) as { id?: string; name?: string };
121
+ if (!data?.id) {
122
+ throw new Error("Current user response missing id");
123
+ }
124
+ return { id: data.id, name: data.name || "User" };
125
+ }
126
+ }
@@ -0,0 +1,7 @@
1
+ <app-alert [title]="title()" [class]="toneClass()">
2
+ @if (message()) {
3
+ {{ message() }}
4
+ } @else {
5
+ <ng-content />
6
+ }
7
+ </app-alert>
@@ -0,0 +1,52 @@
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, computed, input } from "@angular/core";
7
+ import { AlertComponent } from "../../../../components/ui/alert/alert.component";
8
+
9
+ /** Status banner variant conveying the tone of the message. */
10
+ export type AppStatusAlertVariant = "error" | "success" | "info" | "warning";
11
+
12
+ /**
13
+ * Status / error banner — a feature-owned composition over the base `app-alert`
14
+ * ui primitive (not a base-template component). Adds a four-way `variant` with a
15
+ * colour treatment, mirroring the React `components/alerts/status-alert`.
16
+ *
17
+ * Supply the copy via the `message` input, or content-project richer markup
18
+ * (lists, links) as the default slot. Consumed by the authentication
19
+ * login-error / success / session-expired banners.
20
+ */
21
+ @Component({
22
+ selector: "app-status-alert",
23
+ changeDetection: ChangeDetectionStrategy.OnPush,
24
+ imports: [AlertComponent],
25
+ templateUrl: "./status-alert.component.html",
26
+ host: {
27
+ "data-slot": "status-alert",
28
+ },
29
+ })
30
+ export class StatusAlertComponent {
31
+ readonly variant = input<AppStatusAlertVariant>("info");
32
+
33
+ /** Optional title rendered above the message / projected content. */
34
+ readonly title = input<string>("");
35
+
36
+ /** Message copy; may be omitted in favour of projected content. */
37
+ readonly message = input<string>("");
38
+
39
+ /** Colour utilities applied to the banner for the current variant. */
40
+ protected readonly toneClass = computed(() => {
41
+ switch (this.variant()) {
42
+ case "error":
43
+ return "text-destructive";
44
+ case "success":
45
+ return "text-emerald-700";
46
+ case "warning":
47
+ return "text-amber-700";
48
+ default:
49
+ return "text-sky-700";
50
+ }
51
+ });
52
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ /**
8
+ * Centralized configuration for Auth routes. Each route contains both the path
9
+ * and page title. Using constants prevents typos in route paths across the app.
10
+ *
11
+ * Direct port of the React `authenticationConfig.ts`. NOTE: Angular route
12
+ * `path` values are relative (no leading slash) in `app.routes.ts`; these
13
+ * PATH constants keep the leading slash so `routerLink` / `navigate` calls and
14
+ * the open-redirect validation behave exactly as the React version.
15
+ */
16
+ export const ROUTES = {
17
+ LOGIN: {
18
+ PATH: "/login",
19
+ TITLE: "Login | MyApp",
20
+ },
21
+ REGISTER: {
22
+ PATH: "/register",
23
+ TITLE: "Create Account | MyApp",
24
+ },
25
+ FORGOT_PASSWORD: {
26
+ PATH: "/forgot-password",
27
+ TITLE: "Recover Password | MyApp",
28
+ },
29
+ RESET_PASSWORD: {
30
+ PATH: "/reset-password",
31
+ TITLE: "Reset Password | MyApp",
32
+ },
33
+ PROFILE: {
34
+ PATH: "/profile",
35
+ TITLE: "My Profile | MyApp",
36
+ },
37
+ CHANGE_PASSWORD: {
38
+ PATH: "/change-password",
39
+ TITLE: "Change Password | MyApp",
40
+ },
41
+ } as const;
42
+
43
+ /**
44
+ * Centralized configuration for API endpoints. These are server-side endpoints,
45
+ * not client-side routes.
46
+ */
47
+ export const API_ROUTES = {
48
+ // W-21253864: Logout URL integration is not currently supported
49
+ LOGOUT: "/secur/logout.jsp",
50
+ // Custom Apex REST resources handling the auth flows.
51
+ LOGIN: "/services/apexrest/auth/login",
52
+ REGISTER: "/services/apexrest/auth/register",
53
+ FORGOT_PASSWORD: "/services/apexrest/auth/forgot-password",
54
+ RESET_PASSWORD: "/services/apexrest/auth/reset-password",
55
+ CHANGE_PASSWORD: "/services/apexrest/auth/change-password",
56
+ } as const;
57
+
58
+ /**
59
+ * Query parameter key used to store the return URL.
60
+ * e.g. /login?startUrl=/profile
61
+ */
62
+ export const AUTH_REDIRECT_PARAM = "startUrl";
63
+
64
+ /** Placeholder text constants for authentication form inputs. */
65
+ export const AUTH_PLACEHOLDERS = {
66
+ EMAIL: "e.g. name@example.com",
67
+ PASSWORD: "Enter your password",
68
+ PASSWORD_CREATE: "Create a password",
69
+ PASSWORD_CONFIRM: "Re-enter your password",
70
+ PASSWORD_NEW: "Enter new password",
71
+ PASSWORD_NEW_CONFIRM: "Re-enter new password",
72
+ FIRST_NAME: "e.g. Alex",
73
+ LAST_NAME: "e.g. Smith",
74
+ USERNAME: "e.g. asmith",
75
+ } as const;
@@ -0,0 +1,38 @@
1
+ <app-card-layout [title]="title()" [subtitle]="description()">
2
+ <div class="space-y-6">
3
+ @if (showAuthAlert()) {
4
+ <app-status-alert variant="info" message="You are already logged in." />
5
+ }
6
+ @if (errorMessages().length > 0) {
7
+ @if (errorMessages().length === 1) {
8
+ <app-status-alert variant="error" [message]="errorMessages()[0]" />
9
+ } @else {
10
+ <app-status-alert variant="error">
11
+ <ul>
12
+ @for (msg of errorMessages(); track msg) {
13
+ <li>{{ msg }}</li>
14
+ }
15
+ </ul>
16
+ </app-status-alert>
17
+ }
18
+ }
19
+ @if (success()) {
20
+ <app-status-alert variant="success" [message]="success()!" />
21
+ }
22
+
23
+ <form (ngSubmit)="onSubmit()">
24
+ <ng-content />
25
+ <app-submit-button
26
+ class="mt-6 block"
27
+ [label]="submitText()"
28
+ [loadingLabel]="submitLoadingText()"
29
+ [submitting]="submitting()"
30
+ [disabled]="isSubmitDisabled()"
31
+ />
32
+ </form>
33
+
34
+ @if (footer(); as f) {
35
+ <app-footer-link [text]="f.text ?? ''" [to]="f.link" [linkText]="f.linkText" />
36
+ }
37
+ </div>
38
+ </app-card-layout>
@@ -0,0 +1,94 @@
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, computed, inject, input, output } from "@angular/core";
7
+ import { FormsModule } from "@angular/forms";
8
+ import { CardLayoutComponent } from "../../layouts/card-layout/card-layout.component";
9
+ import { StatusAlertComponent } from "../../components/status-alert/status-alert.component";
10
+ import { SubmitButtonComponent } from "../submit-button/submit-button.component";
11
+ import { FooterLinkComponent } from "../footer-link/footer-link.component";
12
+ import { AuthService } from "../../auth/auth.service";
13
+
14
+ /** Footer link descriptor for the auth form frame. */
15
+ export interface AuthFormFooter {
16
+ text?: string;
17
+ link: string;
18
+ linkText: string;
19
+ }
20
+
21
+ /**
22
+ * Wrapper enforcing a consistent layout (card) and error/success alert
23
+ * positioning for all authentication forms — the Angular port of the React
24
+ * `AuthForm`.
25
+ *
26
+ * Auth-aware behavior (identical to React):
27
+ * - While auth state is loading, the submit button is disabled.
28
+ * - If the user is already authenticated and `showAlreadyLoggedIn` is true, an
29
+ * info alert is shown and submit is disabled.
30
+ *
31
+ * The owning page holds the FormGroup and binds `[formGroup]` on the projected
32
+ * `<form>`; this component renders the frame, alerts, submit button, and footer.
33
+ * It emits `(submitted)` when the submit button is pressed so the page runs its
34
+ * submit handler. The page passes its submitting flag via `[submitting]` and any
35
+ * extra disabled condition via `[submitDisabled]`.
36
+ */
37
+ @Component({
38
+ selector: "app-auth-form",
39
+ changeDetection: ChangeDetectionStrategy.OnPush,
40
+ imports: [
41
+ FormsModule,
42
+ CardLayoutComponent,
43
+ StatusAlertComponent,
44
+ SubmitButtonComponent,
45
+ FooterLinkComponent,
46
+ ],
47
+ templateUrl: "./auth-form.component.html",
48
+ })
49
+ export class AuthFormComponent {
50
+ private readonly authService = inject(AuthService);
51
+
52
+ readonly title = input.required<string>();
53
+ readonly description = input<string>("");
54
+ /** Error message(s) to surface; empty/null hides the error alert. */
55
+ readonly error = input<string | string[] | null>(null);
56
+ /** Success message to surface; empty/null hides the success alert. */
57
+ readonly success = input<string | null>(null);
58
+ /** Whether to show the "already logged in" alert and disable submit. @default true */
59
+ readonly showAlreadyLoggedIn = input<boolean>(true);
60
+ readonly submitText = input.required<string>();
61
+ readonly submitLoadingText = input<string>("Submitting…");
62
+ /** True while the page's async submit is in flight. */
63
+ readonly submitting = input<boolean>(false);
64
+ /** Extra disabled condition from the page (e.g. success state). */
65
+ readonly submitDisabled = input<boolean>(false);
66
+ readonly footer = input<AuthFormFooter | null>(null);
67
+
68
+ /** Emitted when the submit button is pressed (page runs its handler). */
69
+ readonly submitted = output<void>();
70
+
71
+ protected readonly isAuthenticated = this.authService.isAuthenticated;
72
+ protected readonly loading = this.authService.loading;
73
+
74
+ /** Info alert shown when already authenticated on a public page. */
75
+ protected readonly showAuthAlert = computed(
76
+ () => this.showAlreadyLoggedIn() && this.isAuthenticated(),
77
+ );
78
+
79
+ /** Submit disabled when: caller says so, already-logged-in, or auth loading. */
80
+ protected readonly isSubmitDisabled = computed(
81
+ () => this.submitDisabled() || this.showAuthAlert() || this.loading(),
82
+ );
83
+
84
+ /** Normalizes the error input to an array for the template's list rendering. */
85
+ protected readonly errorMessages = computed<string[]>(() => {
86
+ const err = this.error();
87
+ if (!err) return [];
88
+ return Array.isArray(err) ? err : [err];
89
+ });
90
+
91
+ protected onSubmit(): void {
92
+ this.submitted.emit();
93
+ }
94
+ }
@@ -0,0 +1,11 @@
1
+ <p class="w-full text-center text-sm text-muted-foreground">
2
+ @if (text()) {
3
+ {{ text() }}&nbsp;
4
+ }
5
+ <a
6
+ [routerLink]="to()"
7
+ class="font-medium underline hover:text-primary transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
8
+ >
9
+ {{ linkText() }}
10
+ </a>
11
+ </p>
@@ -0,0 +1,26 @@
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, input } from "@angular/core";
7
+ import { RouterLink } from "@angular/router";
8
+
9
+ /**
10
+ * Footer navigation link under an auth form — the Angular port of the React
11
+ * `FooterLink`. Renders an optional prefix text followed by a `routerLink`.
12
+ */
13
+ @Component({
14
+ selector: "app-footer-link",
15
+ changeDetection: ChangeDetectionStrategy.OnPush,
16
+ imports: [RouterLink],
17
+ templateUrl: "./footer-link.component.html",
18
+ })
19
+ export class FooterLinkComponent {
20
+ /** Link text prefix (e.g., "Don't have an account?"). */
21
+ readonly text = input<string>("");
22
+ /** Router path to navigate to. */
23
+ readonly to = input.required<string>();
24
+ /** Link label (e.g., "Sign up"). */
25
+ readonly linkText = input.required<string>();
26
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { Directive } from "@angular/core";
7
+ import { FormGroup } from "@angular/forms";
8
+
9
+ /**
10
+ * Shared helpers for the auth form pages.
11
+ *
12
+ * The base `app-input` primitive exposes a two-way `[(value)]` model rather than
13
+ * a ControlValueAccessor, so neither `formControlName` nor `[formGroup]` can bind
14
+ * to it directly. These pages therefore keep a Reactive Forms `FormGroup` as the
15
+ * validation + submit source of truth (with no `[formGroup]` on the template
16
+ * `<form>`) and bridge each field manually with `[value]` / `(valueChange)`:
17
+ * <app-input [value]="form.controls.x.value" (valueChange)="setControl('x', $event)" />
18
+ *
19
+ * `setControl` pushes edits back into the control (marking it touched/dirty so
20
+ * validation surfaces), and `controlError` reads a specific error message for
21
+ * display once the control has been touched — mirroring the React forms, which
22
+ * showed field errors after interaction.
23
+ *
24
+ * Kept as an abstract base (not injectable) so each page owns its own typed
25
+ * `form`. Not an Angular component/service; no DI.
26
+ */
27
+ @Directive()
28
+ export abstract class ReactiveFormPageBase {
29
+ protected abstract readonly form: FormGroup;
30
+
31
+ /** Push an edited value into the named control and mark it touched/dirty. */
32
+ protected setControl(name: string, value: string): void {
33
+ const control = this.form.get(name);
34
+ if (!control) return;
35
+ control.setValue(value);
36
+ control.markAsDirty();
37
+ control.markAsTouched();
38
+ }
39
+
40
+ /**
41
+ * Returns the message for the named error key on the control, but only once
42
+ * the control has been touched (so pristine fields don't show errors).
43
+ */
44
+ protected controlError(name: string, errorKey: string): string | null {
45
+ const control = this.form.get(name);
46
+ if (!control || !control.touched) return null;
47
+ const message = control.errors?.[errorKey];
48
+ return typeof message === "string" ? message : null;
49
+ }
50
+ }
@@ -0,0 +1,8 @@
1
+ <app-button type="submit" [disabled]="submitting() || disabled()" class="w-full">
2
+ @if (submitting()) {
3
+ <app-spinner [diameter]="16" aria-hidden="true" />
4
+ {{ loadingLabel() }}
5
+ } @else {
6
+ {{ label() }}
7
+ }
8
+ </app-button>
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ // Material lays the button's projected content out inside `.mdc-button__label`.
8
+ // Make it a flex row so the spinner and the loading label sit on the same line,
9
+ // vertically centred. Scoped under `:host` (via `::ng-deep`) so the override
10
+ // only reaches this submit button's inner `matButton`, never other buttons.
11
+ :host ::ng-deep .mdc-button__label {
12
+ display: flex;
13
+
14
+ > app-spinner {
15
+ margin: auto 1rem;
16
+ }
17
+ }
@@ -0,0 +1,35 @@
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, input } from "@angular/core";
7
+ import { ButtonComponent } from "../../../../components/ui/button/button.component";
8
+ import { SpinnerComponent } from "../../../../components/ui/spinner/spinner.component";
9
+
10
+ /**
11
+ * Submit button that reflects form submission state — the Angular port of the
12
+ * React `SubmitButton`, which subscribed to `form.isSubmitting`.
13
+ *
14
+ * Reactive Forms have no equivalent subscription; instead the parent passes the
15
+ * form's `pending`/submitting flag via `[submitting]` and the caller-provided
16
+ * `[disabled]`. While submitting the button is disabled and shows a spinner with
17
+ * the loading label — the same UX (prevents double-submit + gives feedback).
18
+ */
19
+ @Component({
20
+ selector: "app-submit-button",
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ imports: [ButtonComponent, SpinnerComponent],
23
+ templateUrl: "./submit-button.component.html",
24
+ styleUrl: "./submit-button.component.scss",
25
+ })
26
+ export class SubmitButtonComponent {
27
+ /** Button text when not submitting. */
28
+ readonly label = input.required<string>();
29
+ /** Button text while submitting. */
30
+ readonly loadingLabel = input<string>("Submitting…");
31
+ /** True while the form submission is in flight. */
32
+ readonly submitting = input<boolean>(false);
33
+ /** Additional disabled condition from the caller (e.g. already authenticated). */
34
+ readonly disabled = input<boolean>(false);
35
+ }
@@ -0,0 +1,30 @@
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 { AppLayoutComponent } from "../../../../components/layout/app-layout/app-layout.component";
8
+ import { SessionTimeoutValidatorComponent } from "../../session-timeout/session-timeout-validator/session-timeout-validator.component";
9
+
10
+ /**
11
+ * Top-level app wrapper — the faithful Angular port of the React `AuthAppLayout`
12
+ * (`<AuthProvider><SessionTimeoutValidator/><AppLayout/></AuthProvider>`).
13
+ *
14
+ * There is no AuthProvider equivalent: `AuthService` is `providedIn: 'root'`, so
15
+ * auth state is app-wide automatically. This component renders the
16
+ * session-timeout validator (whose warning modal is thus available on every
17
+ * child route, exactly like React) followed by the base `app-layout`. Child
18
+ * routes render inside the base layout's own `<router-outlet />` post-compose.
19
+ *
20
+ * Wired as the top route node's `component` in `app.routes.ts`; the route-merger
21
+ * swaps the base layout node for this wrapper (the same element-swap object-search
22
+ * performs by repeating `component: AppLayoutComponent`).
23
+ */
24
+ @Component({
25
+ selector: "app-auth-app-layout",
26
+ changeDetection: ChangeDetectionStrategy.OnPush,
27
+ imports: [SessionTimeoutValidatorComponent, AppLayoutComponent],
28
+ templateUrl: "./auth-app-layout.component.html",
29
+ })
30
+ export class AuthAppLayoutComponent {}