@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,44 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
Select,
|
|
4
|
+
SelectContent,
|
|
5
|
+
SelectItem,
|
|
6
|
+
SelectTrigger,
|
|
7
|
+
SelectValue,
|
|
8
|
+
} from "../../../../../components/ui/select";
|
|
9
|
+
import { useFilterField } from "../FilterContext";
|
|
10
|
+
import FilterFieldWrapper from "./FilterFieldWrapper.vue";
|
|
11
|
+
|
|
12
|
+
const ALL_VALUE = "__all__";
|
|
13
|
+
|
|
14
|
+
const props = defineProps<{
|
|
15
|
+
field: string;
|
|
16
|
+
label: string;
|
|
17
|
+
options: Array<{ value: string; label: string }>;
|
|
18
|
+
helpText?: string;
|
|
19
|
+
}>();
|
|
20
|
+
|
|
21
|
+
const { value, onChange } = useFilterField(() => props.field);
|
|
22
|
+
const id = `filter-${props.field}`;
|
|
23
|
+
|
|
24
|
+
const onValueChange = (v: unknown) => {
|
|
25
|
+
if (v === ALL_VALUE) onChange(undefined);
|
|
26
|
+
else onChange({ field: props.field, label: props.label, type: "picklist", value: String(v) });
|
|
27
|
+
};
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<FilterFieldWrapper :label="label" :html-for="id" :help-text="helpText">
|
|
32
|
+
<Select :model-value="value?.value ?? ALL_VALUE" @update:model-value="onValueChange">
|
|
33
|
+
<SelectTrigger :id="id" class="w-full">
|
|
34
|
+
<SelectValue :placeholder="`Select ${label.toLowerCase()}`" />
|
|
35
|
+
</SelectTrigger>
|
|
36
|
+
<SelectContent>
|
|
37
|
+
<SelectItem :value="ALL_VALUE">All</SelectItem>
|
|
38
|
+
<SelectItem v-for="opt in options" :key="opt.value" :value="opt.value">
|
|
39
|
+
{{ opt.label }}
|
|
40
|
+
</SelectItem>
|
|
41
|
+
</SelectContent>
|
|
42
|
+
</Select>
|
|
43
|
+
</FilterFieldWrapper>
|
|
44
|
+
</template>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, watch } from "vue";
|
|
3
|
+
import { Input } from "../../../../../components/ui/input";
|
|
4
|
+
import { useFilterField } from "../FilterContext";
|
|
5
|
+
import FilterFieldWrapper from "./FilterFieldWrapper.vue";
|
|
6
|
+
import { debounce, FILTER_DEBOUNCE_MS } from "../../../utils/debounce";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
field: string;
|
|
10
|
+
label: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
helpText?: string;
|
|
13
|
+
}>();
|
|
14
|
+
|
|
15
|
+
const { value, onChange } = useFilterField(() => props.field);
|
|
16
|
+
const draft = ref<string>(value.value?.value ?? "");
|
|
17
|
+
|
|
18
|
+
// One stable debounced caller; it always reads the latest props/onChange.
|
|
19
|
+
const emitDebounced = debounce((next: string) => {
|
|
20
|
+
if (next === "") onChange(undefined);
|
|
21
|
+
else onChange({ field: props.field, label: props.label, type: "text", value: next });
|
|
22
|
+
}, FILTER_DEBOUNCE_MS);
|
|
23
|
+
|
|
24
|
+
watch(
|
|
25
|
+
() => value.value?.value,
|
|
26
|
+
(v) => {
|
|
27
|
+
draft.value = v ?? "";
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const onInput = (next: string | number | undefined) => {
|
|
32
|
+
const text = String(next ?? "");
|
|
33
|
+
draft.value = text;
|
|
34
|
+
emitDebounced(text);
|
|
35
|
+
};
|
|
36
|
+
const id = `filter-${props.field}`;
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<FilterFieldWrapper :label="label" :html-for="id" :help-text="helpText">
|
|
41
|
+
<Input
|
|
42
|
+
:id="id"
|
|
43
|
+
type="text"
|
|
44
|
+
:model-value="draft"
|
|
45
|
+
:placeholder="placeholder"
|
|
46
|
+
@update:model-value="onInput"
|
|
47
|
+
/>
|
|
48
|
+
</FilterFieldWrapper>
|
|
49
|
+
</template>
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Per-node row for a CMS source. Used by both the per-source section
|
|
4
|
+
* (`SearchResults`) and the merged grid (`MergedSearchResults`) via the shared
|
|
5
|
+
* `resolveResultRenderer` whenever `source.kind === "cms"`.
|
|
6
|
+
*
|
|
7
|
+
* Renders a title, a published-date subtitle, an optional content-type badge,
|
|
8
|
+
* and a vue-router `<RouterLink>` when a route can be built.
|
|
9
|
+
*
|
|
10
|
+
* Link target: `source.routePattern` tokens `:id` (managedContentId), `:key`
|
|
11
|
+
* (managedContentKey), `:contentType` (contentType). No routePattern -> a plain
|
|
12
|
+
* (non-linked) row.
|
|
13
|
+
*
|
|
14
|
+
* The content-type label is rendered only when its FQN passes `isValidCmsFqn`.
|
|
15
|
+
*/
|
|
16
|
+
import { computed } from "vue";
|
|
17
|
+
import { RouterLink } from "vue-router";
|
|
18
|
+
import type { CmsSourceConfig, CmsSearchItem } from "../../adapters/cms/types";
|
|
19
|
+
import { formatContentTypeLabel, isValidCmsFqn } from "../../adapters/cms/contentTypeUtils";
|
|
20
|
+
|
|
21
|
+
const EM_BLOCK_SPLIT = /(<(?:em)>[\s\S]*?<\/(?:em)>)/g;
|
|
22
|
+
const EM_BLOCK_EXEC = /^<(?:em)>([\s\S]*?)<\/(?:em)>$/;
|
|
23
|
+
const STRAY_TAG = /<\/?[a-zA-Z][^>]*>/g;
|
|
24
|
+
const ROUTE_TOKEN = /:([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
25
|
+
|
|
26
|
+
/** Formats an ISO date string for display; echoes the raw string on failure. */
|
|
27
|
+
function formatPublishedDate(dateStr: string): string {
|
|
28
|
+
const date = new Date(dateStr);
|
|
29
|
+
if (isNaN(date.getTime())) return dateStr;
|
|
30
|
+
return date.toLocaleDateString(undefined, {
|
|
31
|
+
year: "numeric",
|
|
32
|
+
month: "short",
|
|
33
|
+
day: "numeric",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Substitutes `:id` / `:key` / `:contentType` tokens in a route pattern with
|
|
39
|
+
* the item's values (URL-encoded). Returns `null` if a required token has no
|
|
40
|
+
* value.
|
|
41
|
+
*/
|
|
42
|
+
function resolveRoute(pattern: string, item: CmsSearchItem): string | null {
|
|
43
|
+
const tokens: Record<string, string | undefined> = {
|
|
44
|
+
id: item.managedContentId,
|
|
45
|
+
key: item.managedContentKey,
|
|
46
|
+
contentType: item.contentType,
|
|
47
|
+
};
|
|
48
|
+
let failed = false;
|
|
49
|
+
const out = pattern.replace(ROUTE_TOKEN, (_match, token: string) => {
|
|
50
|
+
const value = tokens[token];
|
|
51
|
+
if (value == null || value === "") {
|
|
52
|
+
failed = true;
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
return encodeURIComponent(value);
|
|
56
|
+
});
|
|
57
|
+
return failed ? null : out;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* - Decodes every HTML entity the browser knows (named + numeric).
|
|
62
|
+
* - Runs twice to unwrap the double-encoding CMS search sometimes returns.
|
|
63
|
+
*/
|
|
64
|
+
function decodeEntities(input: string): string {
|
|
65
|
+
if (typeof document === "undefined") return input;
|
|
66
|
+
const el = document.createElement("textarea");
|
|
67
|
+
const pass = (s: string) => {
|
|
68
|
+
el.innerHTML = s;
|
|
69
|
+
return el.value;
|
|
70
|
+
};
|
|
71
|
+
return pass(pass(input));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** A snippet segment: plain text or a highlighted (`<em>`) run. */
|
|
75
|
+
interface Segment {
|
|
76
|
+
text: string;
|
|
77
|
+
highlight: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Parses a snippet that may contain `<em>…</em>` segments into highlighted
|
|
82
|
+
* segments, and strips any other HTML tags so their content renders as plain
|
|
83
|
+
* text. Rendered through text interpolation, so no HTML is ever injected.
|
|
84
|
+
*/
|
|
85
|
+
function highlightSegments(snippet: string): Segment[] {
|
|
86
|
+
const decoded = decodeEntities(snippet);
|
|
87
|
+
return decoded.split(EM_BLOCK_SPLIT).map((part) => {
|
|
88
|
+
const emphasisMatch = EM_BLOCK_EXEC.exec(part);
|
|
89
|
+
if (emphasisMatch) return { text: emphasisMatch[1].replace(STRAY_TAG, ""), highlight: true };
|
|
90
|
+
return { text: part.replace(STRAY_TAG, ""), highlight: false };
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function toPlainText(raw: string): string {
|
|
95
|
+
return decodeEntities(raw).replace(STRAY_TAG, "");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const props = defineProps<{ node: unknown; source: CmsSourceConfig }>();
|
|
99
|
+
|
|
100
|
+
const item = computed(() => props.node as CmsSearchItem);
|
|
101
|
+
const titleSegments = computed(() => (item.value.title ? highlightSegments(item.value.title) : []));
|
|
102
|
+
const snippetSegments = computed(() =>
|
|
103
|
+
item.value.highlightedSnippet ? highlightSegments(item.value.highlightedSnippet) : [],
|
|
104
|
+
);
|
|
105
|
+
const subtitle = computed(() => {
|
|
106
|
+
const publishedDate = item.value.managedContentChannelDeliveryDetails?.[0]?.publishedDate;
|
|
107
|
+
const showType =
|
|
108
|
+
typeof item.value.contentType === "string" && isValidCmsFqn(item.value.contentType);
|
|
109
|
+
const parts: string[] = [];
|
|
110
|
+
if (showType) parts.push(formatContentTypeLabel(item.value.contentType));
|
|
111
|
+
if (publishedDate) parts.push(`Published: ${formatPublishedDate(publishedDate)}`);
|
|
112
|
+
return parts.join(" · ");
|
|
113
|
+
});
|
|
114
|
+
// Only render a drill-down link when the source explicitly configures a
|
|
115
|
+
// routePattern. Without one, content rows render as plain (non-linked) rows.
|
|
116
|
+
const href = computed(() =>
|
|
117
|
+
props.source.routePattern ? resolveRoute(props.source.routePattern, item.value) : null,
|
|
118
|
+
);
|
|
119
|
+
const linkLabel = computed(
|
|
120
|
+
() => `View content: ${item.value.title ? toPlainText(item.value.title) : "content"}`,
|
|
121
|
+
);
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<template>
|
|
125
|
+
<component
|
|
126
|
+
:is="href ? RouterLink : 'div'"
|
|
127
|
+
v-bind="
|
|
128
|
+
href
|
|
129
|
+
? {
|
|
130
|
+
to: href,
|
|
131
|
+
class: 'block hover:bg-accent rounded-md px-3 -mx-3 py-1 transition-colors',
|
|
132
|
+
'aria-label': linkLabel,
|
|
133
|
+
}
|
|
134
|
+
: { class: 'px-3 -mx-3 py-1' }
|
|
135
|
+
"
|
|
136
|
+
>
|
|
137
|
+
<span class="font-medium">
|
|
138
|
+
<template v-if="titleSegments.length > 0">
|
|
139
|
+
<template v-for="(seg, i) in titleSegments" :key="i">
|
|
140
|
+
<mark
|
|
141
|
+
v-if="seg.highlight"
|
|
142
|
+
class="bg-yellow-200 dark:bg-yellow-700/60 rounded-sm px-0.5"
|
|
143
|
+
>{{ seg.text }}</mark
|
|
144
|
+
>
|
|
145
|
+
<template v-else>{{ seg.text }}</template>
|
|
146
|
+
</template>
|
|
147
|
+
</template>
|
|
148
|
+
<template v-else>—</template>
|
|
149
|
+
</span>
|
|
150
|
+
<p v-if="snippetSegments.length > 0" class="text-sm text-neutral-600 dark:text-neutral-300">
|
|
151
|
+
<template v-for="(seg, i) in snippetSegments" :key="i">
|
|
152
|
+
<mark v-if="seg.highlight" class="bg-yellow-200 dark:bg-yellow-700/60 rounded-sm px-0.5">{{
|
|
153
|
+
seg.text
|
|
154
|
+
}}</mark>
|
|
155
|
+
<template v-else>{{ seg.text }}</template>
|
|
156
|
+
</template>
|
|
157
|
+
</p>
|
|
158
|
+
<p v-if="subtitle" class="text-sm text-neutral-600 dark:text-neutral-300">{{ subtitle }}</p>
|
|
159
|
+
</component>
|
|
160
|
+
</template>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Default per-node row used when no `renderResult` is supplied for a source.
|
|
4
|
+
*
|
|
5
|
+
* Layout:
|
|
6
|
+
* - Title: first entry in `displayFields` (preferring displayValue).
|
|
7
|
+
* - Subtitle: remaining display fields joined with " · ".
|
|
8
|
+
* - When `routePattern` is configured on the source, the row is wrapped in a
|
|
9
|
+
* vue-router `<RouterLink>` whose `to` is built by substituting `:fieldName`
|
|
10
|
+
* tokens with the corresponding field values (special token `:id` resolves
|
|
11
|
+
* to the configured `idField`, default "Id").
|
|
12
|
+
*/
|
|
13
|
+
import { computed } from "vue";
|
|
14
|
+
import { RouterLink } from "vue-router";
|
|
15
|
+
import { fieldValue } from "../../utils/fieldUtils";
|
|
16
|
+
import type { DisplayField, SObjectSourceConfig } from "../../types";
|
|
17
|
+
|
|
18
|
+
const ROUTE_TOKEN = /:([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
19
|
+
|
|
20
|
+
function fieldName(f: DisplayField): string {
|
|
21
|
+
return typeof f === "string" ? f : f.name;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Resolves a single dot-path against a node, returning a string or null. */
|
|
25
|
+
function readPath(node: unknown, path: string): string | null {
|
|
26
|
+
if (!node || typeof node !== "object") return null;
|
|
27
|
+
const parts = path.split(".");
|
|
28
|
+
let cursor: unknown = node;
|
|
29
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
30
|
+
if (!cursor || typeof cursor !== "object") return null;
|
|
31
|
+
cursor = (cursor as Record<string, unknown>)[parts[i]];
|
|
32
|
+
}
|
|
33
|
+
const leaf = (cursor as Record<string, unknown> | null)?.[parts[parts.length - 1]];
|
|
34
|
+
if (leaf == null) return null;
|
|
35
|
+
if (typeof leaf === "object") {
|
|
36
|
+
return fieldValue(leaf as { value?: unknown; displayValue?: string | null });
|
|
37
|
+
}
|
|
38
|
+
return String(leaf);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Reads the user-facing string for a top-level display field. */
|
|
42
|
+
function readDisplayField(node: unknown, field: DisplayField): string | null {
|
|
43
|
+
if (typeof field === "string") return readPath(node, field);
|
|
44
|
+
if ("raw" in field) return readPath(node, field.name);
|
|
45
|
+
// Relationship: prefer the first scalar subfield's display value.
|
|
46
|
+
const sub = field.subfields[0];
|
|
47
|
+
if (!sub) return null;
|
|
48
|
+
const subName = fieldName(sub);
|
|
49
|
+
return readPath(node, `${field.name}.${subName}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Substitutes `:fieldName` tokens in routePattern with node values. */
|
|
53
|
+
function resolveRoute(pattern: string, node: unknown, idField: string): string | null {
|
|
54
|
+
let failed = false;
|
|
55
|
+
const out = pattern.replace(ROUTE_TOKEN, (_, token) => {
|
|
56
|
+
const path = token === "id" ? idField : token;
|
|
57
|
+
const value = readPath(node, path);
|
|
58
|
+
if (value == null) {
|
|
59
|
+
failed = true;
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
return encodeURIComponent(value);
|
|
63
|
+
});
|
|
64
|
+
return failed ? null : out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const props = defineProps<{ node: unknown; source: SObjectSourceConfig }>();
|
|
68
|
+
|
|
69
|
+
const idField = computed(() => props.source.idField ?? "Id");
|
|
70
|
+
const title = computed(() => {
|
|
71
|
+
const titleField = props.source.displayFields[0];
|
|
72
|
+
return titleField ? readDisplayField(props.node, titleField) : null;
|
|
73
|
+
});
|
|
74
|
+
const subtitle = computed(() =>
|
|
75
|
+
props.source.displayFields
|
|
76
|
+
.slice(1)
|
|
77
|
+
.map((f) => readDisplayField(props.node, f))
|
|
78
|
+
.filter((s): s is string => s != null && s !== "")
|
|
79
|
+
.join(" · "),
|
|
80
|
+
);
|
|
81
|
+
const href = computed(() =>
|
|
82
|
+
props.source.routePattern
|
|
83
|
+
? resolveRoute(props.source.routePattern, props.node, idField.value)
|
|
84
|
+
: null,
|
|
85
|
+
);
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<RouterLink
|
|
90
|
+
v-if="href"
|
|
91
|
+
:to="href"
|
|
92
|
+
class="block hover:bg-accent rounded-md px-3 -mx-3 py-1 transition-colors"
|
|
93
|
+
:aria-label="`View: ${title ?? 'result'}`"
|
|
94
|
+
>
|
|
95
|
+
<span class="font-medium">{{ title ?? "—" }}</span>
|
|
96
|
+
<p v-if="subtitle" class="text-sm text-neutral-600 dark:text-neutral-300">{{ subtitle }}</p>
|
|
97
|
+
</RouterLink>
|
|
98
|
+
<div v-else class="px-3 -mx-3 py-1">
|
|
99
|
+
<span class="font-medium">{{ title ?? "—" }}</span>
|
|
100
|
+
<p v-if="subtitle" class="text-sm text-neutral-600 dark:text-neutral-300">{{ subtitle }}</p>
|
|
101
|
+
</div>
|
|
102
|
+
</template>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FunctionalComponent, VNodeChild } from "vue";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tiny functional component that invokes a render function and returns its
|
|
5
|
+
* output. Lets templates mount the `(node) => VNodeChild` renderers that
|
|
6
|
+
* `renderResult` / `renderFilters` / `renderHeader` accept.
|
|
7
|
+
*/
|
|
8
|
+
export const RenderVNode: FunctionalComponent<{ render: () => VNodeChild }> = (props) =>
|
|
9
|
+
props.render() as never;
|
|
10
|
+
RenderVNode.props = ["render"];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for choosing how one source's result node is rendered.
|
|
3
|
+
*
|
|
4
|
+
* Every result layout (`SearchResults`, `MergedSearchResults`, and any future
|
|
5
|
+
* grid/list) MUST resolve its per-node renderer through this helper so the
|
|
6
|
+
* kind-aware default can never be missed in one mode. The rule, in order:
|
|
7
|
+
*
|
|
8
|
+
* 1. A `renderResult[source.key]` config override wins in EVERY mode —
|
|
9
|
+
* including `false`, which hides the source entirely.
|
|
10
|
+
* 2. Otherwise `source.kind === "cms"` -> {@link CmsResultRow}.
|
|
11
|
+
* 3. Otherwise (SObject) -> {@link DefaultResultRow}.
|
|
12
|
+
*
|
|
13
|
+
* Returning the same union the callers already use (`renderer | false`) lets a
|
|
14
|
+
* component skip a hidden source without special-casing.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { h, type VNodeChild } from "vue";
|
|
18
|
+
import type { SourceConfig } from "../../types";
|
|
19
|
+
import DefaultResultRow from "./DefaultResultRow.vue";
|
|
20
|
+
import CmsResultRow from "./CmsResultRow.vue";
|
|
21
|
+
|
|
22
|
+
/** A per-node renderer (Vue render function), or `false` to hide the source's results. */
|
|
23
|
+
export type ResultRenderer = ((node: unknown) => VNodeChild) | false;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolves the renderer for one source. See the module doc for the precedence
|
|
27
|
+
* rules. `renderResult` is the optional per-source override map from `<Search>`.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveResultRenderer(
|
|
30
|
+
source: SourceConfig,
|
|
31
|
+
renderResult?: Record<string, ResultRenderer>,
|
|
32
|
+
): ResultRenderer {
|
|
33
|
+
const override = renderResult?.[source.key];
|
|
34
|
+
// A config override wins in every mode — including `false` (hide).
|
|
35
|
+
if (override !== undefined) return override;
|
|
36
|
+
|
|
37
|
+
if (source.kind === "cms") {
|
|
38
|
+
return (node: unknown) => h(CmsResultRow, { node, source });
|
|
39
|
+
}
|
|
40
|
+
return (node: unknown) => h(DefaultResultRow, { node, source });
|
|
41
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import type { Ref, WatchSource } from "vue";
|
|
7
|
+
import { ref, shallowRef, watch } from "vue";
|
|
8
|
+
|
|
9
|
+
/** Reactive async-data snapshot: current data, loading, and error. */
|
|
10
|
+
export interface AsyncData<T> {
|
|
11
|
+
/** Latest successful result. Kept visible during a refetch. */
|
|
12
|
+
data: Ref<T | null>;
|
|
13
|
+
/** True while a fetch for the current args is in flight. */
|
|
14
|
+
loading: Ref<boolean>;
|
|
15
|
+
/** Error message from the most recent failed fetch, else null. */
|
|
16
|
+
error: Ref<string | null>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Runs an async `fetcher` immediately and again whenever `source` changes,
|
|
21
|
+
* exposing the result as reactive refs. Stale responses (the source changed
|
|
22
|
+
* during a fetch, or the component unmounted) are dropped via a generation
|
|
23
|
+
* counter, so only the latest in-flight request can write state.
|
|
24
|
+
*
|
|
25
|
+
* Shipped with the feature (like the React feature's own hook) rather than
|
|
26
|
+
* re-exported from the base app, so the search library works in host apps that
|
|
27
|
+
* don't include base-vue-app's `composables/useAsyncData`.
|
|
28
|
+
*/
|
|
29
|
+
export function useAsyncData<TArgs, T>(
|
|
30
|
+
source: WatchSource<TArgs>,
|
|
31
|
+
fetcher: (args: TArgs) => Promise<T>,
|
|
32
|
+
): AsyncData<T> {
|
|
33
|
+
const data = shallowRef<T | null>(null);
|
|
34
|
+
const loading = ref<boolean>(true);
|
|
35
|
+
const error = ref<string | null>(null);
|
|
36
|
+
|
|
37
|
+
let generation = 0;
|
|
38
|
+
|
|
39
|
+
const run = (args: TArgs): void => {
|
|
40
|
+
const current = ++generation;
|
|
41
|
+
loading.value = true;
|
|
42
|
+
error.value = null;
|
|
43
|
+
|
|
44
|
+
fetcher(args)
|
|
45
|
+
.then((result) => {
|
|
46
|
+
if (current === generation) {
|
|
47
|
+
data.value = result;
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
.catch((err: unknown) => {
|
|
51
|
+
console.error(err);
|
|
52
|
+
if (current === generation) {
|
|
53
|
+
error.value = err instanceof Error ? err.message : "An error occurred";
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
.finally(() => {
|
|
57
|
+
if (current === generation) {
|
|
58
|
+
loading.value = false;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
watch(source, (args) => run(args), { immediate: true, deep: true });
|
|
64
|
+
|
|
65
|
+
return { data, loading, error };
|
|
66
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caches and serves distinct picklist values fetched via the GraphQL
|
|
3
|
+
* aggregate API. The cache key is `${objectName}.${fieldName}`; entries
|
|
4
|
+
* persist for the lifetime of the page so repeated mounts of the same
|
|
5
|
+
* picklist filter share the same response.
|
|
6
|
+
*
|
|
7
|
+
* Pass `null` to short-circuit the fetch (e.g. when inline options are
|
|
8
|
+
* available on the filter config). The query may be a ref/getter; the fetch
|
|
9
|
+
* re-runs when the resolved cache key changes.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { ref, toValue, watch, type MaybeRefOrGetter, type Ref } from "vue";
|
|
13
|
+
import { fetchDistinctValues, type PicklistOption } from "../api/distinctValuesService";
|
|
14
|
+
|
|
15
|
+
const cache = new Map<string, PicklistOption[]>();
|
|
16
|
+
const inflight = new Map<string, Promise<PicklistOption[]>>();
|
|
17
|
+
|
|
18
|
+
export interface DistinctValuesQuery {
|
|
19
|
+
objectName: string;
|
|
20
|
+
fieldName: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useDistinctValues(
|
|
24
|
+
query: MaybeRefOrGetter<DistinctValuesQuery | null>,
|
|
25
|
+
): Ref<PicklistOption[] | null> {
|
|
26
|
+
const keyOf = (q: DistinctValuesQuery | null) => (q ? `${q.objectName}.${q.fieldName}` : null);
|
|
27
|
+
const options = ref<PicklistOption[] | null>(null) as Ref<PicklistOption[] | null>;
|
|
28
|
+
const initialKey = keyOf(toValue(query));
|
|
29
|
+
if (initialKey) options.value = cache.get(initialKey) ?? null;
|
|
30
|
+
|
|
31
|
+
watch(
|
|
32
|
+
() => keyOf(toValue(query)),
|
|
33
|
+
(cacheKey, _prev, onCleanup) => {
|
|
34
|
+
const q = toValue(query);
|
|
35
|
+
if (!q || !cacheKey) return;
|
|
36
|
+
const cached = cache.get(cacheKey);
|
|
37
|
+
if (cached) {
|
|
38
|
+
options.value = cached;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
let cancelled = false;
|
|
42
|
+
onCleanup(() => {
|
|
43
|
+
cancelled = true;
|
|
44
|
+
});
|
|
45
|
+
const existing = inflight.get(cacheKey);
|
|
46
|
+
const promise =
|
|
47
|
+
existing ??
|
|
48
|
+
fetchDistinctValues(q.objectName, q.fieldName).then((result) => {
|
|
49
|
+
cache.set(cacheKey, result);
|
|
50
|
+
inflight.delete(cacheKey);
|
|
51
|
+
return result;
|
|
52
|
+
});
|
|
53
|
+
if (!existing) inflight.set(cacheKey, promise);
|
|
54
|
+
promise
|
|
55
|
+
.then((result) => {
|
|
56
|
+
if (!cancelled) options.value = result;
|
|
57
|
+
})
|
|
58
|
+
.catch((err: unknown) => {
|
|
59
|
+
inflight.delete(cacheKey);
|
|
60
|
+
console.error(`Failed to fetch distinct values for ${cacheKey}:`, err);
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
{ immediate: true },
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return options;
|
|
67
|
+
}
|