@salesforce/templates 66.17.5 → 66.19.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/projectGenerator.js +3 -13
- package/lib/generators/projectGenerator.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package-lock.json +205 -205
- package/lib/templates/project/angularintapp/CHANGELOG.md +8 -0
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/eslint.config.js +2 -1
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package-lock.json +212 -212
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package.json +4 -4
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/components/ui/icon/icon.ts +44 -0
- package/lib/templates/project/angularintapp/package.json +1 -1
- package/lib/templates/project/reactexternalapp/CHANGELOG.md +8 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/eslint.config.js +8 -12
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package-lock.json +188 -188
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package.json +5 -5
- 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/sonner.tsx +7 -1
- package/lib/templates/project/reactexternalapp/package.json +1 -1
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package-lock.json +186 -186
- package/lib/templates/project/vueinternalapp/.forceignore +15 -0
- package/lib/templates/project/vueinternalapp/.husky/pre-commit +4 -0
- package/lib/templates/project/vueinternalapp/.prettierignore +11 -0
- package/lib/templates/project/vueinternalapp/.prettierrc +17 -0
- package/lib/templates/project/vueinternalapp/CHANGELOG.md +5375 -0
- package/lib/templates/project/vueinternalapp/PRD.md +35 -0
- package/lib/templates/project/vueinternalapp/README.md +125 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_ps_/vueinternalapp_Access.permissionset-meta.xml +13 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/.forceignore +15 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/.graphqlrc.yml +2 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/.prettierignore +9 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/.prettierrc +11 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/README.md +35 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/codegen.yml +95 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/components.json +19 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/e2e/app.spec.ts +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/eslint.config.js +124 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/index.html +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/package-lock.json +15530 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/package.json +73 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/playwright.config.ts +24 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/scripts/get-graphql-schema.mjs +71 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/scripts/rewrite-e2e-assets.mjs +23 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/App.vue +5 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/apiUtils.ts +56 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/orgApiVersionService.ts +37 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/searchableContentTypesService.ts +119 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/channelResolver.ts +40 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/cmsQueryFragment.ts +108 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/composables/useSearchableContentTypes.ts +132 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeSessionCache.ts +134 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeUtils.ts +40 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/index.ts +55 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/parseResponse.ts +65 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/searchChannel.ts +31 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/types.ts +66 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/registry.ts +38 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/index.ts +19 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/parseResponse.ts +55 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/queryFragment.ts +141 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts +103 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/distinctValuesService.ts +81 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/searchService.ts +136 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/GlobalSearchBox.types.ts +18 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/GlobalSearchBox.vue +73 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.types.ts +24 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.vue +263 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/Search.vue +200 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.vue +99 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.vue +204 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/PaginationControls.vue +180 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/ScopeSelector.vue +91 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SearchBar.vue +56 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SortControl.vue +55 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/ActiveFilters.vue +59 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterField.vue +63 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.vue +31 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/FilterContext.ts +45 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/FilterProvider.vue +23 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/FilterResetButton.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/PicklistField.vue +35 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/BooleanFilter.vue +47 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/DateRangeFilter.vue +144 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/FilterFieldWrapper.vue +23 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/MultiSelectFilter.vue +95 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/NumericRangeFilter.vue +117 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/SelectFilter.vue +44 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/TextFilter.vue +49 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/CmsResultRow.vue +160 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/DefaultResultRow.vue +102 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/RenderVNode.ts +10 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/resolveResultRenderer.ts +41 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/composables/useAsyncData.ts +66 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/composables/useDistinctValues.ts +67 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/composables/useSearch.ts +906 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/config.json +88 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/constants.ts +8 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/index.ts +115 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/loadConfig.ts +14 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/queryBuilder.ts +100 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/types.ts +375 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/debounce.ts +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/fieldUtils.ts +14 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +304 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/sortUtils.ts +34 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/getAccountDetail.graphql +121 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/api/graphqlClient.ts +44 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/appLayout.vue +153 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/AgentforceConversationClient.vue +76 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/AccountDetailContent.vue +124 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/AccountDetailError.vue +28 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/AccountDetailNotFound.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/AccountDetailSkeleton.vue +57 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/DetailSection.vue +25 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/FieldItem.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/FieldRow.vue +5 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/SkeletonFieldRow.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/TelephoneField.vue +7 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/types.ts +42 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/alerts/status-alert.vue +59 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/layouts/card-layout.vue +30 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/alert/Alert.vue +21 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/alert/AlertAction.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/alert/AlertDescription.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/alert/AlertTitle.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/alert/index.ts +25 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/avatar/Avatar.vue +23 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/avatar/AvatarFallback.vue +31 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/avatar/AvatarImage.vue +16 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/avatar/index.ts +3 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/button/Button.vue +33 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/button/index.ts +43 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/Card.vue +29 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardAction.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardContent.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardDescription.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardFooter.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardHeader.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardTitle.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/index.ts +7 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/checkbox/Checkbox.vue +43 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/checkbox/index.ts +1 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/collapsible/Collapsible.vue +19 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/collapsible/CollapsibleContent.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/collapsible/index.ts +3 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/Dialog.vue +15 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogClose.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogContent.vue +59 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogDescription.vue +31 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogFooter.vue +33 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogHeader.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogOverlay.vue +31 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogPortal.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogTitle.vue +26 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogTrigger.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/index.ts +10 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +51 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuContent.vue +47 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuItem.vue +44 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +30 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuPortal.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +52 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +26 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +19 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +37 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +38 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/index.ts +15 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/Field.vue +27 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldContent.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldDescription.vue +24 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldError.vue +48 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldGroup.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldLabel.vue +24 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldLegend.vue +29 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldSeparator.vue +31 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldSet.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldTitle.vue +22 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/index.ts +33 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/index.ts +115 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/input/Input.vue +30 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/input/index.ts +1 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/label/Label.vue +29 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/label/index.ts +1 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/Pagination.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationContent.vue +15 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationEllipsis.vue +23 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationItem.vue +7 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationLink.vue +31 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationNext.vue +29 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationPrevious.vue +29 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/index.ts +7 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/Popover.vue +15 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverAnchor.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverContent.vue +44 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverDescription.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverHeader.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverTitle.vue +14 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverTrigger.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/index.ts +7 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/Select.vue +15 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectContent.vue +65 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectGroup.vue +26 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectItem.vue +41 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectLabel.vue +26 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectScrollDownButton.vue +32 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectScrollUpButton.vue +32 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectSeparator.vue +24 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectTrigger.vue +45 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectValue.vue +12 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/index.ts +10 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/separator/Separator.vue +33 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/separator/index.ts +1 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/skeleton/Skeleton.vue +15 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/skeleton/index.ts +1 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/sonner/Sonner.vue +29 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/sonner/index.ts +3 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/spinner/Spinner.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/spinner/index.ts +1 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/composables/useAsyncData.ts +69 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/index.ts +10 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/lib/utils.ts +6 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/main.ts +6 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.vue +98 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/pages/Home.vue +20 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/pages/NotFound.vue +28 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/pages/SearchPage.vue +17 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/router-utils.ts +32 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/router.ts +51 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/styles/global.css +134 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/test/setup.ts +1 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/types/conversation.ts +54 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/types/globals.d.ts +13 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/utils/accountFields.ts +25 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/tsconfig.json +45 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/tsconfig.node.json +13 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/ui-bundle.json +7 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/vite-env.d.ts +14 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/vite.config.ts +106 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/vitest-env.d.ts +2 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/vueinternalapp.uibundle-meta.xml +8 -0
- package/lib/templates/project/vueinternalapp/_p_/_m_/applications/vueinternalapp.app-meta.xml +17 -0
- package/lib/templates/project/vueinternalapp/config/project-scratch-def.json +13 -0
- package/lib/templates/project/vueinternalapp/eslint.config.js +7 -0
- package/lib/templates/project/vueinternalapp/jest.config.js +6 -0
- package/lib/templates/project/vueinternalapp/package.json +46 -0
- package/lib/templates/project/vueinternalapp/scripts/apex/hello.apex +10 -0
- package/lib/templates/project/vueinternalapp/scripts/gitignore-templates.json +4 -0
- package/lib/templates/project/vueinternalapp/scripts/graphql-search.sh +191 -0
- package/lib/templates/project/vueinternalapp/scripts/network-senders.mjs +84 -0
- package/lib/templates/project/vueinternalapp/scripts/org-setup-config-schema.mjs +132 -0
- package/lib/templates/project/vueinternalapp/scripts/org-setup-dev.mjs +114 -0
- package/lib/templates/project/vueinternalapp/scripts/org-setup-url.mjs +108 -0
- package/lib/templates/project/vueinternalapp/scripts/org-setup-utils.mjs +569 -0
- package/lib/templates/project/vueinternalapp/scripts/org-setup-xml.mjs +283 -0
- package/lib/templates/project/vueinternalapp/scripts/org-setup.config.json +9 -0
- package/lib/templates/project/vueinternalapp/scripts/org-setup.mjs +2545 -0
- package/lib/templates/project/vueinternalapp/scripts/sf-project-setup.mjs +103 -0
- package/lib/templates/project/vueinternalapp/scripts/soql/account.soql +6 -0
- package/lib/templates/project/vueinternalapp/scripts/validate-org-setup-config.mjs +38 -0
- package/lib/templates/project/vueinternalapp/sfdx-project.json +12 -0
- package/lib/templates/uiBundles/angularbasic/package-lock.json +205 -205
- package/lib/templates/uiBundles/reactbasic/eslint.config.js +8 -12
- package/lib/templates/uiBundles/reactbasic/package-lock.json +188 -188
- package/lib/templates/uiBundles/reactbasic/package.json +5 -5
- package/lib/templates/uiBundles/reactbasic/src/components/ui/button.tsx +1 -1
- package/lib/templates/uiBundles/reactbasic/src/components/ui/sonner.tsx +7 -1
- package/lib/templates/uiBundles/vuebasic/eslint.config.js +6 -1
- package/lib/templates/uiBundles/vuebasic/package-lock.json +198 -198
- package/lib/templates/uiBundles/vuebasic/package.json +5 -5
- package/lib/utils/projectTemplates.d.ts +6 -0
- package/lib/utils/projectTemplates.js +26 -0
- package/lib/utils/projectTemplates.js.map +1 -0
- package/lib/utils/types.d.ts +2 -1
- package/lib/utils/types.js.map +1 -1
- package/lib/utils/uiBundleTemplateUtils.js +5 -0
- package/lib/utils/uiBundleTemplateUtils.js.map +1 -1
- package/package.json +7 -6
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export type { MergedSearchResultsProps } from "./MergedSearchResults.types";
|
|
3
|
+
|
|
4
|
+
// Module scope (not <script setup>): withDefaults hoists its defaults out of setup().
|
|
5
|
+
const DEFAULT_GRID_CLASS = "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3";
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
/**
|
|
10
|
+
* Merged results layout — a single combined grid across every in-scope source,
|
|
11
|
+
* driven by one global pager over the cumulative result set.
|
|
12
|
+
*
|
|
13
|
+
* Counterpart to `<SearchResults>` (one stacked section per source). Reads the
|
|
14
|
+
* aggregate accessors off the `SearchHandle` (`mergedResults`,
|
|
15
|
+
* `globalPagination`, `inScopeSources`), so it honours the config's
|
|
16
|
+
* `pagination.mode` / `mergeOrder` automatically.
|
|
17
|
+
*
|
|
18
|
+
* Layout, top to bottom:
|
|
19
|
+
* - Single-source filter chrome (filters + sort + chips) when the scope is
|
|
20
|
+
* narrowed to one source; omitted under "All".
|
|
21
|
+
* - Results count on its own row above the grid, hidden when empty.
|
|
22
|
+
* - The grid — one card per `mergedResults` item. In a mixed grid each card
|
|
23
|
+
* is topped with an auto source badge (`labelSingular`, falling back to
|
|
24
|
+
* `label`), colored from a palette by config order. Skeletons while loading.
|
|
25
|
+
* - One global pager over the cumulative set.
|
|
26
|
+
*/
|
|
27
|
+
import { computed, useSlots } from "vue";
|
|
28
|
+
import { AlertCircle } from "@lucide/vue";
|
|
29
|
+
import { Alert, AlertDescription, AlertTitle } from "../../../components/ui/alert";
|
|
30
|
+
import { Skeleton } from "../../../components/ui/skeleton";
|
|
31
|
+
import ActiveFilters from "./filters/ActiveFilters.vue";
|
|
32
|
+
import DefaultFilterPanel from "./filters/DefaultFilterPanel.vue";
|
|
33
|
+
import FilterProvider from "./filters/FilterProvider.vue";
|
|
34
|
+
import FilterResetButton from "./filters/FilterResetButton.vue";
|
|
35
|
+
import PaginationControls from "./controls/PaginationControls.vue";
|
|
36
|
+
import SortControl from "./controls/SortControl.vue";
|
|
37
|
+
import type { MergedSearchResultsProps } from "./MergedSearchResults.types";
|
|
38
|
+
import type { ResultRenderer } from "./results/resolveResultRenderer";
|
|
39
|
+
import { RenderVNode } from "./results/RenderVNode";
|
|
40
|
+
import { resolveResultRenderer } from "./results/resolveResultRenderer";
|
|
41
|
+
import { MIN_QUERY_LENGTH } from "../constants";
|
|
42
|
+
import { ALL_SCOPE, type SourceConfig } from "../types";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Palette for the auto source badges in a mixed grid. A source's color is its
|
|
46
|
+
* index in the config's `sources` array, wrapped around the palette.
|
|
47
|
+
*/
|
|
48
|
+
const BADGE_PALETTE = [
|
|
49
|
+
"bg-blue-100 text-blue-700",
|
|
50
|
+
"bg-amber-100 text-amber-700",
|
|
51
|
+
"bg-purple-100 text-purple-700",
|
|
52
|
+
"bg-pink-100 text-pink-700",
|
|
53
|
+
"bg-emerald-100 text-emerald-700",
|
|
54
|
+
"bg-sky-100 text-sky-700",
|
|
55
|
+
] as const;
|
|
56
|
+
|
|
57
|
+
const props = withDefaults(defineProps<MergedSearchResultsProps>(), {
|
|
58
|
+
shortQueryMessage: "Start your search…",
|
|
59
|
+
gridClassName: DEFAULT_GRID_CLASS,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const slots = useSlots();
|
|
63
|
+
|
|
64
|
+
const page = computed(() => props.handle.globalPagination);
|
|
65
|
+
// Distinguishes "haven't typed enough yet" (useSearch short-circuits below
|
|
66
|
+
// MIN_QUERY_LENGTH without fetching) from a genuine zero-match search.
|
|
67
|
+
const isShortQuery = computed(() => props.handle.q.trim().length < MIN_QUERY_LENGTH);
|
|
68
|
+
|
|
69
|
+
// A single source is in scope when the scope is narrowed (selected) or locked.
|
|
70
|
+
// Its filter chrome can render meaningfully; under "All" it can't.
|
|
71
|
+
const singleSourceInScope = computed(() => props.handle.scope !== ALL_SCOPE);
|
|
72
|
+
const soleController = computed(() =>
|
|
73
|
+
singleSourceInScope.value ? props.handle.inScopeSources[0] : undefined,
|
|
74
|
+
);
|
|
75
|
+
// Per-card source badges only make sense when the grid is genuinely mixed.
|
|
76
|
+
const showSourceBadges = computed(() => !singleSourceInScope.value);
|
|
77
|
+
// Badges only show in "All" scope, where `inScopeSources` is every source in
|
|
78
|
+
// config order, so the index here is the source's config position.
|
|
79
|
+
const badgeColorByKey = computed(() => {
|
|
80
|
+
const out: Record<string, string> = {};
|
|
81
|
+
props.handle.inScopeSources.forEach((controller, i) => {
|
|
82
|
+
out[controller.config.key] = BADGE_PALETTE[i % BADGE_PALETTE.length];
|
|
83
|
+
});
|
|
84
|
+
return out;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const skeletons = computed(() => props.skeletonCount ?? page.value.pageSize);
|
|
88
|
+
|
|
89
|
+
const liveMessage = computed(() => {
|
|
90
|
+
const { loading, error, mergedResults } = props.handle;
|
|
91
|
+
if (loading) return "Searching…";
|
|
92
|
+
if (error) return "Search failed.";
|
|
93
|
+
if (isShortQuery.value) return props.shortQueryMessage;
|
|
94
|
+
if (mergedResults.length === 0) return props.emptyMessage ?? "No results found.";
|
|
95
|
+
return `${page.value.totalCount} result${page.value.totalCount === 1 ? "" : "s"}`;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
function getItemKey(sourceKey: string, node: unknown, fallback: number): string {
|
|
99
|
+
const id = node && typeof node === "object" ? (node as { Id?: unknown }).Id : undefined;
|
|
100
|
+
return typeof id === "string" ? `${sourceKey}:${id}` : `${sourceKey}:${fallback}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Cards to render: resolves each node's renderer via the shared helper. */
|
|
104
|
+
const cards = computed(() => {
|
|
105
|
+
const out = [];
|
|
106
|
+
for (const [i, { sourceKey, node }] of props.handle.mergedResults.entries()) {
|
|
107
|
+
const source: SourceConfig | undefined = props.handle.sources[sourceKey]?.config;
|
|
108
|
+
if (!source) continue;
|
|
109
|
+
// Resolve via the shared helper so CMS nodes render through CmsResultRow in
|
|
110
|
+
// the merged grid too; a `renderResult[key]` override (incl. `false`) wins.
|
|
111
|
+
const override = props.renderResult?.[source.key];
|
|
112
|
+
const renderer =
|
|
113
|
+
override === undefined && slots.result
|
|
114
|
+
? (n: unknown) => slots.result!({ sourceKey, node: n, source })
|
|
115
|
+
: resolveResultResolved(source);
|
|
116
|
+
if (renderer === false) continue;
|
|
117
|
+
out.push({
|
|
118
|
+
key: getItemKey(sourceKey, node, i),
|
|
119
|
+
sourceKey,
|
|
120
|
+
badgeLabel: source.labelSingular ?? source.label,
|
|
121
|
+
render: () => renderer(node),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
function resolveResultResolved(source: SourceConfig): ResultRenderer {
|
|
128
|
+
return resolveResultRenderer(source, props.renderResult);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Single-source chrome ---------------------------------------------------------
|
|
132
|
+
const sObjectSource = computed(() => {
|
|
133
|
+
const s = soleController.value?.config;
|
|
134
|
+
return s && s.kind === "sobject" ? s : null;
|
|
135
|
+
});
|
|
136
|
+
const sortBy = computed(() => sObjectSource.value?.sortBy);
|
|
137
|
+
const showFilterPanel = computed(() => (sObjectSource.value?.filterBy?.length ?? 0) > 0);
|
|
138
|
+
const showSortControl = computed(() => (sortBy.value?.length ?? 0) > 0);
|
|
139
|
+
const showActiveChips = computed(() => (soleController.value?.filters.active.length ?? 0) > 0);
|
|
140
|
+
const showChrome = computed(
|
|
141
|
+
() =>
|
|
142
|
+
!!soleController.value &&
|
|
143
|
+
(showFilterPanel.value || showSortControl.value || showActiveChips.value),
|
|
144
|
+
);
|
|
145
|
+
const resetSoleFilters = () => {
|
|
146
|
+
const c = soleController.value;
|
|
147
|
+
c?.filters.active.forEach((f) => c.filters.remove(f.field));
|
|
148
|
+
};
|
|
149
|
+
</script>
|
|
150
|
+
|
|
151
|
+
<template>
|
|
152
|
+
<div :class="['space-y-6', $props.class]">
|
|
153
|
+
<p class="sr-only" role="status" aria-live="polite">{{ liveMessage }}</p>
|
|
154
|
+
|
|
155
|
+
<div v-if="showChrome && soleController" class="flex flex-col gap-4">
|
|
156
|
+
<FilterProvider
|
|
157
|
+
v-if="showFilterPanel && sObjectSource"
|
|
158
|
+
:filters="soleController.filters.active"
|
|
159
|
+
@filter-change="soleController.filters.set"
|
|
160
|
+
@filter-remove="soleController.filters.remove"
|
|
161
|
+
@reset="resetSoleFilters"
|
|
162
|
+
>
|
|
163
|
+
<div class="rounded-md border p-3 space-y-3">
|
|
164
|
+
<div class="flex items-center justify-between">
|
|
165
|
+
<h2 class="text-sm font-semibold">Filters</h2>
|
|
166
|
+
<FilterResetButton size="sm" />
|
|
167
|
+
</div>
|
|
168
|
+
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
169
|
+
<DefaultFilterPanel :source="sObjectSource" />
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</FilterProvider>
|
|
173
|
+
|
|
174
|
+
<div v-if="showSortControl || showActiveChips" class="flex flex-wrap items-center gap-2">
|
|
175
|
+
<SortControl
|
|
176
|
+
v-if="showSortControl && sortBy"
|
|
177
|
+
:configs="sortBy"
|
|
178
|
+
:sort="soleController.sort.current"
|
|
179
|
+
@sort-change="soleController.sort.set"
|
|
180
|
+
/>
|
|
181
|
+
<ActiveFilters
|
|
182
|
+
v-if="showActiveChips"
|
|
183
|
+
:filters="soleController.filters.active"
|
|
184
|
+
@remove="soleController.filters.remove"
|
|
185
|
+
/>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<!-- Page-level error — set only when every in-scope source failed (or a
|
|
190
|
+
whole-request/network failure). Shown once, above the grid. -->
|
|
191
|
+
<Alert v-if="handle.error" variant="destructive" role="alert">
|
|
192
|
+
<AlertCircle aria-hidden="true" />
|
|
193
|
+
<AlertTitle>Search failed</AlertTitle>
|
|
194
|
+
<AlertDescription>{{ handle.error }}</AlertDescription>
|
|
195
|
+
</Alert>
|
|
196
|
+
|
|
197
|
+
<!-- Per-source errors — a source that failed while a sibling succeeded
|
|
198
|
+
surfaces its own banner without blanking the shared grid. -->
|
|
199
|
+
<template v-if="!handle.error">
|
|
200
|
+
<template v-for="c in handle.inScopeSources" :key="c.config.key">
|
|
201
|
+
<Alert v-if="c.error" variant="destructive" role="alert">
|
|
202
|
+
<AlertCircle aria-hidden="true" />
|
|
203
|
+
<AlertTitle>Failed to load {{ c.config.label }}</AlertTitle>
|
|
204
|
+
<AlertDescription>{{ c.error }}</AlertDescription>
|
|
205
|
+
</Alert>
|
|
206
|
+
</template>
|
|
207
|
+
</template>
|
|
208
|
+
|
|
209
|
+
<p
|
|
210
|
+
v-if="!handle.loading && !handle.error && page.totalCount > 0"
|
|
211
|
+
class="text-sm text-muted-foreground"
|
|
212
|
+
>
|
|
213
|
+
{{ page.totalCount }} result{{ page.totalCount === 1 ? "" : "s" }}
|
|
214
|
+
</p>
|
|
215
|
+
|
|
216
|
+
<div v-if="handle.loading" :class="gridClassName">
|
|
217
|
+
<Skeleton v-for="i in Math.min(skeletons, 6)" :key="i" class="h-48 w-full rounded-lg" />
|
|
218
|
+
</div>
|
|
219
|
+
<template v-else-if="handle.error" />
|
|
220
|
+
<p v-else-if="isShortQuery" class="py-12 text-center text-sm text-muted-foreground">
|
|
221
|
+
{{ shortQueryMessage }}
|
|
222
|
+
</p>
|
|
223
|
+
<p
|
|
224
|
+
v-else-if="handle.mergedResults.length === 0"
|
|
225
|
+
class="py-12 text-center text-sm text-muted-foreground"
|
|
226
|
+
>
|
|
227
|
+
{{ emptyMessage ?? "No results found." }}
|
|
228
|
+
</p>
|
|
229
|
+
<ul v-else role="list" :class="gridClassName">
|
|
230
|
+
<li
|
|
231
|
+
v-for="card in cards"
|
|
232
|
+
:key="card.key"
|
|
233
|
+
class="flex flex-col gap-1.5 [&>:last-child]:min-h-0 [&>:last-child]:flex-1"
|
|
234
|
+
>
|
|
235
|
+
<span
|
|
236
|
+
v-if="showSourceBadges"
|
|
237
|
+
:class="[
|
|
238
|
+
'w-fit rounded-full px-2.5 py-0.5 text-xs font-medium',
|
|
239
|
+
badgeColorByKey[card.sourceKey],
|
|
240
|
+
]"
|
|
241
|
+
>
|
|
242
|
+
{{ card.badgeLabel }}
|
|
243
|
+
</span>
|
|
244
|
+
<RenderVNode :render="card.render" />
|
|
245
|
+
</li>
|
|
246
|
+
</ul>
|
|
247
|
+
|
|
248
|
+
<PaginationControls
|
|
249
|
+
v-if="page.totalCount > 0"
|
|
250
|
+
:page-index="page.pageIndex"
|
|
251
|
+
:page-count="page.pageCount"
|
|
252
|
+
:has-next-page="page.hasNextPage"
|
|
253
|
+
:has-previous-page="page.hasPreviousPage"
|
|
254
|
+
:page-size="page.pageSize"
|
|
255
|
+
:page-size-options="page.pageSizeOptions"
|
|
256
|
+
:on-next-page="page.goToNextPage"
|
|
257
|
+
:on-previous-page="page.goToPreviousPage"
|
|
258
|
+
:on-go-to-page="page.goToPage"
|
|
259
|
+
:on-page-size-change="page.setPageSize"
|
|
260
|
+
:disabled="handle.loading || !!handle.error"
|
|
261
|
+
/>
|
|
262
|
+
</div>
|
|
263
|
+
</template>
|
package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/Search.vue
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SObjectSourceConfig } from "../types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Locks the search to a single source. Reuses the same shared `config.json`
|
|
6
|
+
* but shows only results matching the given (kind, key) tuple.
|
|
7
|
+
*
|
|
8
|
+
* When set:
|
|
9
|
+
* - The scope dropdown is hidden.
|
|
10
|
+
* - URL `?scope=` is not written (the page route already implies the source).
|
|
11
|
+
* - Only the matching source is fetched and rendered.
|
|
12
|
+
*/
|
|
13
|
+
export interface RestrictTo {
|
|
14
|
+
kind: SObjectSourceConfig["kind"];
|
|
15
|
+
key: string;
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
/**
|
|
21
|
+
* Drop-in search component. Given a `SearchConfig`, renders a complete search
|
|
22
|
+
* experience: search bar, an aggregate result count, and results.
|
|
23
|
+
*
|
|
24
|
+
* The results layout follows the config's pagination mode — no separate prop:
|
|
25
|
+
* - `pagination.mode: "per-source"` (default): one stacked section per
|
|
26
|
+
* source, each with its own filters, sort, and pagination.
|
|
27
|
+
* - `pagination.mode: "merged"`: a single combined grid across every in-scope
|
|
28
|
+
* source, driven by one global pager over the cumulative result set.
|
|
29
|
+
*
|
|
30
|
+
* Simplest integration:
|
|
31
|
+
* <Search :config="config" />
|
|
32
|
+
*
|
|
33
|
+
* Override points:
|
|
34
|
+
* - `renderResult[sourceKey]` — replace the default row renderer for one
|
|
35
|
+
* source (a Vue render function `(node) => VNodeChild`). `false` hides the
|
|
36
|
+
* source. The `#result="{ sourceKey, node, source }"` scoped slot overrides
|
|
37
|
+
* rendering for every source without a `renderResult` entry.
|
|
38
|
+
* - `renderFilters[sourceKey]` — replace the default filter panel for one
|
|
39
|
+
* source. `false` suppresses filters for that source.
|
|
40
|
+
* - `renderHeader` prop / `#header="{ handle }"` slot — replace the default
|
|
41
|
+
* header (title + subtitle).
|
|
42
|
+
* - `searchPlaceholder` — placeholder text for the search input.
|
|
43
|
+
* - `title`, `subtitle` — defaults passed to the built-in header.
|
|
44
|
+
*/
|
|
45
|
+
import { computed, useSlots, type VNodeChild } from "vue";
|
|
46
|
+
import { AlertCircle } from "@lucide/vue";
|
|
47
|
+
import { Alert, AlertDescription, AlertTitle } from "../../../components/ui/alert";
|
|
48
|
+
import { useSearch } from "../composables/useSearch";
|
|
49
|
+
import SearchBar from "./controls/SearchBar.vue";
|
|
50
|
+
import ScopeSelector from "./controls/ScopeSelector.vue";
|
|
51
|
+
import SearchResults from "./SearchResults.vue";
|
|
52
|
+
import MergedSearchResults from "./MergedSearchResults.vue";
|
|
53
|
+
import { RenderVNode } from "./results/RenderVNode";
|
|
54
|
+
import type { ResultRenderer } from "./results/resolveResultRenderer";
|
|
55
|
+
import { ALL_SCOPE } from "../types";
|
|
56
|
+
import type { SearchConfig, SearchHandle } from "../types";
|
|
57
|
+
|
|
58
|
+
type FilterRenderer = (() => VNodeChild) | false;
|
|
59
|
+
|
|
60
|
+
const props = withDefaults(
|
|
61
|
+
defineProps<{
|
|
62
|
+
config: SearchConfig;
|
|
63
|
+
renderResult?: Record<string, ResultRenderer>;
|
|
64
|
+
renderFilters?: Record<string, FilterRenderer>;
|
|
65
|
+
emptyMessages?: Record<string, string>;
|
|
66
|
+
searchPlaceholder?: string;
|
|
67
|
+
title?: string;
|
|
68
|
+
subtitle?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Lock the search to a single source by `(kind, key)`. Hides the scope
|
|
71
|
+
* dropdown and forces results to that one source.
|
|
72
|
+
*/
|
|
73
|
+
restrictTo?: RestrictTo;
|
|
74
|
+
/**
|
|
75
|
+
* Show the source-scope dropdown next to the search bar. Defaults to `true`
|
|
76
|
+
* when the config has 2+ sources, `false` otherwise. Always hidden when
|
|
77
|
+
* `restrictTo` is set.
|
|
78
|
+
*/
|
|
79
|
+
showScopeSelector?: boolean;
|
|
80
|
+
/** Label for the "search everything" entry in the scope dropdown. */
|
|
81
|
+
allScopeLabel?: string;
|
|
82
|
+
/** Optional override for the entire header region. */
|
|
83
|
+
renderHeader?: (handle: SearchHandle) => VNodeChild;
|
|
84
|
+
class?: string;
|
|
85
|
+
/** Root element tag; use `"main"` when this owns its route's landmark (WCAG 1.3.1). */
|
|
86
|
+
as?: "div" | "main";
|
|
87
|
+
}>(),
|
|
88
|
+
{ searchPlaceholder: "Search…", title: "Search", as: "div", showScopeSelector: undefined },
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const slots = useSlots();
|
|
92
|
+
|
|
93
|
+
// Resolve restrictTo to a lockedScope value, surfacing a clear warning if the
|
|
94
|
+
// (kind, key) pair doesn't match any source.
|
|
95
|
+
const lockedScope = computed<string | undefined>(() => {
|
|
96
|
+
const restrict = props.restrictTo;
|
|
97
|
+
if (!restrict) return undefined;
|
|
98
|
+
const match = props.config.sources.find(
|
|
99
|
+
(s) => s.kind === restrict.kind && s.key === restrict.key,
|
|
100
|
+
);
|
|
101
|
+
if (!match) {
|
|
102
|
+
console.warn(
|
|
103
|
+
`<Search restrictTo>: no source with kind="${restrict.kind}" and key="${restrict.key}" in config. Falling back to "all".`,
|
|
104
|
+
);
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return match.key;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const handle = useSearch(props.config, { lockedScope: lockedScope.value });
|
|
111
|
+
|
|
112
|
+
// The results layout follows the pagination mode — a "merged" config means a
|
|
113
|
+
// single combined grid, "per-source" (default) means one section per source.
|
|
114
|
+
const merged = computed(() => props.config.pagination?.mode === "merged");
|
|
115
|
+
const totalResults = computed(() =>
|
|
116
|
+
Object.values(handle.sources).reduce(
|
|
117
|
+
(acc, controller) => acc + (controller.result?.totalCount ?? 0),
|
|
118
|
+
0,
|
|
119
|
+
),
|
|
120
|
+
);
|
|
121
|
+
const shouldShowScope = computed(
|
|
122
|
+
() => !handle.scopeLocked && (props.showScopeSelector ?? props.config.sources.length >= 2),
|
|
123
|
+
);
|
|
124
|
+
// The aggregate count is only meaningful when results from more than one source
|
|
125
|
+
// are combined. In merged mode the results grid renders its own count row.
|
|
126
|
+
const sourcesInScope = computed(() =>
|
|
127
|
+
handle.scope === ALL_SCOPE ? props.config.sources.length : 1,
|
|
128
|
+
);
|
|
129
|
+
const showTotalResults = computed(() => !merged.value && sourcesInScope.value >= 2);
|
|
130
|
+
const emptyMessage = computed(() => props.emptyMessages?.[handle.scope]);
|
|
131
|
+
</script>
|
|
132
|
+
|
|
133
|
+
<template>
|
|
134
|
+
<component
|
|
135
|
+
:is="as"
|
|
136
|
+
:class="['max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-6 space-y-6', $props.class]"
|
|
137
|
+
>
|
|
138
|
+
<RenderVNode v-if="renderHeader" :render="() => renderHeader!(handle)" />
|
|
139
|
+
<slot v-else-if="slots.header" name="header" :handle="handle" />
|
|
140
|
+
<header v-else class="space-y-2">
|
|
141
|
+
<h1 class="text-2xl font-bold">{{ title }}</h1>
|
|
142
|
+
<p v-if="subtitle" class="text-sm text-muted-foreground">{{ subtitle }}</p>
|
|
143
|
+
</header>
|
|
144
|
+
|
|
145
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
146
|
+
<SearchBar :value="handle.q" :placeholder="searchPlaceholder" @change="handle.setQ" />
|
|
147
|
+
<ScopeSelector
|
|
148
|
+
v-if="shouldShowScope"
|
|
149
|
+
:config="config"
|
|
150
|
+
:scope="handle.scope"
|
|
151
|
+
:all-label="allScopeLabel"
|
|
152
|
+
:discovered-content-types="handle.discoveredContentTypes"
|
|
153
|
+
@scope-change="handle.setScope"
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<MergedSearchResults
|
|
158
|
+
v-if="merged"
|
|
159
|
+
:handle="handle"
|
|
160
|
+
:render-result="renderResult"
|
|
161
|
+
:empty-message="emptyMessage"
|
|
162
|
+
>
|
|
163
|
+
<template v-if="slots.result" #result="slotProps"
|
|
164
|
+
><slot name="result" v-bind="slotProps"
|
|
165
|
+
/></template>
|
|
166
|
+
</MergedSearchResults>
|
|
167
|
+
<template v-else>
|
|
168
|
+
<!-- Page-level failure (every queried source failed, or a whole-request/
|
|
169
|
+
network error). Show the single consolidated banner and nothing else
|
|
170
|
+
— the per-source sections would each repeat the same error. -->
|
|
171
|
+
<Alert v-if="handle.error" variant="destructive" role="alert">
|
|
172
|
+
<AlertCircle aria-hidden="true" />
|
|
173
|
+
<AlertTitle>Search failed</AlertTitle>
|
|
174
|
+
<AlertDescription>{{ handle.error }}</AlertDescription>
|
|
175
|
+
</Alert>
|
|
176
|
+
<template v-else>
|
|
177
|
+
<!-- Aggregate count on its own row, directly above the results — only in
|
|
178
|
+
"All" scope, where the per-source sections don't each carry a single
|
|
179
|
+
meaningful total. -->
|
|
180
|
+
<p
|
|
181
|
+
v-if="showTotalResults && !handle.loading"
|
|
182
|
+
class="text-sm text-muted-foreground"
|
|
183
|
+
role="status"
|
|
184
|
+
>
|
|
185
|
+
{{ totalResults }} total result{{ totalResults === 1 ? "" : "s" }}
|
|
186
|
+
</p>
|
|
187
|
+
<SearchResults
|
|
188
|
+
:handle="handle"
|
|
189
|
+
:render-result="renderResult"
|
|
190
|
+
:render-filters="renderFilters"
|
|
191
|
+
:empty-messages="emptyMessages"
|
|
192
|
+
>
|
|
193
|
+
<template v-if="slots.result" #result="slotProps"
|
|
194
|
+
><slot name="result" v-bind="slotProps"
|
|
195
|
+
/></template>
|
|
196
|
+
</SearchResults>
|
|
197
|
+
</template>
|
|
198
|
+
</template>
|
|
199
|
+
</component>
|
|
200
|
+
</template>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Renders one SourceSection per configured source, in declaration order.
|
|
4
|
+
*
|
|
5
|
+
* Customization is optional — sensible defaults kick in for any source you
|
|
6
|
+
* don't override:
|
|
7
|
+
* - `renderResult[sourceKey]` — per-node render function. Default: title from
|
|
8
|
+
* `displayFields[0]`, subtitle from the rest, optional `<RouterLink>` driven
|
|
9
|
+
* by `routePattern`. Alternatively provide the scoped slot
|
|
10
|
+
* `#result="{ sourceKey, node, source }"` for all sources.
|
|
11
|
+
* - `renderFilters[sourceKey]` — sidebar filter UI. Default: one input per
|
|
12
|
+
* `filterBy` entry, with picklist options resolved from inline config or
|
|
13
|
+
* auto-fetched from the GraphQL aggregate API.
|
|
14
|
+
*
|
|
15
|
+
* Pass `false` for either entry to suppress the default for that source.
|
|
16
|
+
*
|
|
17
|
+
* Filter chrome (sidebar + active-filter chips + sort dropdown) is
|
|
18
|
+
* automatically hidden when `handle.scope === "all"`. Pre-existing filter /
|
|
19
|
+
* sort selections stay in state, so narrowing the scope brings them back.
|
|
20
|
+
*/
|
|
21
|
+
import { h, useSlots, type VNodeChild } from "vue";
|
|
22
|
+
import SourceSection from "./SourceSection.vue";
|
|
23
|
+
import DefaultFilterPanel from "./filters/DefaultFilterPanel.vue";
|
|
24
|
+
import { resolveResultRenderer, type ResultRenderer } from "./results/resolveResultRenderer";
|
|
25
|
+
import { ALL_SCOPE, parseScope, type SearchHandle, type SourceConfig } from "../types";
|
|
26
|
+
import { isCmsSearchSupported } from "../adapters/cms/api/orgApiVersionService";
|
|
27
|
+
|
|
28
|
+
type FilterRenderer = (() => VNodeChild) | false;
|
|
29
|
+
|
|
30
|
+
const props = defineProps<{
|
|
31
|
+
handle: SearchHandle;
|
|
32
|
+
renderResult?: Record<string, ResultRenderer>;
|
|
33
|
+
renderFilters?: Record<string, FilterRenderer>;
|
|
34
|
+
emptyMessages?: Record<string, string>;
|
|
35
|
+
}>();
|
|
36
|
+
|
|
37
|
+
const slots = useSlots();
|
|
38
|
+
|
|
39
|
+
/** Resolves the per-node renderer: map override > `#result` slot > kind default. */
|
|
40
|
+
function resolveRenderer(source: SourceConfig): ResultRenderer {
|
|
41
|
+
const override = props.renderResult?.[source.key];
|
|
42
|
+
if (override === undefined && slots.result) {
|
|
43
|
+
return (node: unknown) => slots.result!({ sourceKey: source.key, node, source });
|
|
44
|
+
}
|
|
45
|
+
return resolveResultRenderer(source, props.renderResult);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveFilters(source: SourceConfig): (() => VNodeChild) | undefined {
|
|
49
|
+
const override = props.renderFilters?.[source.key];
|
|
50
|
+
if (override === false) return undefined;
|
|
51
|
+
if (override) return override;
|
|
52
|
+
// CMS sources have no `filterBy`; the guard keeps their chrome off.
|
|
53
|
+
if (source.kind === "sobject" && (source.filterBy?.length ?? 0) > 0) {
|
|
54
|
+
return () => h(DefaultFilterPanel, { source });
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Sections to render for the current scope (hidden sources are omitted). */
|
|
60
|
+
function visibleSections() {
|
|
61
|
+
const filtersHidden = props.handle.scope === ALL_SCOPE;
|
|
62
|
+
// Match sections against the base scope so a "<cmsKey>:<fqn>" scope still
|
|
63
|
+
// renders the CMS source's section (the fqn only narrows the query).
|
|
64
|
+
const { base: scopeBase } = parseScope(props.handle.scope);
|
|
65
|
+
const out = [];
|
|
66
|
+
for (const [key, controller] of Object.entries(props.handle.sources)) {
|
|
67
|
+
if (scopeBase !== ALL_SCOPE && scopeBase !== key) continue;
|
|
68
|
+
// CMS search is skipped on builds below the CMS-search API version, so a CMS
|
|
69
|
+
// source never returns rows — hide its section (same gate as the fetch skip).
|
|
70
|
+
if (controller.config.kind === "cms" && !isCmsSearchSupported()) continue;
|
|
71
|
+
// A `renderResult[key]` override of `false` hides the source.
|
|
72
|
+
const renderResult = resolveRenderer(controller.config);
|
|
73
|
+
if (renderResult === false) continue;
|
|
74
|
+
out.push({
|
|
75
|
+
key,
|
|
76
|
+
controller,
|
|
77
|
+
renderResult,
|
|
78
|
+
renderFilters: resolveFilters(controller.config),
|
|
79
|
+
filtersHidden,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
<template>
|
|
87
|
+
<div class="space-y-10">
|
|
88
|
+
<SourceSection
|
|
89
|
+
v-for="section in visibleSections()"
|
|
90
|
+
:key="section.key"
|
|
91
|
+
:controller="section.controller"
|
|
92
|
+
:render-result="section.renderResult"
|
|
93
|
+
:render-filters="section.renderFilters"
|
|
94
|
+
:hide-filter-chrome="section.filtersHidden"
|
|
95
|
+
:empty-message="emptyMessages?.[section.key]"
|
|
96
|
+
:q="handle.q"
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
</template>
|