@salesforce/templates 66.13.5 → 66.13.7
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/lib/templates/project/angularextapp/CHANGELOG.md +258 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/angular.json +1 -1
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/eslint-plugin-angular-graphql.js +148 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/eslint.config.js +9 -1
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package-lock.json +680 -408
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package.json +2 -2
- package/lib/templates/project/angularextapp/package.json +1 -1
- package/lib/templates/project/angularextapp/scripts/org-setup-config-schema.mjs +17 -0
- package/lib/templates/project/angularextapp/scripts/org-setup-utils.mjs +33 -3
- package/lib/templates/project/angularextapp/scripts/org-setup.mjs +535 -3
- package/lib/templates/project/angularintapp/CHANGELOG.md +266 -0
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/angular.json +1 -1
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/eslint-plugin-angular-graphql.js +148 -0
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/eslint.config.js +9 -1
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package-lock.json +690 -418
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package.json +2 -2
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/features/agentforce/agentforce-embed.service.ts +6 -1
- package/lib/templates/project/angularintapp/package.json +1 -1
- package/lib/templates/project/angularintapp/scripts/org-setup-config-schema.mjs +17 -0
- package/lib/templates/project/angularintapp/scripts/org-setup-utils.mjs +33 -3
- package/lib/templates/project/angularintapp/scripts/org-setup.mjs +535 -3
- package/lib/templates/project/reactexternalapp/CHANGELOG.md +1409 -0
- package/lib/templates/project/reactexternalapp/PRD.md +38 -0
- package/lib/templates/project/reactexternalapp/README.md +14 -10
- package/lib/templates/project/reactexternalapp/_p_/_m_/_ps_/reactexternalapp_Guest_User_Api_Access.permissionset-meta.xml +25 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package-lock.json +953 -714
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package.json +9 -7
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/scripts/get-graphql-schema.mjs +8 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/LanguageSwitcher.tsx +116 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/__examples__/language-switcher-example.tsx +36 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/__examples__/vite-config-site-example.ts +44 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/buildLanguageUrl.ts +158 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/index.ts +28 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/languages.ts +46 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/components/SocialLogin.tsx +141 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/Login.tsx +2 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/README.md +246 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/apiUtils.ts +56 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/orgApiVersionService.ts +37 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/searchableContentTypesService.ts +116 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/channelResolver.ts +40 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/cmsQueryFragment.ts +97 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeSessionCache.ts +134 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeUtils.ts +38 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/hooks/useSearchableContentTypes.ts +128 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/index.ts +54 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/parseResponse.ts +65 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/searchChannel.ts +15 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/types.ts +66 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/registry.ts +38 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/index.ts +19 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/parseResponse.ts +56 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/queryFragment.ts +141 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts +101 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/distinctValuesService.ts +84 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/searchService.ts +136 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/GlobalSearchBox.tsx +98 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.tsx +294 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/Search.tsx +203 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.tsx +88 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.tsx +187 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/PaginationControls.tsx +185 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/ScopeSelector.tsx +107 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SearchBar.tsx +55 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SortControl.tsx +62 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/ActiveFilters.tsx +61 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.tsx +125 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_/components → search/components/filters}/FilterContext.tsx +7 -20
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/BooleanFilter.tsx +50 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/DateRangeFilter.tsx +160 -0
- package/lib/templates/project/{reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters → reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs}/FilterFieldWrapper.tsx +11 -6
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/MultiSelectFilter.tsx +89 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/NumericRangeFilter.tsx +108 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/SelectFilter.tsx +46 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/TextFilter.tsx +52 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/CmsResultRow.tsx +101 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/DefaultResultRow.tsx +109 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/resolveResultRenderer.ts +49 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/config.json +88 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/constants.ts +8 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_ → search}/hooks/useAsyncData.ts +3 -10
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useDistinctValues.ts +59 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts +966 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/index.ts +115 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/loadConfig.ts +14 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/queryBuilder.ts +100 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/types.ts +376 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/debounce.ts +17 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/fieldUtils.ts +14 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +285 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/sortUtils.ts +34 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/graphqlClient.ts +24 -8
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/appLayout.tsx +7 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/CustomerWebClientChat.tsx +74 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/components → components}/ObjectBreadcrumb.tsx +7 -2
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/button.tsx +1 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/datePicker.tsx +2 -2
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/table.tsx +9 -2
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/index.ts +8 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +53 -18
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/Home.tsx +3 -27
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/TestChatPage.tsx +26 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/routes.tsx +21 -8
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/types/chat.ts +36 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/types/globals.d.ts +23 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/utils/fieldUtils.ts → utils/accountFields.ts} +9 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/tsconfig.json +2 -2
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig.cls +133 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig.cls-meta.xml +5 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig_Test.cls +101 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig_Test.cls-meta.xml +5 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/package.xml +4 -0
- package/lib/templates/project/reactexternalapp/package.json +8 -2
- package/lib/templates/project/reactexternalapp/scripts/org-setup-config-schema.mjs +120 -0
- package/lib/templates/project/reactexternalapp/scripts/org-setup-dev.mjs +87 -0
- package/lib/templates/project/reactexternalapp/scripts/org-setup-utils.mjs +569 -0
- package/lib/templates/project/reactexternalapp/scripts/org-setup-xml.mjs +182 -0
- package/lib/templates/project/reactexternalapp/scripts/org-setup.config.json +6 -2
- package/lib/templates/project/reactexternalapp/scripts/org-setup.mjs +1509 -328
- package/lib/templates/project/reactexternalapp/scripts/validate-org-setup-config.mjs +38 -0
- package/lib/templates/project/reactinternalapp/CHANGELOG.md +1223 -0
- package/lib/templates/project/reactinternalapp/PRD.md +35 -0
- package/lib/templates/project/reactinternalapp/README.md +14 -10
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package-lock.json +813 -538
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package.json +10 -9
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/README.md +246 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/apiUtils.ts +56 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/orgApiVersionService.ts +37 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/searchableContentTypesService.ts +116 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/channelResolver.ts +40 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/cmsQueryFragment.ts +97 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeSessionCache.ts +134 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeUtils.ts +38 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/hooks/useSearchableContentTypes.ts +128 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/index.ts +54 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/parseResponse.ts +65 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/searchChannel.ts +15 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/types.ts +66 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/registry.ts +38 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/index.ts +19 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/parseResponse.ts +56 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/queryFragment.ts +141 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts +101 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/distinctValuesService.ts +84 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/searchService.ts +136 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/GlobalSearchBox.tsx +98 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.tsx +294 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/Search.tsx +203 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.tsx +88 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.tsx +187 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/PaginationControls.tsx +185 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/ScopeSelector.tsx +107 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SearchBar.tsx +55 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SortControl.tsx +62 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/ActiveFilters.tsx +61 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.tsx +125 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_/components → search/components/filters}/FilterContext.tsx +7 -20
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/BooleanFilter.tsx +50 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/DateRangeFilter.tsx +160 -0
- package/lib/templates/project/{reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters → reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs}/FilterFieldWrapper.tsx +11 -6
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/MultiSelectFilter.tsx +89 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/NumericRangeFilter.tsx +108 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/SelectFilter.tsx +46 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/TextFilter.tsx +52 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/CmsResultRow.tsx +101 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/DefaultResultRow.tsx +109 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/resolveResultRenderer.ts +49 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/config.json +88 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/constants.ts +8 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_ → search}/hooks/useAsyncData.ts +3 -10
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useDistinctValues.ts +59 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts +966 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/index.ts +115 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/loadConfig.ts +14 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/queryBuilder.ts +100 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/types.ts +376 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/debounce.ts +17 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/fieldUtils.ts +14 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +285 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/sortUtils.ts +34 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/AgentforceConversationClient.tsx +16 -132
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/components → components}/ObjectBreadcrumb.tsx +7 -2
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ui/button.tsx +1 -1
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ui/datePicker.tsx +2 -2
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ui/table.tsx +9 -2
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +49 -16
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/Home.tsx +3 -27
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/routes.tsx +12 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/utils/fieldUtils.ts → utils/accountFields.ts} +9 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/tsconfig.json +2 -2
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -0
- package/lib/templates/project/reactinternalapp/package.json +4 -2
- package/lib/templates/project/reactinternalapp/scripts/org-setup-config-schema.mjs +24 -0
- package/lib/templates/project/reactinternalapp/scripts/org-setup-dev.mjs +87 -0
- package/lib/templates/project/reactinternalapp/scripts/org-setup-utils.mjs +569 -0
- package/lib/templates/project/reactinternalapp/scripts/org-setup-xml.mjs +182 -0
- package/lib/templates/project/reactinternalapp/scripts/org-setup.mjs +1162 -214
- package/lib/templates/uiBundles/angularbasic/angular.json +1 -1
- package/lib/templates/uiBundles/angularbasic/eslint-plugin-angular-graphql.js +148 -0
- package/lib/templates/uiBundles/angularbasic/eslint.config.js +9 -1
- package/lib/templates/uiBundles/angularbasic/package-lock.json +680 -408
- package/lib/templates/uiBundles/angularbasic/package.json +2 -2
- package/lib/templates/uiBundles/reactbasic/package-lock.json +738 -440
- package/lib/utils/template-placeholders.js +14 -0
- package/lib/utils/template-placeholders.js.map +1 -1
- package/lib/utils/uiBundleTemplateUtils.d.ts +5 -1
- package/lib/utils/uiBundleTemplateUtils.js +7 -1
- package/lib/utils/uiBundleTemplateUtils.js.map +1 -1
- package/package.json +7 -7
- package/lib/templates/project/reactexternalapp/AGENT.md +0 -252
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/accountSearchService.ts +0 -46
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountIndustries.graphql +0 -19
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountTypes.graphql +0 -19
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/getAccountDetail.graphql +0 -121
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/searchAccounts.graphql +0 -51
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountObjectDetailPage.tsx +0 -355
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountSearch.tsx +0 -304
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/Home.tsx +0 -34
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/api/objectSearchService.ts +0 -82
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/ActiveFilters.tsx +0 -89
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/PaginationControls.tsx +0 -109
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SearchBar.tsx +0 -41
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SortControl.tsx +0 -143
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/BooleanFilter.tsx +0 -78
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateFilter.tsx +0 -128
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateRangeFilter.tsx +0 -70
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/MultiSelectFilter.tsx +0 -97
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/NumericRangeFilter.tsx +0 -167
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SearchFilter.tsx +0 -52
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SelectFilter.tsx +0 -97
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/TextFilter.tsx +0 -93
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useDebouncedCallback.ts +0 -34
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useObjectSearchParams.ts +0 -254
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/debounce.ts +0 -25
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/filterUtils.ts +0 -404
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/sortUtils.ts +0 -38
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/accountSearchService.ts +0 -46
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountIndustries.graphql +0 -19
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountTypes.graphql +0 -19
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/query/searchAccounts.graphql +0 -51
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/graphql-operations-types.ts +0 -11260
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/AccountSearch.tsx +0 -297
- package/lib/templates/project/reactexternalapp/scripts/prepare-import-unique-fields.js +0 -122
- package/lib/templates/project/reactinternalapp/AGENT.md +0 -252
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/accountSearchService.ts +0 -46
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountIndustries.graphql +0 -19
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountTypes.graphql +0 -19
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/getAccountDetail.graphql +0 -121
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/searchAccounts.graphql +0 -51
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountObjectDetailPage.tsx +0 -355
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountSearch.tsx +0 -304
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/Home.tsx +0 -34
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/api/objectSearchService.ts +0 -82
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/ActiveFilters.tsx +0 -89
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/PaginationControls.tsx +0 -109
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SearchBar.tsx +0 -41
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SortControl.tsx +0 -143
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/BooleanFilter.tsx +0 -78
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateFilter.tsx +0 -128
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateRangeFilter.tsx +0 -70
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/MultiSelectFilter.tsx +0 -97
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/NumericRangeFilter.tsx +0 -167
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SearchFilter.tsx +0 -52
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SelectFilter.tsx +0 -97
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/TextFilter.tsx +0 -93
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useDebouncedCallback.ts +0 -34
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useObjectSearchParams.ts +0 -254
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/debounce.ts +0 -25
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/filterUtils.ts +0 -404
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/sortUtils.ts +0 -38
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/accountSearchService.ts +0 -46
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountIndustries.graphql +0 -19
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountTypes.graphql +0 -19
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/searchAccounts.graphql +0 -51
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/graphql-operations-types.ts +0 -11260
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountSearch.tsx +0 -297
- package/lib/templates/project/reactinternalapp/scripts/prepare-import-unique-fields.js +0 -122
- /package/lib/templates/project/angularextapp/_p_/_m_/{permissionsets → _ps_}/angularextapp_Guest_User_Api_Access.permissionset-meta.xml +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/{permissionsets → _ps_}/angularintapp_Access.permissionset-meta.xml +0 -0
- /package/lib/templates/project/reactinternalapp/_p_/_m_/{permissionsets → _ps_}/reactinternalapp_Access.permissionset-meta.xml +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SObject fragment builder.
|
|
3
|
+
*
|
|
4
|
+
* Extracted verbatim from `queryBuilder.ts`: `buildSelectionBody`,
|
|
5
|
+
* `renderField`, `buildSourceWhere`, `assertValidKey`, `KEY_PATTERN`, and the
|
|
6
|
+
* per-source variable/selection assembly. Emits one `QueryFragmentContribution`
|
|
7
|
+
* with `placement: "uiapi.query"` — the `${key}: ${objectName}(...) { … }`
|
|
8
|
+
* selection, its 4 per-source variable declarations, and their 4 values.
|
|
9
|
+
*
|
|
10
|
+
* Behaviour is byte-for-byte equivalent to the original inline logic; this is a
|
|
11
|
+
* move, not a rewrite.
|
|
12
|
+
*
|
|
13
|
+
* Selection sets are generated from `displayFields`:
|
|
14
|
+
* - string `"Name"` → `Name @optional { value displayValue }`
|
|
15
|
+
* - `{ name, raw: true }` → `Name`
|
|
16
|
+
* - `{ name, subfields }` → nested `Name @optional { ... }`
|
|
17
|
+
*
|
|
18
|
+
* The `idField` (default "Id") is always emitted; it is **not** wrapped
|
|
19
|
+
* because uiapi's Id is a scalar.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
buildFilter,
|
|
24
|
+
buildGlobalQueryClause,
|
|
25
|
+
type ActiveFilterValue,
|
|
26
|
+
} from "../../utils/filterUtils";
|
|
27
|
+
import { buildOrderBy } from "../../utils/sortUtils";
|
|
28
|
+
import type { DisplayField, SObjectSourceConfig } from "../../types";
|
|
29
|
+
import type { QueryFragmentContribution, SourceRequest } from "../types";
|
|
30
|
+
|
|
31
|
+
const KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
32
|
+
|
|
33
|
+
function assertValidKey(key: string): void {
|
|
34
|
+
if (!KEY_PATTERN.test(key)) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Invalid source key "${key}". Must match /^[A-Za-z_][A-Za-z0-9_]*$/ to be a valid GraphQL alias and variable prefix.`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Combines a per-source structured-filter clause with the global-q `or` clause.
|
|
43
|
+
* Returns `undefined` when neither side has any constraints.
|
|
44
|
+
*/
|
|
45
|
+
function buildSourceWhere(
|
|
46
|
+
source: SObjectSourceConfig,
|
|
47
|
+
q: string,
|
|
48
|
+
filters: ActiveFilterValue[],
|
|
49
|
+
): unknown {
|
|
50
|
+
const clauses: unknown[] = [];
|
|
51
|
+
const globalClause = buildGlobalQueryClause(q, source.searchableFields);
|
|
52
|
+
if (globalClause) clauses.push(globalClause);
|
|
53
|
+
const structured = buildFilter(filters, source.filterBy ?? []);
|
|
54
|
+
if (structured) clauses.push(structured);
|
|
55
|
+
if (clauses.length === 0) return undefined;
|
|
56
|
+
if (clauses.length === 1) return clauses[0];
|
|
57
|
+
return { and: clauses };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function buildSelectionBody(source: SObjectSourceConfig): string {
|
|
61
|
+
const idField = source.idField ?? "Id";
|
|
62
|
+
const fieldLines = [` ${idField}`];
|
|
63
|
+
for (const field of source.displayFields) {
|
|
64
|
+
fieldLines.push(...renderField(field, " "));
|
|
65
|
+
}
|
|
66
|
+
return [
|
|
67
|
+
` edges { node {`,
|
|
68
|
+
...fieldLines,
|
|
69
|
+
` } }`,
|
|
70
|
+
` pageInfo { hasNextPage hasPreviousPage startCursor endCursor }`,
|
|
71
|
+
` totalCount`,
|
|
72
|
+
].join("\n");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function renderField(field: DisplayField, indentStr: string): string[] {
|
|
76
|
+
if (typeof field === "string") {
|
|
77
|
+
return [`${indentStr}${field} @optional { value displayValue }`];
|
|
78
|
+
}
|
|
79
|
+
if ("raw" in field) {
|
|
80
|
+
return [`${indentStr}${field.name}`];
|
|
81
|
+
}
|
|
82
|
+
const lines = [`${indentStr}${field.name} @optional {`];
|
|
83
|
+
for (const sub of field.subfields) {
|
|
84
|
+
lines.push(...renderField(sub, indentStr + " "));
|
|
85
|
+
}
|
|
86
|
+
lines.push(`${indentStr}}`);
|
|
87
|
+
return lines;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Builds one SObject source's `QueryFragmentContribution`.
|
|
92
|
+
*
|
|
93
|
+
* The fragment is the `${key}: ${objectName}(...) { … }` selection that lives
|
|
94
|
+
* inside `uiapi.query`; the 4 declarations/values are the per-source
|
|
95
|
+
* `first / after / where / orderBy` variables. Output matches the original
|
|
96
|
+
* inline builder exactly.
|
|
97
|
+
*/
|
|
98
|
+
export function buildSObjectFragment(
|
|
99
|
+
request: SourceRequest<SObjectSourceConfig>,
|
|
100
|
+
): QueryFragmentContribution {
|
|
101
|
+
const { source } = request;
|
|
102
|
+
assertValidKey(source.key);
|
|
103
|
+
|
|
104
|
+
const firstVar = `${source.key}_first`;
|
|
105
|
+
const afterVar = `${source.key}_after`;
|
|
106
|
+
const whereVar = `${source.key}_where`;
|
|
107
|
+
const orderByVar = `${source.key}_orderBy`;
|
|
108
|
+
const whereType = source.whereTypeName ?? `${source.objectName}_Filter`;
|
|
109
|
+
const orderByType = source.orderByTypeName ?? `${source.objectName}_OrderBy`;
|
|
110
|
+
|
|
111
|
+
const variableDeclarations = [
|
|
112
|
+
`$${firstVar}: Int`,
|
|
113
|
+
`$${afterVar}: String`,
|
|
114
|
+
`$${whereVar}: ${whereType}`,
|
|
115
|
+
`$${orderByVar}: ${orderByType}`,
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
const fragment =
|
|
119
|
+
`${source.key}: ${source.objectName}(` +
|
|
120
|
+
`first: $${firstVar}, ` +
|
|
121
|
+
`after: $${afterVar}, ` +
|
|
122
|
+
`where: $${whereVar}, ` +
|
|
123
|
+
`orderBy: $${orderByVar}` +
|
|
124
|
+
`) {\n` +
|
|
125
|
+
buildSelectionBody(source) +
|
|
126
|
+
`\n}`;
|
|
127
|
+
|
|
128
|
+
const variables: Record<string, unknown> = {
|
|
129
|
+
[firstVar]: request.pageSize,
|
|
130
|
+
[afterVar]: request.afterCursor ?? null,
|
|
131
|
+
[whereVar]: buildSourceWhere(source, request.q, request.filters) ?? null,
|
|
132
|
+
[orderByVar]: buildOrderBy(request.sort) ?? null,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
variableDeclarations,
|
|
137
|
+
variables,
|
|
138
|
+
placement: "uiapi.query",
|
|
139
|
+
fragment,
|
|
140
|
+
};
|
|
141
|
+
}
|
package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kind-agnostic adapter contracts for unified (multi-source) search.
|
|
3
|
+
*
|
|
4
|
+
* Every searchable backend ("sobject", "cms", …) is fronted by a
|
|
5
|
+
* {@link SourceAdapter}. The core query builder and result parser are
|
|
6
|
+
* completely kind-agnostic: they dispatch to the adapter registered for a
|
|
7
|
+
* source's `kind`, so a new backend plugs in by adding a `kind` + an adapter,
|
|
8
|
+
* never by touching the common builder/parser or any consumer.
|
|
9
|
+
*
|
|
10
|
+
* - {@link SourceAdapter.buildRequest} contributes a {@link QueryFragmentContribution}
|
|
11
|
+
* (its GraphQL fragment, variable declarations, and variable values) that the
|
|
12
|
+
* builder assembles into one combined document.
|
|
13
|
+
* - {@link SourceAdapter.parseResponse} receives the full response `data` root
|
|
14
|
+
* and extracts this source's `SourceResult`, returning `null` (never
|
|
15
|
+
* throwing) when its own subtree is absent or errored — this is what lets
|
|
16
|
+
* sources fail independently (partial-failure resilience).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { SourceConfig, SourceResult } from "../types";
|
|
20
|
+
import type { ActiveFilterValue } from "../utils/filterUtils";
|
|
21
|
+
import type { SortState } from "../utils/sortUtils";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* One source's contribution to the combined GraphQL document.
|
|
25
|
+
*
|
|
26
|
+
* The builder concatenates every source's `variableDeclarations` into the
|
|
27
|
+
* `query Search(...)` header and merges every source's `variables` into one
|
|
28
|
+
* flat map. Fragments are placed by `placement`:
|
|
29
|
+
* - `"uiapi.query"` → inside the shared `uiapi { query { … } }` block.
|
|
30
|
+
* - `"root"` → a sibling of `uiapi` at the document root (e.g. the CMS
|
|
31
|
+
* `managed_content { … }` block).
|
|
32
|
+
*
|
|
33
|
+
* A contribution MAY declare zero extra variables (e.g. the CMS bootstrap call
|
|
34
|
+
* omits `$cmsContentTypeFQNs` entirely). The builder handles declaration lists of
|
|
35
|
+
* differing lengths, so an unused/undeclared variable is never emitted.
|
|
36
|
+
*/
|
|
37
|
+
export interface QueryFragmentContribution {
|
|
38
|
+
/** Namespaced GraphQL variable declarations, e.g. `"$cmsKeyword: String!"`. */
|
|
39
|
+
variableDeclarations: string[];
|
|
40
|
+
/** Values for the declared variables, keyed by (unprefixed) variable name. */
|
|
41
|
+
variables: Record<string, unknown>;
|
|
42
|
+
/** Where the `fragment` is spliced into the combined document. */
|
|
43
|
+
placement: "uiapi.query" | "root";
|
|
44
|
+
/** The GraphQL selection fragment (unindented; the builder indents it). */
|
|
45
|
+
fragment: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A per-source search request. Generic over the source config so an adapter can
|
|
50
|
+
* narrow to its own config kind (e.g. `SourceRequest<CmsSourceConfig>`).
|
|
51
|
+
*
|
|
52
|
+
* The base fields (`source`/`q`/`filters`/`sort`/`pageSize`/`afterCursor`) are
|
|
53
|
+
* kind-agnostic. The trailing fields are optional and only populated for the
|
|
54
|
+
* kinds that need them; the sync query builder never resolves async values, so
|
|
55
|
+
* anything async (e.g. the CMS UIBundle id) is resolved by the caller and
|
|
56
|
+
* threaded in here.
|
|
57
|
+
*/
|
|
58
|
+
export interface SourceRequest<T extends SourceConfig = SourceConfig> {
|
|
59
|
+
/** The config for the source this request targets. */
|
|
60
|
+
source: T;
|
|
61
|
+
/** The global search term, broadcast to every source. */
|
|
62
|
+
q: string;
|
|
63
|
+
/** Active structured filters (SObject-relevant; other kinds may ignore). */
|
|
64
|
+
filters: ActiveFilterValue[];
|
|
65
|
+
/** Active sort (SObject-relevant; other kinds may ignore). */
|
|
66
|
+
sort: SortState | null;
|
|
67
|
+
/** Page size for this fetch (validated against `pageSizeOptions` upstream). */
|
|
68
|
+
pageSize: number;
|
|
69
|
+
/** Opaque forward cursor for this fetch (CMS decodes it to an offset). */
|
|
70
|
+
afterCursor: string | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* CMS-only, optional. Present (non-null, non-empty) only when content types
|
|
73
|
+
* are known/selected; absent on the bootstrap call so the CMS adapter omits
|
|
74
|
+
* `$cmsContentTypeFQNs` entirely.
|
|
75
|
+
*/
|
|
76
|
+
contentTypes?: string[] | null;
|
|
77
|
+
/**
|
|
78
|
+
* CMS-only, optional. The resolved UIBundle id fed into the CMS search
|
|
79
|
+
* query. Resolved by the caller (async `getUIBundleId()`) BEFORE the sync
|
|
80
|
+
* query builder runs, then threaded in here so `buildRequest` stays
|
|
81
|
+
* synchronous. Ignored by non-CMS adapters.
|
|
82
|
+
*/
|
|
83
|
+
uiBundleId?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Runtime adapter for one source `kind`. Bridges a kind-agnostic
|
|
88
|
+
* {@link SourceRequest} to a GraphQL fragment and back to a {@link SourceResult}.
|
|
89
|
+
*/
|
|
90
|
+
export interface SourceAdapter<T extends SourceConfig = SourceConfig> {
|
|
91
|
+
/** The `kind` discriminator this adapter handles. */
|
|
92
|
+
readonly kind: T["kind"];
|
|
93
|
+
/** Build this source's fragment/variables contribution for the combined doc. */
|
|
94
|
+
buildRequest(request: SourceRequest<T>): QueryFragmentContribution;
|
|
95
|
+
/**
|
|
96
|
+
* Extract this source's result from the full response `data` root. Returns
|
|
97
|
+
* `null` when the source's own subtree is absent or errored — never throws
|
|
98
|
+
* for a partial failure.
|
|
99
|
+
*/
|
|
100
|
+
parseResponse(root: Record<string, unknown>, request: SourceRequest<T>): SourceResult | null;
|
|
101
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches distinct values for a picklist field via the uiapi aggregate
|
|
3
|
+
* `groupBy` query.
|
|
4
|
+
*
|
|
5
|
+
* Used by the auto-render pathway when a picklist filter does not declare
|
|
6
|
+
* inline `options`. Each call hits the GraphQL endpoint once and returns the
|
|
7
|
+
* sorted set of distinct values plus their display labels.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createDataSDK } from "@salesforce/platform-sdk";
|
|
11
|
+
|
|
12
|
+
export interface PicklistOption {
|
|
13
|
+
value: string;
|
|
14
|
+
label: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
18
|
+
|
|
19
|
+
function assertValidIdentifier(name: string, kind: string): void {
|
|
20
|
+
if (!KEY_PATTERN.test(name)) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Invalid ${kind} "${name}". Must match /^[A-Za-z_][A-Za-z0-9_]*$/ to be a valid GraphQL identifier.`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Builds and executes a `uiapi.aggregate.<objectName>(groupBy: { <field>: { group: true } })`
|
|
29
|
+
* query and extracts the distinct values.
|
|
30
|
+
*/
|
|
31
|
+
export async function fetchDistinctValues(
|
|
32
|
+
objectName: string,
|
|
33
|
+
fieldName: string,
|
|
34
|
+
): Promise<PicklistOption[]> {
|
|
35
|
+
assertValidIdentifier(objectName, "objectName");
|
|
36
|
+
assertValidIdentifier(fieldName, "fieldName");
|
|
37
|
+
|
|
38
|
+
const document = `query DistinctValues {
|
|
39
|
+
uiapi {
|
|
40
|
+
aggregate {
|
|
41
|
+
${objectName}(groupBy: { ${fieldName}: { group: true } }) {
|
|
42
|
+
edges {
|
|
43
|
+
node {
|
|
44
|
+
aggregate @optional {
|
|
45
|
+
${fieldName} @optional {
|
|
46
|
+
value
|
|
47
|
+
displayValue
|
|
48
|
+
label
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}`;
|
|
57
|
+
|
|
58
|
+
const data = await createDataSDK();
|
|
59
|
+
const response = await data.graphql!.query<unknown>({ query: document });
|
|
60
|
+
|
|
61
|
+
if (response.errors?.length) {
|
|
62
|
+
throw new Error(response.errors.map((e) => e.message).join("; "));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const root = response.data as Record<string, unknown> | undefined;
|
|
66
|
+
const aggregate = (root?.uiapi as Record<string, unknown> | undefined)?.aggregate as
|
|
67
|
+
| Record<string, unknown>
|
|
68
|
+
| undefined;
|
|
69
|
+
const objectAgg = aggregate?.[objectName] as
|
|
70
|
+
| { edges?: Array<{ node?: { aggregate?: Record<string, unknown> } }> }
|
|
71
|
+
| undefined;
|
|
72
|
+
const edges = objectAgg?.edges ?? [];
|
|
73
|
+
|
|
74
|
+
return edges
|
|
75
|
+
.map((edge) => {
|
|
76
|
+
const field = edge?.node?.aggregate?.[fieldName] as
|
|
77
|
+
| { value?: string | null; displayValue?: string | null; label?: string | null }
|
|
78
|
+
| undefined;
|
|
79
|
+
const value = field?.value;
|
|
80
|
+
if (value == null || value === "") return null;
|
|
81
|
+
return { value, label: field?.label ?? field?.displayValue ?? value };
|
|
82
|
+
})
|
|
83
|
+
.filter((opt): opt is PicklistOption => opt !== null);
|
|
84
|
+
}
|
package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/searchService.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executes a combined search request and parses each source's subtree via its
|
|
3
|
+
* adapter, with partial-failure resilience.
|
|
4
|
+
*
|
|
5
|
+
* The combined GraphQL response can carry BOTH partial `data` AND `errors`
|
|
6
|
+
* (`uiapi` and CMS `managed_content` are independent roots). Rather than
|
|
7
|
+
* throwing on `response.errors`, we parse each source through
|
|
8
|
+
* `getAdapter(kind).parseResponse(root, req)`:
|
|
9
|
+
* - a `SourceResult` → recorded in `results[key]`.
|
|
10
|
+
* - `null` (subtree absent/errored) → the source produced no data; if the
|
|
11
|
+
* request as a whole reported GraphQL errors, an attributed message is
|
|
12
|
+
* recorded in `errors[key]` so the UI can show a per-source error while
|
|
13
|
+
* sibling sources still render.
|
|
14
|
+
*
|
|
15
|
+
* Only a whole-request failure — the SDK call rejecting, or a response with no
|
|
16
|
+
* `data` at all — throws, so `useSearch` surfaces it as a page-level error
|
|
17
|
+
* (preserving the original network-failure behaviour).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { createDataSDK } from "@salesforce/platform-sdk";
|
|
21
|
+
import { buildSearchQuery } from "../queryBuilder";
|
|
22
|
+
import { getAdapter } from "../adapters/registry";
|
|
23
|
+
import type { SourceRequest } from "../adapters/types";
|
|
24
|
+
import type { SourceResult } from "../types";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The outcome of a combined `runSearch`: successfully-parsed results plus a
|
|
28
|
+
* per-source error map. `errors` is keyed by `source.key` and only carries an
|
|
29
|
+
* entry for a source that failed to produce data (its subtree was absent or
|
|
30
|
+
* errored). A source appears in at most one of `results` / `errors`.
|
|
31
|
+
*/
|
|
32
|
+
export interface RunSearchOutcome {
|
|
33
|
+
results: Record<string, SourceResult>;
|
|
34
|
+
errors: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Minimal shape of a GraphQL error we can attribute to a source by path. */
|
|
38
|
+
interface GraphQLErrorLike {
|
|
39
|
+
message?: string | null;
|
|
40
|
+
path?: Array<string | number> | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Attributes GraphQL errors to a source by matching the error `path` against
|
|
45
|
+
* the source's location in the document:
|
|
46
|
+
* - SObject aliases live under `uiapi.query.<key>`.
|
|
47
|
+
* - CMS lives under the root `managed_content` block.
|
|
48
|
+
*
|
|
49
|
+
* Returns the joined messages whose `path` matches `request`. When NO path
|
|
50
|
+
* matches (or paths are absent), returns a GENERIC per-source message rather
|
|
51
|
+
* than echoing unrelated sources' messages — otherwise one source could display
|
|
52
|
+
* another's error text (over-broad cross-attribution). The whole-request
|
|
53
|
+
* (`data == null`) path still joins ALL error messages; that lives in
|
|
54
|
+
* `runSearch` and is unaffected.
|
|
55
|
+
*/
|
|
56
|
+
function attributeError(request: SourceRequest, errors: GraphQLErrorLike[]): string {
|
|
57
|
+
const kind = request.source.kind;
|
|
58
|
+
const key = request.source.key;
|
|
59
|
+
|
|
60
|
+
const matched = errors.filter((e) => {
|
|
61
|
+
const path = e.path;
|
|
62
|
+
if (!path || path.length === 0) return false;
|
|
63
|
+
if (kind === "cms") {
|
|
64
|
+
return path[0] === "managed_content";
|
|
65
|
+
}
|
|
66
|
+
// SObject: uiapi -> query -> <key>
|
|
67
|
+
return path[0] === "uiapi" && path[1] === "query" && path[2] === key;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// No path matched this source: don't leak sibling sources' messages — fall
|
|
71
|
+
// back to a generic per-source explanation.
|
|
72
|
+
if (matched.length === 0) {
|
|
73
|
+
return "Search failed for this source.";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const message = matched
|
|
77
|
+
.map((e) => e.message)
|
|
78
|
+
.filter((m): m is string => !!m)
|
|
79
|
+
.join("; ");
|
|
80
|
+
return message || "Search failed for this source.";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Runs one combined multi-source GraphQL request. Returns `{ results, errors }`
|
|
85
|
+
* (never throws on partial GraphQL errors); throws only on a whole-request /
|
|
86
|
+
* network failure.
|
|
87
|
+
*/
|
|
88
|
+
export async function runSearch(requests: SourceRequest[]): Promise<RunSearchOutcome> {
|
|
89
|
+
const { document, variables } = buildSearchQuery(requests);
|
|
90
|
+
|
|
91
|
+
const data = await createDataSDK();
|
|
92
|
+
const response = await data.graphql!.query<unknown, Record<string, unknown>>({
|
|
93
|
+
query: document,
|
|
94
|
+
variables,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const root = response.data as Record<string, unknown> | undefined;
|
|
98
|
+
const gqlErrors = (response.errors ?? []) as GraphQLErrorLike[];
|
|
99
|
+
|
|
100
|
+
// Whole-request failure: no data at all. Surface as a page-level error
|
|
101
|
+
// (preserves the original network-failure behaviour).
|
|
102
|
+
if (root == null) {
|
|
103
|
+
const message = gqlErrors
|
|
104
|
+
.map((e) => e.message)
|
|
105
|
+
.filter((m): m is string => !!m)
|
|
106
|
+
.join("; ");
|
|
107
|
+
throw new Error(message || "Unified search failed");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const results: Record<string, SourceResult> = {};
|
|
111
|
+
const errors: Record<string, string> = {};
|
|
112
|
+
|
|
113
|
+
for (const request of requests) {
|
|
114
|
+
const key = request.source.key;
|
|
115
|
+
let parsed: SourceResult | null = null;
|
|
116
|
+
try {
|
|
117
|
+
parsed = getAdapter(request.source.kind).parseResponse(root, request);
|
|
118
|
+
} catch {
|
|
119
|
+
// An adapter that throws while parsing its own subtree is a
|
|
120
|
+
// per-source failure, not a page-level one.
|
|
121
|
+
parsed = null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (parsed != null) {
|
|
125
|
+
results[key] = parsed;
|
|
126
|
+
} else if (gqlErrors.length > 0) {
|
|
127
|
+
// No data for this source AND the request reported errors — attribute
|
|
128
|
+
// a message so the UI can show a per-source error.
|
|
129
|
+
errors[key] = attributeError(request, gqlErrors);
|
|
130
|
+
}
|
|
131
|
+
// else: no data and no errors — the source simply returned nothing;
|
|
132
|
+
// leave it absent from both maps (empty result, no error).
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return { results, errors };
|
|
136
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GlobalSearchBox — pure launcher component for global search.
|
|
3
|
+
*
|
|
4
|
+
* A self-contained search input + button that navigates to `/search?q=...`
|
|
5
|
+
* when the user submits a query of at least MIN_QUERY_LENGTH characters.
|
|
6
|
+
*
|
|
7
|
+
* Props are purely cosmetic (className, placeholder, autoFocus) — all behavior
|
|
8
|
+
* (the gate, the route, the query key) is owned by this component, not the caller.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```tsx
|
|
12
|
+
* import { GlobalSearchBox } from "../features/search";
|
|
13
|
+
* <GlobalSearchBox className="mb-8" placeholder="Search everything..." />
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Search } from "lucide-react";
|
|
18
|
+
import { useId, useState } from "react";
|
|
19
|
+
import { useNavigate } from "react-router";
|
|
20
|
+
import { Input } from "../../../components/ui/input";
|
|
21
|
+
import { Button } from "../../../components/ui/button";
|
|
22
|
+
import { GLOBAL_QUERY_KEY } from "../utils/filterUtils";
|
|
23
|
+
import { MIN_QUERY_LENGTH } from "../constants";
|
|
24
|
+
|
|
25
|
+
export interface GlobalSearchBoxProps {
|
|
26
|
+
/** Optional CSS class for container styling/placement. */
|
|
27
|
+
className?: string;
|
|
28
|
+
/** Placeholder text for the search input. */
|
|
29
|
+
placeholder?: string;
|
|
30
|
+
/** Whether the input should autofocus on mount. */
|
|
31
|
+
autoFocus?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Accessible (screen-reader) label for the input. Exposed as a prop so
|
|
34
|
+
* callers can localize it; defaults to English. */
|
|
35
|
+
label?: string;
|
|
36
|
+
/** Visible text for the submit button. Exposed for localization. */
|
|
37
|
+
submitLabel?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Minimum length of keyword required by search. `{min}` is replaced with
|
|
40
|
+
* {@link MIN_QUERY_LENGTH}. Exposed for localization. */
|
|
41
|
+
minLengthHint?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function GlobalSearchBox({
|
|
45
|
+
className,
|
|
46
|
+
placeholder = "Search…",
|
|
47
|
+
autoFocus,
|
|
48
|
+
label = "Search",
|
|
49
|
+
submitLabel = "Search",
|
|
50
|
+
minLengthHint = "Enter at least {min} characters to search.",
|
|
51
|
+
}: GlobalSearchBoxProps) {
|
|
52
|
+
const navigate = useNavigate();
|
|
53
|
+
const [text, setText] = useState("");
|
|
54
|
+
const inputId = useId();
|
|
55
|
+
const hintId = useId();
|
|
56
|
+
|
|
57
|
+
const canSearch = text.trim().length >= MIN_QUERY_LENGTH;
|
|
58
|
+
const hintText = minLengthHint.replace("{min}", String(MIN_QUERY_LENGTH));
|
|
59
|
+
|
|
60
|
+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
if (!canSearch) return;
|
|
63
|
+
const params = `?${GLOBAL_QUERY_KEY}=${encodeURIComponent(text.trim())}`;
|
|
64
|
+
navigate(`/search${params}`);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<form onSubmit={handleSubmit} className={className} role="search">
|
|
69
|
+
<label htmlFor={inputId} className="sr-only">
|
|
70
|
+
{label}
|
|
71
|
+
</label>
|
|
72
|
+
<div className="flex gap-2">
|
|
73
|
+
<div className="relative flex-1">
|
|
74
|
+
<Search
|
|
75
|
+
aria-hidden="true"
|
|
76
|
+
className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground"
|
|
77
|
+
/>
|
|
78
|
+
<Input
|
|
79
|
+
id={inputId}
|
|
80
|
+
type="text"
|
|
81
|
+
value={text}
|
|
82
|
+
onChange={(e) => setText(e.target.value)}
|
|
83
|
+
placeholder={placeholder}
|
|
84
|
+
autoFocus={autoFocus}
|
|
85
|
+
className="pl-9"
|
|
86
|
+
aria-describedby={hintId}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
<Button type="submit" disabled={!canSearch}>
|
|
90
|
+
{submitLabel}
|
|
91
|
+
</Button>
|
|
92
|
+
</div>
|
|
93
|
+
<p id={hintId} className="sr-only">
|
|
94
|
+
{hintText}
|
|
95
|
+
</p>
|
|
96
|
+
</form>
|
|
97
|
+
);
|
|
98
|
+
}
|