@salesforce/templates 66.14.0 → 66.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/generators/uiEmbeddingGenerator.js +6 -6
- package/lib/generators/uiEmbeddingGenerator.js.map +1 -1
- package/lib/i18n/i18n.d.ts +2 -2
- package/lib/i18n/i18n.js +2 -2
- package/lib/i18n/i18n.js.map +1 -1
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package-lock.json +446 -344
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package-lock.json +449 -347
- package/lib/templates/project/reactexternalapp/CHANGELOG.md +437 -0
- package/lib/templates/project/reactexternalapp/README.md +2 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package-lock.json +675 -428
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package.json +6 -4
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/patches/aria-hidden+1.2.6.patch +45 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/Login.tsx +3 -2
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/Register.tsx +3 -2
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/searchableContentTypesService.ts +9 -6
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/cmsQueryFragment.ts +14 -3
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeUtils.ts +7 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/index.ts +1 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/searchChannel.ts +17 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts +2 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.tsx +16 -4
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.tsx +1 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.tsx +27 -4
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts +12 -7
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +14 -4
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/appLayout.tsx +42 -7
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ObjectBreadcrumb.tsx +1 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +34 -47
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/Home.tsx +3 -2
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/utils/accountFields.ts +1 -14
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleAuthUtils_Test.cls +141 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleAuthUtils_Test.cls-meta.xml +5 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig.cls +23 -11
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig_Test.cls +80 -0
- package/lib/templates/project/reactexternalapp/package.json +1 -1
- package/lib/templates/project/reactexternalapp/scripts/org-setup-config-schema.mjs +14 -2
- package/lib/templates/project/reactexternalapp/scripts/org-setup-dev.mjs +27 -0
- package/lib/templates/project/reactexternalapp/scripts/org-setup-url.mjs +108 -0
- package/lib/templates/project/reactexternalapp/scripts/org-setup-xml.mjs +96 -0
- package/lib/templates/project/reactexternalapp/scripts/org-setup.mjs +195 -3
- package/lib/templates/project/reactinternalapp/CHANGELOG.md +533 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package-lock.json +676 -429
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package.json +7 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/patches/aria-hidden+1.2.6.patch +45 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/searchableContentTypesService.ts +9 -6
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/cmsQueryFragment.ts +14 -3
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeUtils.ts +7 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/index.ts +1 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/searchChannel.ts +17 -1
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts +2 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.tsx +16 -4
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.tsx +1 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.tsx +27 -4
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.tsx +7 -1
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts +12 -7
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +24 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/appLayout.tsx +41 -7
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ObjectBreadcrumb.tsx +1 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +40 -50
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/Home.tsx +7 -2
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/utils/accountFields.ts +1 -14
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -1
- package/lib/templates/project/reactinternalapp/package.json +1 -1
- package/lib/templates/project/reactinternalapp/scripts/org-setup-config-schema.mjs +14 -2
- package/lib/templates/project/reactinternalapp/scripts/org-setup-dev.mjs +27 -0
- package/lib/templates/project/reactinternalapp/scripts/org-setup-url.mjs +108 -0
- package/lib/templates/project/reactinternalapp/scripts/org-setup-xml.mjs +96 -0
- package/lib/templates/project/reactinternalapp/scripts/org-setup.mjs +195 -3
- package/lib/templates/uiBundles/angularbasic/package-lock.json +446 -344
- package/lib/templates/uiBundles/reactbasic/package-lock.json +672 -425
- package/lib/templates/uiBundles/reactbasic/package.json +6 -4
- package/lib/templates/uiBundles/reactbasic/patches/aria-hidden+1.2.6.patch +45 -0
- package/lib/templates/uiembedding/default/default.html +1 -1
- package/lib/utils/template-placeholders.d.ts +1 -1
- package/lib/utils/template-placeholders.js +3 -1
- package/lib/utils/template-placeholders.js.map +1 -1
- package/lib/utils/types.d.ts +1 -1
- package/package.json +5 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/README.md +0 -246
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/language-switcher → _f_/_ls_}/__examples__/html-middleware-site-example.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/language-switcher → _f_/_ls_}/__examples__/language-switcher-example.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/language-switcher → _f_/_ls_}/build-language-url.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/language-switcher → _f_/_ls_}/index.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/language-switcher → _f_/_ls_}/language-switcher.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/language-switcher → _f_/_ls_}/language-switcher.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/language-switcher → _f_/_ls_}/languages.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/api/object-search.service.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/breadcrumb/breadcrumb.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/breadcrumb/breadcrumb.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/active-filters.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/active-filters.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-range-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-range-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/filter-field-wrapper.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/filter-field-wrapper.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/multi-select-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/multi-select-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/numeric-range-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/numeric-range-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/search-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/search-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/select-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/select-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/text-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/text-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/object-search-state.service.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/pagination/pagination.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/pagination/pagination.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/search-bar/search-bar.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/search-bar/search-bar.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/sort-control/sort-control.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/sort-control/sort-control.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/debounce.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/field-utils.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/filter-utils.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/index.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/sort-utils.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth/auth.guard.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth/auth.service.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth-form/auth-form.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth-form/auth-form.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth-layout/auth-layout.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth-layout/auth-layout.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/card-layout/card-layout.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/card-layout/card-layout.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/config/authentication.config.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/footer-link/footer-link.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/footer-link/footer-link.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/menu/auth-menu/auth-menu.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/menu/auth-menu/auth-menu.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/reactive-form-page.base.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/session/session.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/session/session.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/status-alert/status-alert.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/status-alert/status-alert.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/submit-button/submit-button.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/submit-button/submit-button.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/auth-helpers.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/auth-validators.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/helpers.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/session-time-servlet.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/session-timeout.config.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/session-timeout.service.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/api/object-search.service.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/breadcrumb/breadcrumb.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/breadcrumb/breadcrumb.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/active-filters.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/active-filters.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-range-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-range-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/filter-field-wrapper.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/filter-field-wrapper.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/multi-select-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/multi-select-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/numeric-range-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/numeric-range-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/search-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/search-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/select-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/select-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/text-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/text-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/object-search-state.service.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/pagination/pagination.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/pagination/pagination.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/search-bar/search-bar.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/search-bar/search-bar.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/sort-control/sort-control.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/sort-control/sort-control.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/debounce.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/field-utils.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/filter-utils.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/index.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/sort-utils.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/agentforce/agentforce-embed.service.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/agentforce/conversation.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/agentforce/conversation.ts +0 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
"node": ">=22"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
+
"postinstall": "patch-package",
|
|
10
11
|
"dev": "vite",
|
|
11
12
|
"dev:design": "vite --mode design",
|
|
12
13
|
"build": "tsc -b && vite build",
|
|
@@ -18,8 +19,8 @@
|
|
|
18
19
|
"graphql:schema": "node scripts/get-graphql-schema.mjs"
|
|
19
20
|
},
|
|
20
21
|
"dependencies": {
|
|
21
|
-
"@salesforce/platform-sdk": "^
|
|
22
|
-
"@salesforce/ui-bundle": "^
|
|
22
|
+
"@salesforce/platform-sdk": "^12.4.5",
|
|
23
|
+
"@salesforce/ui-bundle": "^12.4.5",
|
|
23
24
|
"@tailwindcss/vite": "^4.1.17",
|
|
24
25
|
"class-variance-authority": "^0.7.1",
|
|
25
26
|
"clsx": "^2.1.1",
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
"tailwind-merge": "^3.5.0",
|
|
36
37
|
"tailwindcss": "^4.1.17",
|
|
37
38
|
"tw-animate-css": "^1.4.0",
|
|
38
|
-
"@salesforce/agentforce-conversation-client": "^
|
|
39
|
+
"@salesforce/agentforce-conversation-client": "^12.0.0"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@eslint/js": "^9.39.1",
|
|
@@ -45,8 +46,8 @@
|
|
|
45
46
|
"@graphql-eslint/eslint-plugin": "^4.1.0",
|
|
46
47
|
"@graphql-tools/utils": "^11.0.0",
|
|
47
48
|
"@playwright/test": "^1.49.0",
|
|
48
|
-
"@salesforce/graphiti": "^
|
|
49
|
-
"@salesforce/vite-plugin-ui-bundle": "^
|
|
49
|
+
"@salesforce/graphiti": "^12.4.5",
|
|
50
|
+
"@salesforce/vite-plugin-ui-bundle": "^12.4.5",
|
|
50
51
|
"@testing-library/jest-dom": "^6.6.3",
|
|
51
52
|
"@testing-library/react": "^16.1.0",
|
|
52
53
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
"graphql": "^16.11.0",
|
|
64
65
|
"graphql-codegen-typescript-operation-types": "^2.0.2",
|
|
65
66
|
"jsdom": "^25.0.1",
|
|
67
|
+
"patch-package": "^8.0.1",
|
|
66
68
|
"serve": "^14.2.5",
|
|
67
69
|
"typescript": "~5.9.3",
|
|
68
70
|
"typescript-eslint": "^8.46.4",
|
package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/patches/aria-hidden+1.2.6.patch
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
diff --git a/node_modules/aria-hidden/dist/es2015/index.js b/node_modules/aria-hidden/dist/es2015/index.js
|
|
2
|
+
index 00aa2ea..1d2f61e 100644
|
|
3
|
+
--- a/node_modules/aria-hidden/dist/es2015/index.js
|
|
4
|
+
+++ b/node_modules/aria-hidden/dist/es2015/index.js
|
|
5
|
+
@@ -131,7 +131,9 @@ export var hideOthers = function (originalTarget, parentNode, markerName) {
|
|
6
|
+
// we should not hide aria-live elements - https://github.com/theKashey/aria-hidden/issues/10
|
|
7
|
+
// and script elements, as they have no impact on accessibility.
|
|
8
|
+
targets.push.apply(targets, Array.from(activeParentNode.querySelectorAll('[aria-live], script')));
|
|
9
|
+
- return applyAttributeToOthers(targets, activeParentNode, markerName, 'aria-hidden');
|
|
10
|
+
+ return supportsInert()
|
|
11
|
+
+ ? inertOthers(targets, activeParentNode, markerName)
|
|
12
|
+
+ : applyAttributeToOthers(targets, activeParentNode, markerName, 'aria-hidden');
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Marks everything except given node(or nodes) as inert
|
|
16
|
+
diff --git a/node_modules/aria-hidden/dist/es2019/index.js b/node_modules/aria-hidden/dist/es2019/index.js
|
|
17
|
+
index 17404da..a444872 100644
|
|
18
|
+
--- a/node_modules/aria-hidden/dist/es2019/index.js
|
|
19
|
+
+++ b/node_modules/aria-hidden/dist/es2019/index.js
|
|
20
|
+
@@ -126,7 +126,9 @@ export const hideOthers = (originalTarget, parentNode, markerName = 'data-aria-h
|
|
21
|
+
// we should not hide aria-live elements - https://github.com/theKashey/aria-hidden/issues/10
|
|
22
|
+
// and script elements, as they have no impact on accessibility.
|
|
23
|
+
targets.push(...Array.from(activeParentNode.querySelectorAll('[aria-live], script')));
|
|
24
|
+
- return applyAttributeToOthers(targets, activeParentNode, markerName, 'aria-hidden');
|
|
25
|
+
+ return supportsInert()
|
|
26
|
+
+ ? inertOthers(targets, activeParentNode, markerName)
|
|
27
|
+
+ : applyAttributeToOthers(targets, activeParentNode, markerName, 'aria-hidden');
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Marks everything except given node(or nodes) as inert
|
|
31
|
+
diff --git a/node_modules/aria-hidden/dist/es5/index.js b/node_modules/aria-hidden/dist/es5/index.js
|
|
32
|
+
index 1b86f56..863f83e 100644
|
|
33
|
+
--- a/node_modules/aria-hidden/dist/es5/index.js
|
|
34
|
+
+++ b/node_modules/aria-hidden/dist/es5/index.js
|
|
35
|
+
@@ -134,7 +134,9 @@ var hideOthers = function (originalTarget, parentNode, markerName) {
|
|
36
|
+
// we should not hide aria-live elements - https://github.com/theKashey/aria-hidden/issues/10
|
|
37
|
+
// and script elements, as they have no impact on accessibility.
|
|
38
|
+
targets.push.apply(targets, Array.from(activeParentNode.querySelectorAll('[aria-live], script')));
|
|
39
|
+
- return applyAttributeToOthers(targets, activeParentNode, markerName, 'aria-hidden');
|
|
40
|
+
+ return supportsInert()
|
|
41
|
+
+ ? inertOthers(targets, activeParentNode, markerName)
|
|
42
|
+
+ : applyAttributeToOthers(targets, activeParentNode, markerName, 'aria-hidden');
|
|
43
|
+
};
|
|
44
|
+
exports.hideOthers = hideOthers;
|
|
45
|
+
/**
|
|
@@ -60,13 +60,16 @@ function isSearchable(entry: RawContentTypeEntry): boolean {
|
|
|
60
60
|
return true;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
* `isValidCmsFqn`.
|
|
68
|
-
*/
|
|
63
|
+
/** ManagedContentType key prefix — the `id` custom content types carry instead of an FQN. */
|
|
64
|
+
const CONTENT_TYPE_RECORD_ID_PATTERN = /^0T1[a-zA-Z0-9]{12,15}$/;
|
|
65
|
+
|
|
66
|
+
/** Extracts the prefixed FQN from an entry, rebuilding `c__<name>` when `id` is a custom-type record id. */
|
|
69
67
|
function entryFqn(entry: RawContentTypeEntry): string | undefined {
|
|
68
|
+
// Custom types put a ManagedContentType id in `id`; the FQN the GraphQL filter
|
|
69
|
+
// accepts is `c__<name>`.
|
|
70
|
+
if (entry.id && CONTENT_TYPE_RECORD_ID_PATTERN.test(entry.id)) {
|
|
71
|
+
return entry.name ? `c__${entry.name}` : undefined;
|
|
72
|
+
}
|
|
70
73
|
return entry.id ?? entry.contentType ?? entry.fqn ?? entry.developerName;
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* The `$cmsContentTypeFQNs` declaration, its `contentTypeFQNs:` argument, and
|
|
7
7
|
* its variable value are all emitted only when a non-empty content-type list is
|
|
8
|
-
* passed; on the bootstrap call (no types known) they are omitted entirely.
|
|
8
|
+
* passed; on the bootstrap call (no types known) they are omitted entirely. The
|
|
9
|
+
* `$cmsLanguage` pieces follow the same conditional pattern.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import type { QueryFragmentContribution } from "../types";
|
|
@@ -26,6 +27,8 @@ export interface BuildCmsFragmentParams {
|
|
|
26
27
|
* `isValidCmsFqn`-validated values.
|
|
27
28
|
*/
|
|
28
29
|
contentTypes?: string[] | null;
|
|
30
|
+
/** User language tag for the `language` argument; emitted only when a non-empty string. */
|
|
31
|
+
language?: string | null;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
/**
|
|
@@ -33,9 +36,10 @@ export interface BuildCmsFragmentParams {
|
|
|
33
36
|
* included only when `params.contentTypes` is a non-empty array.
|
|
34
37
|
*/
|
|
35
38
|
export function buildCmsQueryFragment(params: BuildCmsFragmentParams): QueryFragmentContribution {
|
|
36
|
-
const { keyword, uiBundleId, offset, limit, contentTypes } = params;
|
|
39
|
+
const { keyword, uiBundleId, offset, limit, contentTypes, language } = params;
|
|
37
40
|
|
|
38
41
|
const hasContentTypes = Array.isArray(contentTypes) && contentTypes.length > 0;
|
|
42
|
+
const hasLanguage = typeof language === "string" && language.length > 0;
|
|
39
43
|
|
|
40
44
|
const variableDeclarations = [
|
|
41
45
|
"$cmsKeyword: String!",
|
|
@@ -61,12 +65,19 @@ export function buildCmsQueryFragment(params: BuildCmsFragmentParams): QueryFrag
|
|
|
61
65
|
variables.cmsContentTypeFQNs = contentTypes;
|
|
62
66
|
}
|
|
63
67
|
|
|
68
|
+
// Top-level `language: String` arg, emitted only when a non-empty tag is provided.
|
|
69
|
+
const languageArg = hasLanguage ? "\n language: $cmsLanguage" : "";
|
|
70
|
+
if (hasLanguage) {
|
|
71
|
+
variableDeclarations.push("$cmsLanguage: String");
|
|
72
|
+
variables.cmsLanguage = language;
|
|
73
|
+
}
|
|
74
|
+
|
|
64
75
|
const fragment = `managed_content {
|
|
65
76
|
search {
|
|
66
77
|
searchContentInChannels(
|
|
67
78
|
searchIdentifiers: { uibundleIds: [$UIBundleId] }
|
|
68
79
|
keyword: $cmsKeyword
|
|
69
|
-
pagination: { limit: $cmsLimit, offset: $cmsOffset }${contentTypesArg}
|
|
80
|
+
pagination: { limit: $cmsLimit, offset: $cmsOffset }${languageArg}${contentTypesArg}
|
|
70
81
|
) {
|
|
71
82
|
total
|
|
72
83
|
offset
|
|
@@ -8,21 +8,23 @@
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Formats a CMS content type FQN into a human-readable label.
|
|
11
|
-
* Strips "sfdc_cms__" prefix, splits camelCase, title-cases each word.
|
|
11
|
+
* Strips the "sfdc_cms__" (OOTB) or "c__" (custom) prefix, splits camelCase, title-cases each word.
|
|
12
12
|
*
|
|
13
13
|
* Examples:
|
|
14
14
|
* "sfdc_cms__blogPost" → "Blog Post"
|
|
15
15
|
* "sfdc_cms__news" → "News"
|
|
16
|
-
* "
|
|
17
|
-
* "
|
|
16
|
+
* "c__NewsArticle" → "News Article"
|
|
17
|
+
* "c__Recipe" → "Recipe"
|
|
18
18
|
*/
|
|
19
19
|
export function formatContentTypeLabel(fqn: string): string {
|
|
20
|
-
const stripped = fqn.replace(/^sfdc_cms__/, "");
|
|
20
|
+
const stripped = fqn.replace(/^(?:sfdc_cms__|c__)/, "");
|
|
21
21
|
const words = stripped.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
22
22
|
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
// OOTB content types are namespaced `sfdc_cms__<name>`; custom types created in
|
|
26
|
+
// a namespace-less org take the default `c__<name>` prefix. Both are valid FQNs.
|
|
27
|
+
export const CMS_FQN_PATTERN = /^(?:sfdc_cms__|c__)[a-zA-Z][a-zA-Z0-9]{0,39}$/;
|
|
26
28
|
|
|
27
29
|
export function isValidCmsFqn(fqn: string): boolean {
|
|
28
30
|
return CMS_FQN_PATTERN.test(fqn);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { getCurrentApp } from "@salesforce/platform-sdk";
|
|
1
|
+
import { createDataSDK, getCurrentApp } from "@salesforce/platform-sdk";
|
|
2
|
+
import { fetchI18nContext } from "@salesforce/platform-sdk/i18n";
|
|
2
3
|
|
|
3
4
|
/** Returns the UIBundle id for CMS search, resolved from the running app. */
|
|
4
5
|
export async function getUIBundleId(): Promise<string> {
|
|
@@ -6,6 +7,21 @@ export async function getUIBundleId(): Promise<string> {
|
|
|
6
7
|
return app.identity?.bundleId ?? "";
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
/** Fallback language tag used when the user's own language can't be resolved. */
|
|
11
|
+
export const DEFAULT_LANGUAGE = "en-US";
|
|
12
|
+
|
|
13
|
+
/** Returns the current user's language tag (e.g. `"en-US"`), falling back to {@link DEFAULT_LANGUAGE}. */
|
|
14
|
+
export async function getUserLanguage(): Promise<string> {
|
|
15
|
+
try {
|
|
16
|
+
const sdk = await createDataSDK();
|
|
17
|
+
if (!sdk?.graphql) return DEFAULT_LANGUAGE;
|
|
18
|
+
const ctx = await fetchI18nContext(sdk);
|
|
19
|
+
return ctx.lang || DEFAULT_LANGUAGE;
|
|
20
|
+
} catch {
|
|
21
|
+
return DEFAULT_LANGUAGE;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
/** Shape guard for a UIBundle record id (9YE + 12-15 alphanumerics). */
|
|
10
26
|
export const UI_BUNDLE_ID_PATTERN = /^9YE[a-zA-Z0-9]{12,15}$/;
|
|
11
27
|
|
package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts
CHANGED
|
@@ -81,6 +81,8 @@ export interface SourceRequest<T extends SourceConfig = SourceConfig> {
|
|
|
81
81
|
* synchronous. Ignored by non-CMS adapters.
|
|
82
82
|
*/
|
|
83
83
|
uiBundleId?: string;
|
|
84
|
+
/** CMS-only, optional. The current user's language tag fed to the CMS search `language` argument. */
|
|
85
|
+
language?: string;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
/**
|
|
@@ -40,6 +40,7 @@ import { FilterProvider, FilterResetButton } from "./filters/FilterContext";
|
|
|
40
40
|
import { PaginationControls } from "./controls/PaginationControls";
|
|
41
41
|
import { SortControl } from "./controls/SortControl";
|
|
42
42
|
import { resolveResultRenderer, type ResultRenderer } from "./results/resolveResultRenderer";
|
|
43
|
+
import { MIN_QUERY_LENGTH } from "../constants";
|
|
43
44
|
import { ALL_SCOPE, type SearchHandle, type SourceController } from "../types";
|
|
44
45
|
|
|
45
46
|
export interface MergedSearchResultsProps {
|
|
@@ -52,6 +53,8 @@ export interface MergedSearchResultsProps {
|
|
|
52
53
|
renderResult?: Record<string, ResultRenderer>;
|
|
53
54
|
/** Message shown when there are no results. Defaults to "No results found." */
|
|
54
55
|
emptyMessage?: string;
|
|
56
|
+
/** Message shown when the query is below {@link MIN_QUERY_LENGTH}. Defaults to "Start your search…" */
|
|
57
|
+
shortQueryMessage?: string;
|
|
55
58
|
/** Number of skeleton placeholders to show while loading. Defaults to `pageSize`. */
|
|
56
59
|
skeletonCount?: number;
|
|
57
60
|
/** Extra classes on the grid container. Defaults to a responsive 1/2/3-column grid. */
|
|
@@ -81,11 +84,16 @@ export function MergedSearchResults({
|
|
|
81
84
|
handle,
|
|
82
85
|
renderResult,
|
|
83
86
|
emptyMessage,
|
|
87
|
+
shortQueryMessage = "Start your search…",
|
|
84
88
|
skeletonCount,
|
|
85
89
|
gridClassName = DEFAULT_GRID_CLASS,
|
|
86
90
|
className,
|
|
87
91
|
}: MergedSearchResultsProps) {
|
|
88
|
-
const { globalPagination: page, mergedResults, loading, error } = handle;
|
|
92
|
+
const { globalPagination: page, mergedResults, loading, error, q } = handle;
|
|
93
|
+
// Distinguishes "haven't typed enough yet" (useSearch short-circuits below
|
|
94
|
+
// MIN_QUERY_LENGTH without fetching) from a genuine zero-match search — both
|
|
95
|
+
// otherwise present identically as loading=false, mergedResults=[].
|
|
96
|
+
const isShortQuery = q.trim().length < MIN_QUERY_LENGTH;
|
|
89
97
|
|
|
90
98
|
// A single source is in scope when the scope is narrowed (selected from the
|
|
91
99
|
// dropdown) or locked (`lockedScope` / restrictTo). Its filter chrome can
|
|
@@ -111,9 +119,11 @@ export function MergedSearchResults({
|
|
|
111
119
|
? "Searching…"
|
|
112
120
|
: error
|
|
113
121
|
? "Search failed."
|
|
114
|
-
:
|
|
115
|
-
?
|
|
116
|
-
:
|
|
122
|
+
: isShortQuery
|
|
123
|
+
? shortQueryMessage
|
|
124
|
+
: mergedResults.length === 0
|
|
125
|
+
? (emptyMessage ?? "No results found.")
|
|
126
|
+
: `${page.totalCount} result${page.totalCount === 1 ? "" : "s"}`;
|
|
117
127
|
|
|
118
128
|
return (
|
|
119
129
|
<div className={`space-y-6 ${className ?? ""}`}>
|
|
@@ -161,6 +171,8 @@ export function MergedSearchResults({
|
|
|
161
171
|
<Skeleton key={i} className="h-48 w-full rounded-lg" />
|
|
162
172
|
))}
|
|
163
173
|
</div>
|
|
174
|
+
) : isShortQuery ? (
|
|
175
|
+
<p className="py-12 text-center text-sm text-muted-foreground">{shortQueryMessage}</p>
|
|
164
176
|
) : mergedResults.length === 0 ? (
|
|
165
177
|
<p className="py-12 text-center text-sm text-muted-foreground">
|
|
166
178
|
{emptyMessage ?? "No results found."}
|
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { ReactNode } from "react";
|
|
12
|
-
import { AlertCircle, SearchX } from "lucide-react";
|
|
12
|
+
import { AlertCircle, Search, SearchX } from "lucide-react";
|
|
13
13
|
import { Alert, AlertDescription, AlertTitle } from "../../../components/ui/alert";
|
|
14
14
|
import { Skeleton } from "../../../components/ui/skeleton";
|
|
15
15
|
import { ActiveFilters } from "./filters/ActiveFilters";
|
|
16
16
|
import { FilterProvider, FilterResetButton } from "./filters/FilterContext";
|
|
17
17
|
import { PaginationControls } from "./controls/PaginationControls";
|
|
18
18
|
import { SortControl } from "./controls/SortControl";
|
|
19
|
+
import { MIN_QUERY_LENGTH } from "../constants";
|
|
19
20
|
import type { SourceController } from "../types";
|
|
20
21
|
|
|
21
22
|
interface SourceSectionProps {
|
|
@@ -35,6 +36,15 @@ interface SourceSectionProps {
|
|
|
35
36
|
*/
|
|
36
37
|
hideFilterChrome?: boolean;
|
|
37
38
|
emptyMessage?: string;
|
|
39
|
+
/**
|
|
40
|
+
* The current search query. Used to distinguish a not-yet-started search
|
|
41
|
+
* (query below {@link MIN_QUERY_LENGTH}, so no fetch was issued) from a
|
|
42
|
+
* genuine zero-match result, so the empty state can prompt instead of
|
|
43
|
+
* claiming "No … found."
|
|
44
|
+
*/
|
|
45
|
+
q?: string;
|
|
46
|
+
/** Message shown when the query is below {@link MIN_QUERY_LENGTH}. Defaults to "Start your search…" */
|
|
47
|
+
shortQueryMessage?: string;
|
|
38
48
|
className?: string;
|
|
39
49
|
}
|
|
40
50
|
|
|
@@ -44,6 +54,8 @@ export function SourceSection({
|
|
|
44
54
|
renderFilters,
|
|
45
55
|
hideFilterChrome,
|
|
46
56
|
emptyMessage,
|
|
57
|
+
q = "",
|
|
58
|
+
shortQueryMessage = "Start your search…",
|
|
47
59
|
className,
|
|
48
60
|
}: SourceSectionProps) {
|
|
49
61
|
const { config, result, loading, error, filters, sort, pagination } = controller;
|
|
@@ -51,6 +63,9 @@ export function SourceSection({
|
|
|
51
63
|
const totalCount = result?.totalCount;
|
|
52
64
|
const showResults = !loading && !error && nodes.length > 0;
|
|
53
65
|
const showEmpty = !loading && !error && nodes.length === 0;
|
|
66
|
+
// A query below MIN_QUERY_LENGTH never triggers a fetch, so an empty result
|
|
67
|
+
// means "not searched yet" — prompt rather than report zero matches.
|
|
68
|
+
const isShortQuery = q.trim().length < MIN_QUERY_LENGTH;
|
|
54
69
|
|
|
55
70
|
const pageSizeOptions = pagination.pageSizeOptions ?? [pagination.pageSize];
|
|
56
71
|
|
|
@@ -70,7 +85,9 @@ export function SourceSection({
|
|
|
70
85
|
: error
|
|
71
86
|
? `Failed to load ${config.label}.`
|
|
72
87
|
: showEmpty
|
|
73
|
-
?
|
|
88
|
+
? isShortQuery
|
|
89
|
+
? shortQueryMessage
|
|
90
|
+
: (emptyMessage ?? `No ${config.label.toLowerCase()} found.`)
|
|
74
91
|
: totalCount != null
|
|
75
92
|
? `${config.label}: ${totalCount} ${totalCount === 1 ? "result" : "results"}`
|
|
76
93
|
: "";
|
|
@@ -153,9 +170,15 @@ export function SourceSection({
|
|
|
153
170
|
|
|
154
171
|
{showEmpty && (
|
|
155
172
|
<div className="flex flex-col items-center justify-center py-10 text-center">
|
|
156
|
-
|
|
173
|
+
{isShortQuery ? (
|
|
174
|
+
<Search className="size-10 text-muted-foreground mb-3" />
|
|
175
|
+
) : (
|
|
176
|
+
<SearchX className="size-10 text-muted-foreground mb-3" />
|
|
177
|
+
)}
|
|
157
178
|
<p className="text-sm text-muted-foreground">
|
|
158
|
-
{
|
|
179
|
+
{isShortQuery
|
|
180
|
+
? shortQueryMessage
|
|
181
|
+
: (emptyMessage ?? `No ${config.label.toLowerCase()} found.`)}
|
|
159
182
|
</p>
|
|
160
183
|
</div>
|
|
161
184
|
)}
|
|
@@ -64,7 +64,13 @@ function DefaultFilterField({ filter, source }: DefaultFilterFieldProps) {
|
|
|
64
64
|
return <PicklistField filter={filter} source={source} multi={true} />;
|
|
65
65
|
case "numeric":
|
|
66
66
|
return (
|
|
67
|
-
<NumericRangeFilter
|
|
67
|
+
<NumericRangeFilter
|
|
68
|
+
field={filter.field}
|
|
69
|
+
label={filter.label}
|
|
70
|
+
helpText={filter.helpText}
|
|
71
|
+
min={filter.min}
|
|
72
|
+
max={filter.max}
|
|
73
|
+
/>
|
|
68
74
|
);
|
|
69
75
|
case "boolean":
|
|
70
76
|
return <BooleanFilter field={filter.field} label={filter.label} helpText={filter.helpText} />;
|
package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts
CHANGED
|
@@ -40,7 +40,11 @@ import { MIN_QUERY_LENGTH } from "../constants";
|
|
|
40
40
|
// `resolveChannelId` derives the discovery channel id from a CMS result;
|
|
41
41
|
// `useSearchableContentTypes` is the session-cached discovery hook;
|
|
42
42
|
// `isValidCmsFqn` validates a selected FQN before it enters state or a query.
|
|
43
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
getUIBundleId,
|
|
45
|
+
getUserLanguage,
|
|
46
|
+
isConfiguredUIBundleId,
|
|
47
|
+
} from "../adapters/cms/searchChannel";
|
|
44
48
|
import { getOrgSupportsCmsSearch } from "../adapters/cms/api/orgApiVersionService";
|
|
45
49
|
import { resolveChannelId } from "../adapters/cms/channelResolver";
|
|
46
50
|
import { useSearchableContentTypes } from "../adapters/cms/hooks/useSearchableContentTypes";
|
|
@@ -98,7 +102,7 @@ interface ResolvedPagination {
|
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
/** Resolves the single global pagination config, filling in defaults. */
|
|
101
|
-
function resolvePagination(config: SearchConfig): ResolvedPagination {
|
|
105
|
+
export function resolvePagination(config: SearchConfig): ResolvedPagination {
|
|
102
106
|
const p = config.pagination;
|
|
103
107
|
const pageSizeOptions =
|
|
104
108
|
p?.pageSizeOptions && p.pageSizeOptions.length > 0
|
|
@@ -119,7 +123,7 @@ function validatePageSize(pagination: ResolvedPagination, size: number): number
|
|
|
119
123
|
return valid.includes(size) ? size : pagination.pageSize;
|
|
120
124
|
}
|
|
121
125
|
|
|
122
|
-
function initSourceState(
|
|
126
|
+
export function initSourceState(
|
|
123
127
|
source: SourceConfig,
|
|
124
128
|
params: URLSearchParams,
|
|
125
129
|
pagination: ResolvedPagination,
|
|
@@ -135,7 +139,8 @@ function initSourceState(
|
|
|
135
139
|
filters: read.filters,
|
|
136
140
|
sort,
|
|
137
141
|
pageSize,
|
|
138
|
-
|
|
142
|
+
// Cursors aren't persisted to the URL, so always seed the first page (page param ignored on read).
|
|
143
|
+
pageIndex: 0,
|
|
139
144
|
afterCursor: undefined,
|
|
140
145
|
cursorStack: [],
|
|
141
146
|
};
|
|
@@ -503,8 +508,8 @@ export function useSearch(config: SearchConfig, options?: UseSearchOptions): Sea
|
|
|
503
508
|
// page-level error — content-type narrowing is an enhancement, not a
|
|
504
509
|
// hard requirement. Read in `.catch` below.
|
|
505
510
|
let isDiscoveryRefetch = false;
|
|
506
|
-
Promise.all([getUIBundleId(), getOrgSupportsCmsSearch()])
|
|
507
|
-
.then(([uiBundleId, orgSupportsCmsSearch]) => {
|
|
511
|
+
Promise.all([getUIBundleId(), getUserLanguage(), getOrgSupportsCmsSearch()])
|
|
512
|
+
.then(([uiBundleId, userLanguage, orgSupportsCmsSearch]) => {
|
|
508
513
|
if (cancelled || generation !== fetchGenRef.current) return;
|
|
509
514
|
|
|
510
515
|
const requests: SourceRequest[] = activeSources.flatMap((source) => {
|
|
@@ -541,7 +546,7 @@ export function useSearch(config: SearchConfig, options?: UseSearchOptions): Sea
|
|
|
541
546
|
if (!cmsFqn && discoveredContentTypeFqns.length > 0) {
|
|
542
547
|
isDiscoveryRefetch = true;
|
|
543
548
|
}
|
|
544
|
-
return [{ ...base, uiBundleId, contentTypes }];
|
|
549
|
+
return [{ ...base, uiBundleId, language: userLanguage, contentTypes }];
|
|
545
550
|
});
|
|
546
551
|
|
|
547
552
|
// Every in-scope source was gated out (e.g. a CMS-only scope while CMS
|
package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts
CHANGED
|
@@ -49,6 +49,9 @@ export type FilterFieldConfig<TFieldName extends string = string> = {
|
|
|
49
49
|
/** Date-family only — see {@link DateFilterMode}. Defaults to `"comparison"`. */
|
|
50
50
|
dateMode?: DateFilterMode;
|
|
51
51
|
helpText?: string;
|
|
52
|
+
// Numeric-family only.
|
|
53
|
+
min?: number;
|
|
54
|
+
max?: number;
|
|
52
55
|
};
|
|
53
56
|
|
|
54
57
|
export type ActiveFilterValue<TFieldName extends string = string> = {
|
|
@@ -205,12 +208,17 @@ function toEndOfDay(d: string) {
|
|
|
205
208
|
return `${d}T23:59:59.999Z`;
|
|
206
209
|
}
|
|
207
210
|
|
|
208
|
-
|
|
211
|
+
/** Escapes LIKE metacharacters (`\`, `%`, `_`) so user input is matched literally. */
|
|
212
|
+
function escapeLikePattern(value: string): string {
|
|
213
|
+
return value.replace(/[\\%_]/g, "\\$&");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function buildSingleFilter(filter: ActiveFilterValue, config?: FilterFieldConfig): unknown {
|
|
209
217
|
const { field, type, value, min, max } = filter;
|
|
210
218
|
switch (type) {
|
|
211
219
|
case "text": {
|
|
212
220
|
if (!value) return null;
|
|
213
|
-
return { [field]: { like: `%${value}%` } };
|
|
221
|
+
return { [field]: { like: `%${escapeLikePattern(value)}%` } };
|
|
214
222
|
}
|
|
215
223
|
case "picklist": {
|
|
216
224
|
if (!value) return null;
|
|
@@ -219,8 +227,19 @@ function buildSingleFilter(filter: ActiveFilterValue, _config?: FilterFieldConfi
|
|
|
219
227
|
case "numeric": {
|
|
220
228
|
if (!min && !max) return null;
|
|
221
229
|
const ops: Record<string, number> = {};
|
|
222
|
-
|
|
223
|
-
|
|
230
|
+
// clamp both endpoints into the declared [min,max] bounds
|
|
231
|
+
const clamp = (n: number) => {
|
|
232
|
+
if (config?.min != null) n = Math.max(n, config.min);
|
|
233
|
+
if (config?.max != null) n = Math.min(n, config.max);
|
|
234
|
+
return n;
|
|
235
|
+
};
|
|
236
|
+
// Drop any endpoint that isn't finite — NaN/Infinity serialize to null on the wire.
|
|
237
|
+
const lo = Number(min);
|
|
238
|
+
if (min && Number.isFinite(lo)) ops.gte = clamp(lo);
|
|
239
|
+
const hi = Number(max);
|
|
240
|
+
if (max && Number.isFinite(hi)) ops.lte = clamp(hi);
|
|
241
|
+
// If both endpoints were dropped as invalid, emit no clause at all.
|
|
242
|
+
if (Object.keys(ops).length === 0) return null;
|
|
224
243
|
return { [field]: ops };
|
|
225
244
|
}
|
|
226
245
|
case "boolean": {
|
|
@@ -275,7 +294,7 @@ export function buildGlobalQueryClause(q: string, searchableFields: string[]): u
|
|
|
275
294
|
if (!trimmed || searchableFields.length === 0) return undefined;
|
|
276
295
|
const clauses = searchableFields.map((path) => {
|
|
277
296
|
const parts = path.split(".");
|
|
278
|
-
let clause: Record<string, unknown> = { like: `%${trimmed}%` };
|
|
297
|
+
let clause: Record<string, unknown> = { like: `%${escapeLikePattern(trimmed)}%` };
|
|
279
298
|
for (let i = parts.length - 1; i >= 0; i--) {
|
|
280
299
|
clause = { [parts[i]]: clause };
|
|
281
300
|
}
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import { AgentforceConversationClient } from "./components/AgentforceConversationClient";
|
|
2
2
|
import { Outlet, Link, useLocation } from "react-router";
|
|
3
3
|
import { getAllRoutes } from "./router-utils";
|
|
4
|
-
import { useState } from "react";
|
|
4
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
5
5
|
|
|
6
6
|
export default function AppLayout() {
|
|
7
7
|
const [isOpen, setIsOpen] = useState(false);
|
|
8
8
|
const location = useLocation();
|
|
9
|
+
const navId = useId();
|
|
10
|
+
const outletWrapperRef = useRef<HTMLDivElement>(null);
|
|
11
|
+
const previousPathname = useRef<string | null>(null);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (previousPathname.current !== null && previousPathname.current !== location.pathname) {
|
|
15
|
+
const wrapper = outletWrapperRef.current;
|
|
16
|
+
const target = wrapper?.querySelector<HTMLElement>("h1, main, [role='main']") ?? wrapper;
|
|
17
|
+
if (target) {
|
|
18
|
+
if (!target.hasAttribute("tabindex")) target.setAttribute("tabindex", "-1");
|
|
19
|
+
target.focus();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
previousPathname.current = location.pathname;
|
|
23
|
+
}, [location.pathname]);
|
|
9
24
|
|
|
10
25
|
const isActive = (path: string) => location.pathname === path;
|
|
11
26
|
|
|
@@ -28,6 +43,20 @@ export default function AppLayout() {
|
|
|
28
43
|
|
|
29
44
|
return (
|
|
30
45
|
<>
|
|
46
|
+
<a
|
|
47
|
+
href="#main-content"
|
|
48
|
+
onClick={(e) => {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
const content = outletWrapperRef.current;
|
|
51
|
+
if (content) {
|
|
52
|
+
if (!content.hasAttribute("tabindex")) content.setAttribute("tabindex", "-1");
|
|
53
|
+
content.focus();
|
|
54
|
+
}
|
|
55
|
+
}}
|
|
56
|
+
className="sr-only focus:not-sr-only focus:absolute focus:z-50 focus:top-2 focus:left-2 focus:rounded-md focus:bg-white focus:px-4 focus:py-2 focus:text-gray-900 focus:shadow focus:ring-2 focus:ring-gray-900"
|
|
57
|
+
>
|
|
58
|
+
Skip to main content
|
|
59
|
+
</a>
|
|
31
60
|
<nav className="bg-white border-b border-gray-200">
|
|
32
61
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
33
62
|
<div className="flex justify-between items-center h-16">
|
|
@@ -38,8 +67,10 @@ export default function AppLayout() {
|
|
|
38
67
|
onClick={toggleMenu}
|
|
39
68
|
className="p-2 rounded-md text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
40
69
|
aria-label="Toggle menu"
|
|
70
|
+
aria-expanded={isOpen}
|
|
71
|
+
aria-controls={navId}
|
|
41
72
|
>
|
|
42
|
-
<div className="w-6 h-6 flex flex-col justify-center space-y-1.5">
|
|
73
|
+
<div aria-hidden="true" className="w-6 h-6 flex flex-col justify-center space-y-1.5">
|
|
43
74
|
<span
|
|
44
75
|
className={`block h-0.5 w-6 bg-current transition-all ${
|
|
45
76
|
isOpen ? "rotate-45 translate-y-2" : ""
|
|
@@ -56,14 +87,15 @@ export default function AppLayout() {
|
|
|
56
87
|
</div>
|
|
57
88
|
</button>
|
|
58
89
|
</div>
|
|
59
|
-
{
|
|
60
|
-
|
|
90
|
+
<div id={navId} className="pb-4" hidden={!isOpen}>
|
|
91
|
+
{isOpen && (
|
|
61
92
|
<div className="flex flex-col space-y-2">
|
|
62
93
|
{navigationRoutes.map((item) => (
|
|
63
94
|
<Link
|
|
64
95
|
key={item.path}
|
|
65
96
|
to={item.path}
|
|
66
97
|
onClick={() => setIsOpen(false)}
|
|
98
|
+
aria-current={isActive(item.path) ? "page" : undefined}
|
|
67
99
|
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
|
68
100
|
isActive(item.path)
|
|
69
101
|
? "bg-blue-100 text-blue-700"
|
|
@@ -74,11 +106,13 @@ export default function AppLayout() {
|
|
|
74
106
|
</Link>
|
|
75
107
|
))}
|
|
76
108
|
</div>
|
|
77
|
-
|
|
78
|
-
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
79
111
|
</div>
|
|
80
112
|
</nav>
|
|
81
|
-
<
|
|
113
|
+
<div id="main-content" ref={outletWrapperRef}>
|
|
114
|
+
<Outlet />
|
|
115
|
+
</div>
|
|
82
116
|
<AgentforceConversationClient agentId="<USER_AGENT_ID_18_CHAR_0Xx...>" />
|
|
83
117
|
</>
|
|
84
118
|
);
|
package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ObjectBreadcrumb.tsx
CHANGED
|
@@ -9,11 +9,7 @@ import {
|
|
|
9
9
|
} from "./ui/breadcrumb";
|
|
10
10
|
import { Skeleton } from "./ui/skeleton";
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* Home → List → Record breadcrumb for object pages. Previously imported from
|
|
14
|
-
* feature-react-object-search; inlined here so the app no longer depends on
|
|
15
|
-
* that feature.
|
|
16
|
-
*/
|
|
12
|
+
/** Home → List → Record breadcrumb, shared so every object page renders the same navigation trail. */
|
|
17
13
|
interface ObjectBreadcrumbProps {
|
|
18
14
|
listPath: string;
|
|
19
15
|
listLabel: string;
|