@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
package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filter primitives for search.
|
|
3
|
+
*
|
|
4
|
+
* Two responsibilities:
|
|
5
|
+
* 1. Build a GraphQL `where` clause from a list of active filter values.
|
|
6
|
+
* 2. Serialize/deserialize active filters to/from URLSearchParams under a
|
|
7
|
+
* per-source namespace prefix (so multiple sources can coexist on one URL).
|
|
8
|
+
*
|
|
9
|
+
* The URL helpers are namespaced: each source declares its key, and filter
|
|
10
|
+
* params are written as `s.<key>.f.<field>=...`. Sort uses
|
|
11
|
+
* `s.<key>.sort=<field>&s.<key>.dir=ASC|DESC`. Page size and page index use
|
|
12
|
+
* `s.<key>.ps=<size>` and `s.<key>.page=<n>`.
|
|
13
|
+
*
|
|
14
|
+
* The global search term `q=...` is **not** scoped to a source — it's broadcast
|
|
15
|
+
* to every source's `searchableFields` by the query builder.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { SortState } from "./sortUtils";
|
|
19
|
+
|
|
20
|
+
export type FilterFieldType =
|
|
21
|
+
| "text"
|
|
22
|
+
| "picklist"
|
|
23
|
+
| "numeric"
|
|
24
|
+
| "boolean"
|
|
25
|
+
| "date"
|
|
26
|
+
| "daterange"
|
|
27
|
+
| "datetime"
|
|
28
|
+
| "datetimerange"
|
|
29
|
+
| "multipicklist";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Which comparison UI a date/datetime filter exposes:
|
|
33
|
+
* - `"range"`: a single Between control (two date inputs, lower–upper bound).
|
|
34
|
+
* - `"comparison"`: an After / Before selector with one date input.
|
|
35
|
+
* - `"both"`: a Between / After / Before selector that switches between the two.
|
|
36
|
+
*
|
|
37
|
+
* Applies only to the date-family `type`s (`date`, `daterange`, `datetime`,
|
|
38
|
+
* `datetimerange`); ignored for other field types. Defaults to `"comparison"`.
|
|
39
|
+
*/
|
|
40
|
+
export type DateFilterMode = "range" | "comparison" | "both";
|
|
41
|
+
|
|
42
|
+
export type FilterFieldConfig<TFieldName extends string = string> = {
|
|
43
|
+
field: TFieldName;
|
|
44
|
+
label: string;
|
|
45
|
+
type: FilterFieldType;
|
|
46
|
+
placeholder?: string;
|
|
47
|
+
/** Required for picklist / multipicklist. */
|
|
48
|
+
options?: Array<{ value: string; label: string }>;
|
|
49
|
+
/** Date-family only — see {@link DateFilterMode}. Defaults to `"comparison"`. */
|
|
50
|
+
dateMode?: DateFilterMode;
|
|
51
|
+
helpText?: string;
|
|
52
|
+
// Numeric-family only.
|
|
53
|
+
min?: number;
|
|
54
|
+
max?: number;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type ActiveFilterValue<TFieldName extends string = string> = {
|
|
58
|
+
field: TFieldName;
|
|
59
|
+
label: string;
|
|
60
|
+
type: FilterFieldType;
|
|
61
|
+
value?: string;
|
|
62
|
+
min?: string;
|
|
63
|
+
max?: string;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// URL serialization (per-source namespace)
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
const SOURCE_PREFIX = "s.";
|
|
71
|
+
const FILTER_INFIX = ".f.";
|
|
72
|
+
const SORT_SUFFIX = ".sort";
|
|
73
|
+
const DIR_SUFFIX = ".dir";
|
|
74
|
+
const PAGE_SIZE_SUFFIX = ".ps";
|
|
75
|
+
const PAGE_SUFFIX = ".page";
|
|
76
|
+
|
|
77
|
+
/** Global search term key (not source-scoped). */
|
|
78
|
+
export const GLOBAL_QUERY_KEY = "q";
|
|
79
|
+
|
|
80
|
+
function filterKey(sourceKey: string, field: string, suffix?: string) {
|
|
81
|
+
return `${SOURCE_PREFIX}${sourceKey}${FILTER_INFIX}${field}${suffix ? `.${suffix}` : ""}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The source's own default sort / page size. When a source's current state
|
|
86
|
+
* matches these, the corresponding URL params are omitted so the query string
|
|
87
|
+
* only ever reflects an *explicit* user choice — resetting filters or returning
|
|
88
|
+
* a source to its defaults yields a clean URL instead of one littered with the
|
|
89
|
+
* defaults. The read path falls back to these same defaults when the params are
|
|
90
|
+
* absent, so the round-trip is lossless.
|
|
91
|
+
*/
|
|
92
|
+
export interface SourceParamDefaults {
|
|
93
|
+
sort?: SortState | null;
|
|
94
|
+
pageSize?: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function sortMatchesDefault(sort: SortState, defaultSort: SortState | null | undefined): boolean {
|
|
98
|
+
return (
|
|
99
|
+
!!defaultSort && sort.field === defaultSort.field && sort.direction === defaultSort.direction
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function writeSourceParams(
|
|
104
|
+
params: URLSearchParams,
|
|
105
|
+
sourceKey: string,
|
|
106
|
+
filters: ActiveFilterValue[],
|
|
107
|
+
sort: SortState | null,
|
|
108
|
+
pageSize?: number,
|
|
109
|
+
pageIndex?: number,
|
|
110
|
+
defaults?: SourceParamDefaults,
|
|
111
|
+
): void {
|
|
112
|
+
for (const filter of filters) {
|
|
113
|
+
if (filter.value !== undefined && filter.value !== "") {
|
|
114
|
+
params.set(filterKey(sourceKey, filter.field), filter.value);
|
|
115
|
+
}
|
|
116
|
+
if (filter.min !== undefined && filter.min !== "") {
|
|
117
|
+
params.set(filterKey(sourceKey, filter.field, "min"), filter.min);
|
|
118
|
+
}
|
|
119
|
+
if (filter.max !== undefined && filter.max !== "") {
|
|
120
|
+
params.set(filterKey(sourceKey, filter.field, "max"), filter.max);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Only serialize sort when it differs from the source default — otherwise
|
|
124
|
+
// the URL would carry the default the user never chose.
|
|
125
|
+
if (sort && !sortMatchesDefault(sort, defaults?.sort)) {
|
|
126
|
+
params.set(`${SOURCE_PREFIX}${sourceKey}${SORT_SUFFIX}`, sort.field);
|
|
127
|
+
params.set(`${SOURCE_PREFIX}${sourceKey}${DIR_SUFFIX}`, sort.direction);
|
|
128
|
+
}
|
|
129
|
+
if (pageSize !== undefined && pageSize !== defaults?.pageSize) {
|
|
130
|
+
params.set(`${SOURCE_PREFIX}${sourceKey}${PAGE_SIZE_SUFFIX}`, String(pageSize));
|
|
131
|
+
}
|
|
132
|
+
if (pageIndex !== undefined && pageIndex > 0) {
|
|
133
|
+
params.set(`${SOURCE_PREFIX}${sourceKey}${PAGE_SUFFIX}`, String(pageIndex + 1));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface ReadSourceParamsResult {
|
|
138
|
+
filters: ActiveFilterValue[];
|
|
139
|
+
sort: SortState | null;
|
|
140
|
+
pageSize: number | undefined;
|
|
141
|
+
pageIndex: number;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function readSourceParams(
|
|
145
|
+
params: URLSearchParams,
|
|
146
|
+
sourceKey: string,
|
|
147
|
+
configs: FilterFieldConfig[],
|
|
148
|
+
): ReadSourceParamsResult {
|
|
149
|
+
const filters: ActiveFilterValue[] = [];
|
|
150
|
+
for (const config of configs) {
|
|
151
|
+
const { field, label, type } = config;
|
|
152
|
+
const value = params.get(filterKey(sourceKey, field)) ?? undefined;
|
|
153
|
+
const min = params.get(filterKey(sourceKey, field, "min")) ?? undefined;
|
|
154
|
+
const max = params.get(filterKey(sourceKey, field, "max")) ?? undefined;
|
|
155
|
+
|
|
156
|
+
const hasValue = value !== undefined && value !== "";
|
|
157
|
+
const hasRange = (min !== undefined && min !== "") || (max !== undefined && max !== "");
|
|
158
|
+
if (hasValue || hasRange) {
|
|
159
|
+
filters.push({ field, label, type, value, min, max });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let sort: SortState | null = null;
|
|
164
|
+
const sortField = params.get(`${SOURCE_PREFIX}${sourceKey}${SORT_SUFFIX}`);
|
|
165
|
+
const sortDir = params.get(`${SOURCE_PREFIX}${sourceKey}${DIR_SUFFIX}`);
|
|
166
|
+
if (sortField) {
|
|
167
|
+
sort = { field: sortField, direction: sortDir === "DESC" ? "DESC" : "ASC" };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const pageSizeRaw = params.get(`${SOURCE_PREFIX}${sourceKey}${PAGE_SIZE_SUFFIX}`);
|
|
171
|
+
const pageSize = pageSizeRaw ? parseInt(pageSizeRaw, 10) : undefined;
|
|
172
|
+
const pageRaw = params.get(`${SOURCE_PREFIX}${sourceKey}${PAGE_SUFFIX}`);
|
|
173
|
+
const page = pageRaw ? parseInt(pageRaw, 10) : 1;
|
|
174
|
+
const pageIndex = !isNaN(page) && page > 1 ? page - 1 : 0;
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
filters,
|
|
178
|
+
sort,
|
|
179
|
+
pageSize: pageSize && !isNaN(pageSize) ? pageSize : undefined,
|
|
180
|
+
pageIndex,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
// GraphQL filter building
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Combines active filter values into a GraphQL `where` clause for one source.
|
|
190
|
+
* Returns `undefined` when no constraints are active.
|
|
191
|
+
*/
|
|
192
|
+
export function buildFilter(filters: ActiveFilterValue[], configs: FilterFieldConfig[]): unknown {
|
|
193
|
+
const configMap = new Map(configs.map((c) => [c.field, c]));
|
|
194
|
+
const clauses: unknown[] = [];
|
|
195
|
+
for (const filter of filters) {
|
|
196
|
+
const clause = buildSingleFilter(filter, configMap.get(filter.field));
|
|
197
|
+
if (clause) clauses.push(clause);
|
|
198
|
+
}
|
|
199
|
+
if (clauses.length === 0) return undefined;
|
|
200
|
+
if (clauses.length === 1) return clauses[0];
|
|
201
|
+
return { and: clauses };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function toStartOfDay(d: string) {
|
|
205
|
+
return `${d}T00:00:00.000Z`;
|
|
206
|
+
}
|
|
207
|
+
function toEndOfDay(d: string) {
|
|
208
|
+
return `${d}T23:59:59.999Z`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Escapes LIKE metacharacters (`\`, `%`, `_`) so user input is matched literally. */
|
|
212
|
+
function escapeLikePattern(value: string): string {
|
|
213
|
+
return value.replace(/[\\%_]/g, "\\$&");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function buildSingleFilter(filter: ActiveFilterValue, config?: FilterFieldConfig): unknown {
|
|
217
|
+
const { field, type, value, min, max } = filter;
|
|
218
|
+
switch (type) {
|
|
219
|
+
case "text": {
|
|
220
|
+
if (!value) return null;
|
|
221
|
+
return { [field]: { like: `%${escapeLikePattern(value)}%` } };
|
|
222
|
+
}
|
|
223
|
+
case "picklist": {
|
|
224
|
+
if (!value) return null;
|
|
225
|
+
return { [field]: { eq: value } };
|
|
226
|
+
}
|
|
227
|
+
case "numeric": {
|
|
228
|
+
if (!min && !max) return null;
|
|
229
|
+
const ops: Record<string, number> = {};
|
|
230
|
+
// clamp both endpoints into the declared [min,max] bounds
|
|
231
|
+
const clamp = (n: number) => {
|
|
232
|
+
if (config?.min != null) n = Math.max(n, config.min);
|
|
233
|
+
if (config?.max != null) n = Math.min(n, config.max);
|
|
234
|
+
return n;
|
|
235
|
+
};
|
|
236
|
+
// Drop any endpoint that isn't finite — NaN/Infinity serialize to null on the wire.
|
|
237
|
+
const lo = Number(min);
|
|
238
|
+
if (min && Number.isFinite(lo)) ops.gte = clamp(lo);
|
|
239
|
+
const hi = Number(max);
|
|
240
|
+
if (max && Number.isFinite(hi)) ops.lte = clamp(hi);
|
|
241
|
+
// If both endpoints were dropped as invalid, emit no clause at all.
|
|
242
|
+
if (Object.keys(ops).length === 0) return null;
|
|
243
|
+
return { [field]: ops };
|
|
244
|
+
}
|
|
245
|
+
case "boolean": {
|
|
246
|
+
if (value === undefined || value === "") return null;
|
|
247
|
+
return { [field]: { eq: value === "true" } };
|
|
248
|
+
}
|
|
249
|
+
case "multipicklist": {
|
|
250
|
+
if (!value) return null;
|
|
251
|
+
const values = value.split(",").filter(Boolean);
|
|
252
|
+
if (values.length === 0) return null;
|
|
253
|
+
if (values.length === 1) return { [field]: { eq: values[0] } };
|
|
254
|
+
return { [field]: { in: values } };
|
|
255
|
+
}
|
|
256
|
+
case "date": {
|
|
257
|
+
if (!min && !max) return null;
|
|
258
|
+
const op = min ? "gte" : "lte";
|
|
259
|
+
const dateStr = min ?? max;
|
|
260
|
+
return { [field]: { [op]: { value: dateStr } } };
|
|
261
|
+
}
|
|
262
|
+
case "daterange": {
|
|
263
|
+
if (!min && !max) return null;
|
|
264
|
+
const clauses: unknown[] = [];
|
|
265
|
+
if (min) clauses.push({ [field]: { gte: { value: min } } });
|
|
266
|
+
if (max) clauses.push({ [field]: { lte: { value: max } } });
|
|
267
|
+
return clauses.length === 1 ? clauses[0] : { and: clauses };
|
|
268
|
+
}
|
|
269
|
+
case "datetime": {
|
|
270
|
+
if (!min && !max) return null;
|
|
271
|
+
const op = min ? "gte" : "lte";
|
|
272
|
+
const dateStr = min ?? max;
|
|
273
|
+
const isoStr = op === "gte" ? toStartOfDay(dateStr!) : toEndOfDay(dateStr!);
|
|
274
|
+
return { [field]: { [op]: { value: isoStr } } };
|
|
275
|
+
}
|
|
276
|
+
case "datetimerange": {
|
|
277
|
+
if (!min && !max) return null;
|
|
278
|
+
const clauses: unknown[] = [];
|
|
279
|
+
if (min) clauses.push({ [field]: { gte: { value: toStartOfDay(min) } } });
|
|
280
|
+
if (max) clauses.push({ [field]: { lte: { value: toEndOfDay(max) } } });
|
|
281
|
+
return clauses.length === 1 ? clauses[0] : { and: clauses };
|
|
282
|
+
}
|
|
283
|
+
default:
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Builds a global-q `or` clause across the source's searchable fields.
|
|
290
|
+
* Returns `undefined` when q is empty or the source has no searchable fields.
|
|
291
|
+
*/
|
|
292
|
+
export function buildGlobalQueryClause(q: string, searchableFields: string[]): unknown {
|
|
293
|
+
const trimmed = q.trim();
|
|
294
|
+
if (!trimmed || searchableFields.length === 0) return undefined;
|
|
295
|
+
const clauses = searchableFields.map((path) => {
|
|
296
|
+
const parts = path.split(".");
|
|
297
|
+
let clause: Record<string, unknown> = { like: `%${escapeLikePattern(trimmed)}%` };
|
|
298
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
299
|
+
clause = { [parts[i]]: clause };
|
|
300
|
+
}
|
|
301
|
+
return clause;
|
|
302
|
+
});
|
|
303
|
+
return clauses.length === 1 ? clauses[0] : { or: clauses };
|
|
304
|
+
}
|
package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/sortUtils.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sort state and order-by builder for search.
|
|
3
|
+
*
|
|
4
|
+
* The order-by output shape matches the Salesforce uiapi GraphQL convention:
|
|
5
|
+
* { [field]: { order: ASC|DESC, nulls: LAST } }
|
|
6
|
+
*
|
|
7
|
+
* The constants are stringly-typed here (rather than imported from codegen)
|
|
8
|
+
* because search builds its query at runtime and isn't tied to a
|
|
9
|
+
* specific generated schema.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type SortFieldConfig<TFieldName extends string = string> = {
|
|
13
|
+
field: TFieldName;
|
|
14
|
+
label: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type SortState<TFieldName extends string = string> = {
|
|
18
|
+
field: TFieldName;
|
|
19
|
+
direction: "ASC" | "DESC";
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const ORDER_ASC = "ASC";
|
|
23
|
+
const ORDER_DESC = "DESC";
|
|
24
|
+
const NULLS_LAST = "LAST";
|
|
25
|
+
|
|
26
|
+
export function buildOrderBy(sort: SortState | null): unknown {
|
|
27
|
+
if (!sort) return undefined;
|
|
28
|
+
return {
|
|
29
|
+
[sort.field]: {
|
|
30
|
+
order: sort.direction === "ASC" ? ORDER_ASC : ORDER_DESC,
|
|
31
|
+
nulls: NULLS_LAST,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
query GetAccountDetail($id: ID!) {
|
|
2
|
+
uiapi {
|
|
3
|
+
query {
|
|
4
|
+
Account(where: { Id: { eq: $id } }) {
|
|
5
|
+
edges {
|
|
6
|
+
node {
|
|
7
|
+
Id
|
|
8
|
+
Name @optional {
|
|
9
|
+
value
|
|
10
|
+
displayValue
|
|
11
|
+
}
|
|
12
|
+
Owner @optional {
|
|
13
|
+
Name @optional {
|
|
14
|
+
value
|
|
15
|
+
displayValue
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
Phone @optional {
|
|
19
|
+
value
|
|
20
|
+
displayValue
|
|
21
|
+
}
|
|
22
|
+
Fax @optional {
|
|
23
|
+
value
|
|
24
|
+
displayValue
|
|
25
|
+
}
|
|
26
|
+
Parent @optional {
|
|
27
|
+
Name @optional {
|
|
28
|
+
value
|
|
29
|
+
displayValue
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
Website @optional {
|
|
33
|
+
value
|
|
34
|
+
displayValue
|
|
35
|
+
}
|
|
36
|
+
Type @optional {
|
|
37
|
+
value
|
|
38
|
+
displayValue
|
|
39
|
+
}
|
|
40
|
+
NumberOfEmployees @optional {
|
|
41
|
+
value
|
|
42
|
+
displayValue
|
|
43
|
+
}
|
|
44
|
+
Industry @optional {
|
|
45
|
+
value
|
|
46
|
+
displayValue
|
|
47
|
+
}
|
|
48
|
+
AnnualRevenue @optional {
|
|
49
|
+
value
|
|
50
|
+
displayValue
|
|
51
|
+
}
|
|
52
|
+
Description @optional {
|
|
53
|
+
value
|
|
54
|
+
displayValue
|
|
55
|
+
}
|
|
56
|
+
BillingStreet @optional {
|
|
57
|
+
value
|
|
58
|
+
displayValue
|
|
59
|
+
}
|
|
60
|
+
BillingCity @optional {
|
|
61
|
+
value
|
|
62
|
+
displayValue
|
|
63
|
+
}
|
|
64
|
+
BillingState @optional {
|
|
65
|
+
value
|
|
66
|
+
displayValue
|
|
67
|
+
}
|
|
68
|
+
BillingPostalCode @optional {
|
|
69
|
+
value
|
|
70
|
+
displayValue
|
|
71
|
+
}
|
|
72
|
+
BillingCountry @optional {
|
|
73
|
+
value
|
|
74
|
+
displayValue
|
|
75
|
+
}
|
|
76
|
+
ShippingStreet @optional {
|
|
77
|
+
value
|
|
78
|
+
displayValue
|
|
79
|
+
}
|
|
80
|
+
ShippingCity @optional {
|
|
81
|
+
value
|
|
82
|
+
displayValue
|
|
83
|
+
}
|
|
84
|
+
ShippingState @optional {
|
|
85
|
+
value
|
|
86
|
+
displayValue
|
|
87
|
+
}
|
|
88
|
+
ShippingPostalCode @optional {
|
|
89
|
+
value
|
|
90
|
+
displayValue
|
|
91
|
+
}
|
|
92
|
+
ShippingCountry @optional {
|
|
93
|
+
value
|
|
94
|
+
displayValue
|
|
95
|
+
}
|
|
96
|
+
CreatedBy @optional {
|
|
97
|
+
Name @optional {
|
|
98
|
+
value
|
|
99
|
+
displayValue
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
CreatedDate @optional {
|
|
103
|
+
value
|
|
104
|
+
displayValue
|
|
105
|
+
}
|
|
106
|
+
LastModifiedBy @optional {
|
|
107
|
+
Name @optional {
|
|
108
|
+
value
|
|
109
|
+
displayValue
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
LastModifiedDate @optional {
|
|
113
|
+
value
|
|
114
|
+
displayValue
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin GraphQL client: createDataSDK + sdk.graphql with centralized error
|
|
3
|
+
* handling. Mutations are routed to sdk.graphql.mutate and everything else to
|
|
4
|
+
* sdk.graphql.query (the SDK rejects an operation sent to the wrong method).
|
|
5
|
+
* Use with gql-tagged queries and generated operation types for type-safe calls.
|
|
6
|
+
*/
|
|
7
|
+
import { createDataSDK } from '@salesforce/platform-sdk';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* True when the operation's first definition is a `mutation`. Strips GraphQL
|
|
11
|
+
* comments first so a leading `# ...` line can't mask the keyword. Queries
|
|
12
|
+
* (named or anonymous `{ ... }` shorthand) and subscriptions fall through to
|
|
13
|
+
* query().
|
|
14
|
+
*/
|
|
15
|
+
function isMutation(operation: string): boolean {
|
|
16
|
+
return /^\s*mutation\b/.test(operation.replace(/#[^\n\r]*/g, ''));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function executeGraphQL<TData, TVariables>(
|
|
20
|
+
operation: string,
|
|
21
|
+
variables?: TVariables
|
|
22
|
+
): Promise<TData> {
|
|
23
|
+
const data = await createDataSDK();
|
|
24
|
+
const result = isMutation(operation)
|
|
25
|
+
? await data.graphql!.mutate<TData, TVariables>({
|
|
26
|
+
mutation: operation,
|
|
27
|
+
variables: variables,
|
|
28
|
+
})
|
|
29
|
+
: await data.graphql!.query<TData, TVariables>({
|
|
30
|
+
query: operation,
|
|
31
|
+
variables: variables,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (result.errors?.length) {
|
|
35
|
+
const msg = result.errors.map(e => e.message).join('; ');
|
|
36
|
+
throw new Error(`GraphQL Error: ${msg}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (result.data == null) {
|
|
40
|
+
throw new Error('GraphQL response data is null');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return result.data;
|
|
44
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
All rights reserved.
|
|
4
|
+
For full license text, see the LICENSE.txt file
|
|
5
|
+
-->
|
|
6
|
+
<script setup lang="ts">
|
|
7
|
+
import { computed, nextTick, ref, useId, watch } from "vue";
|
|
8
|
+
import { RouterLink, RouterView, useRoute } from "vue-router";
|
|
9
|
+
import { getAllRoutes } from "./router-utils";
|
|
10
|
+
import AgentforceConversationClient from "./components/AgentforceConversationClient.vue";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* App-owned layout — overrides the base `appLayout.vue` at compose (the app
|
|
14
|
+
* package ships its own copy, so patches-cli's "app wins" rule selects this one
|
|
15
|
+
* over the base-vue-app version). The Vue analogue of the React internal app's
|
|
16
|
+
* `appLayout.tsx` and the Angular internal app's `app-layout`.
|
|
17
|
+
*
|
|
18
|
+
* Over the base layout it:
|
|
19
|
+
* - mounts the floating `<AgentforceConversationClient />` after the router
|
|
20
|
+
* outlet (internal apps carry no `AuthMenu` / auth feature);
|
|
21
|
+
* - adds the accessibility behaviour of the React layout: a skip-to-main
|
|
22
|
+
* link, a labelled primary nav, `aria-expanded` / `aria-controls` on the
|
|
23
|
+
* hamburger, `aria-current="page"` on the active link, h1 focus after a
|
|
24
|
+
* route change, menu-button refocus when the active link is re-clicked, and
|
|
25
|
+
* the "Search | MyApp" document title for `/search`.
|
|
26
|
+
*
|
|
27
|
+
* Nav items are derived from the merged router config at runtime (routes whose
|
|
28
|
+
* `meta.showInNavigation` is true), mirroring the base layout.
|
|
29
|
+
*
|
|
30
|
+
* Replace the placeholder `agentId` below with your deployed agent's 18-char id.
|
|
31
|
+
*/
|
|
32
|
+
const isOpen = ref(false);
|
|
33
|
+
const route = useRoute();
|
|
34
|
+
const navId = useId();
|
|
35
|
+
const outletWrapperRef = ref<HTMLElement | null>(null);
|
|
36
|
+
const menuButtonRef = ref<HTMLButtonElement | null>(null);
|
|
37
|
+
|
|
38
|
+
const SEARCH_PATH = /^\/search\/?$/i;
|
|
39
|
+
|
|
40
|
+
const focusMainContent = () => {
|
|
41
|
+
const content = outletWrapperRef.value;
|
|
42
|
+
if (content) {
|
|
43
|
+
if (!content.hasAttribute("tabindex")) content.setAttribute("tabindex", "-1");
|
|
44
|
+
content.focus();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Runs on initial entry (title for direct /search entry) and on every path
|
|
49
|
+
// change (focus the new page's h1 so keyboard / screen-reader users land on it).
|
|
50
|
+
watch(
|
|
51
|
+
() => route.path,
|
|
52
|
+
async (path, previous) => {
|
|
53
|
+
if (SEARCH_PATH.test(path)) document.title = "Search | MyApp";
|
|
54
|
+
if (previous !== undefined && previous !== path) {
|
|
55
|
+
// Let the new page render before looking for its h1.
|
|
56
|
+
await nextTick();
|
|
57
|
+
const target = outletWrapperRef.value?.querySelector<HTMLElement>("h1");
|
|
58
|
+
if (target) {
|
|
59
|
+
if (!target.hasAttribute("tabindex")) target.setAttribute("tabindex", "-1");
|
|
60
|
+
target.focus();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{ immediate: true, flush: "post" },
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const toggleMenu = () => {
|
|
68
|
+
isOpen.value = !isOpen.value;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const isActive = (path: string) => route.path === path;
|
|
72
|
+
|
|
73
|
+
const onNavClick = (path: string) => {
|
|
74
|
+
isOpen.value = false;
|
|
75
|
+
if (isActive(path)) menuButtonRef.value?.focus();
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const navigationRoutes = computed<{ path: string; label: string }[]>(() =>
|
|
79
|
+
getAllRoutes()
|
|
80
|
+
.filter(
|
|
81
|
+
(r) =>
|
|
82
|
+
r.meta?.showInNavigation === true &&
|
|
83
|
+
r.fullPath !== undefined &&
|
|
84
|
+
r.meta?.label !== undefined,
|
|
85
|
+
)
|
|
86
|
+
.map((r) => ({
|
|
87
|
+
path: r.fullPath,
|
|
88
|
+
label: r.meta?.label as string,
|
|
89
|
+
})),
|
|
90
|
+
);
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<template>
|
|
94
|
+
<a
|
|
95
|
+
href="#main-content"
|
|
96
|
+
class="sr-only focus:not-sr-only focus:absolute focus:z-50 focus:top-2 focus:left-2 focus:rounded-md focus:bg-white focus:px-4 focus:py-2 focus:text-gray-900 focus:shadow focus:ring-2 focus:ring-gray-900"
|
|
97
|
+
@click.prevent="focusMainContent"
|
|
98
|
+
>
|
|
99
|
+
Skip to main content
|
|
100
|
+
</a>
|
|
101
|
+
<nav aria-label="Primary" class="bg-white border-b border-gray-200">
|
|
102
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
103
|
+
<div class="flex justify-between items-center h-16">
|
|
104
|
+
<RouterLink to="/" class="text-xl font-semibold text-gray-900"> Vue App </RouterLink>
|
|
105
|
+
<button
|
|
106
|
+
ref="menuButtonRef"
|
|
107
|
+
type="button"
|
|
108
|
+
class="p-2 rounded-md text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
109
|
+
aria-label="Toggle menu"
|
|
110
|
+
:aria-expanded="isOpen"
|
|
111
|
+
:aria-controls="navId"
|
|
112
|
+
@click="toggleMenu"
|
|
113
|
+
>
|
|
114
|
+
<div aria-hidden="true" class="w-6 h-6 flex flex-col justify-center space-y-1.5">
|
|
115
|
+
<span
|
|
116
|
+
class="block h-0.5 w-6 bg-current transition-all"
|
|
117
|
+
:class="{ 'rotate-45 translate-y-2': isOpen }"
|
|
118
|
+
/>
|
|
119
|
+
<span
|
|
120
|
+
class="block h-0.5 w-6 bg-current transition-all"
|
|
121
|
+
:class="{ 'opacity-0': isOpen }"
|
|
122
|
+
/>
|
|
123
|
+
<span
|
|
124
|
+
class="block h-0.5 w-6 bg-current transition-all"
|
|
125
|
+
:class="{ '-rotate-45 -translate-y-2': isOpen }"
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
<div :id="navId" class="pb-4" :hidden="!isOpen">
|
|
131
|
+
<div v-if="isOpen" class="flex flex-col space-y-2">
|
|
132
|
+
<RouterLink
|
|
133
|
+
v-for="item in navigationRoutes"
|
|
134
|
+
:key="item.path"
|
|
135
|
+
:to="item.path"
|
|
136
|
+
class="px-3 py-2 rounded-md text-sm font-medium transition-colors"
|
|
137
|
+
:class="
|
|
138
|
+
isActive(item.path) ? 'bg-blue-100 text-blue-700' : 'text-gray-700 hover:bg-gray-100'
|
|
139
|
+
"
|
|
140
|
+
:aria-current="isActive(item.path) ? 'page' : undefined"
|
|
141
|
+
@click="onNavClick(item.path)"
|
|
142
|
+
>
|
|
143
|
+
{{ item.label }}
|
|
144
|
+
</RouterLink>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</nav>
|
|
149
|
+
<div id="main-content" ref="outletWrapperRef">
|
|
150
|
+
<RouterView />
|
|
151
|
+
</div>
|
|
152
|
+
<AgentforceConversationClient agent-id="<USER_AGENT_ID_18_CHAR_0Xx...>" />
|
|
153
|
+
</template>
|