@salesforce/ui-bundle-template-feature-react-search 11.3.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/LICENSE.txt +82 -0
- package/README.md +692 -0
- package/dist/.forceignore +15 -0
- package/dist/.husky/pre-commit +4 -0
- package/dist/.prettierignore +11 -0
- package/dist/.prettierrc +17 -0
- package/dist/CHANGELOG.md +3499 -0
- package/dist/README.md +28 -0
- package/dist/config/project-scratch-def.json +13 -0
- package/dist/eslint.config.js +7 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/.forceignore +15 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/.graphqlrc.yml +2 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/.prettierignore +9 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/.prettierrc +11 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/CHANGELOG.md +10 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/README.md +75 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/codegen.yml +95 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/components.json +18 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/e2e/app.spec.ts +17 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/eslint.config.js +169 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/feature-react-search.uibundle-meta.xml +7 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/index.html +12 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/package.json +76 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/playwright.config.ts +24 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/scripts/get-graphql-schema.mjs +71 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/scripts/rewrite-e2e-assets.mjs +23 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/api/graphqlClient.ts +44 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/app.tsx +17 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/appLayout.tsx +83 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/assets/icons/book.svg +3 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/assets/icons/copy.svg +4 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/assets/icons/rocket.svg +3 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/assets/icons/star.svg +3 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/assets/images/codey-1.png +0 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/assets/images/codey-2.png +0 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/assets/images/codey-3.png +0 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/assets/images/vibe-codey.svg +194 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/alerts/status-alert.tsx +52 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/layouts/card-layout.tsx +29 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/alert.tsx +76 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/avatar.tsx +109 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/badge.tsx +48 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/breadcrumb.tsx +109 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/button.tsx +67 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/calendar.tsx +232 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/card.tsx +103 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/checkbox.tsx +32 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/collapsible.tsx +33 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/datePicker.tsx +127 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/dialog.tsx +162 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/dropdown-menu.tsx +257 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/field.tsx +237 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/index.ts +109 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/input.tsx +19 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/label.tsx +22 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/pagination.tsx +132 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/popover.tsx +89 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/select.tsx +193 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/separator.tsx +26 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/skeleton.tsx +14 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/sonner.tsx +20 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/spinner.tsx +16 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/table.tsx +114 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/components/ui/tabs.tsx +88 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/api/distinctValuesService.ts +84 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/api/searchService.ts +71 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/Search.tsx +160 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/SearchResults.tsx +77 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/SourceSection.tsx +167 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/controls/PaginationControls.tsx +115 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/controls/ScopeSelector.tsx +55 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/controls/SearchBar.tsx +55 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/controls/SortControl.tsx +62 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/ActiveFilters.tsx +61 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/DefaultFilterPanel.tsx +122 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/FilterContext.tsx +70 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/BooleanFilter.tsx +50 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/DateRangeFilter.tsx +50 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/FilterFieldWrapper.tsx +26 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/MultiSelectFilter.tsx +47 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/NumericRangeFilter.tsx +78 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/SelectFilter.tsx +46 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/TextFilter.tsx +52 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/results/DefaultResultRow.tsx +109 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/config.json +82 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/hooks/useAsyncData.ts +56 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/hooks/useDistinctValues.ts +59 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/hooks/useSearch.ts +442 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/index.ts +80 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/loadConfig.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/queryBuilder.ts +156 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/types.ts +169 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/utils/debounce.ts +17 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/utils/fieldUtils.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/utils/filterUtils.ts +272 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/features/search/utils/sortUtils.ts +34 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/hooks/useAsyncData.ts +67 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/lib/utils.ts +6 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/navigationMenu.tsx +80 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/pages/Home.tsx +11 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/pages/NotFound.tsx +18 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/router-utils.tsx +35 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/routes.tsx +22 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/src/styles/global.css +135 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/tsconfig.json +45 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/tsconfig.node.json +13 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/ui-bundle.json +7 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/vite-env.d.ts +4 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/vite.config.ts +106 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/vitest-env.d.ts +2 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/vitest.config.ts +11 -0
- package/dist/force-app/main/default/uiBundles/feature-react-search/vitest.setup.ts +1 -0
- package/dist/jest.config.js +6 -0
- package/dist/package-lock.json +9995 -0
- package/dist/package.json +44 -0
- package/dist/scripts/apex/hello.apex +10 -0
- package/dist/scripts/gitignore-templates.json +4 -0
- package/dist/scripts/graphql-search.sh +191 -0
- package/dist/scripts/org-setup-config-schema.mjs +96 -0
- package/dist/scripts/org-setup.config.json +5 -0
- package/dist/scripts/org-setup.mjs +1392 -0
- package/dist/scripts/sf-project-setup.mjs +103 -0
- package/dist/scripts/soql/account.soql +6 -0
- package/dist/scripts/validate-org-setup-config.mjs +38 -0
- package/dist/sfdx-project.json +12 -0
- package/package.json +51 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/__inherit__appLayout.tsx +9 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/components/ui/__inherit__alert.tsx +39 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/components/ui/__inherit__button.tsx +45 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/components/ui/__inherit__checkbox.tsx +8 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/components/ui/__inherit__input.tsx +5 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/components/ui/__inherit__label.tsx +8 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/components/ui/__inherit__pagination.tsx +47 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/components/ui/__inherit__select.tsx +57 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/components/ui/__inherit__skeleton.tsx +5 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/api/distinctValuesService.ts +84 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/api/searchService.ts +71 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/Search.tsx +160 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/SearchResults.tsx +77 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/SourceSection.tsx +167 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/controls/PaginationControls.tsx +115 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/controls/ScopeSelector.tsx +55 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/controls/SearchBar.tsx +55 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/controls/SortControl.tsx +62 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/ActiveFilters.tsx +61 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/DefaultFilterPanel.tsx +122 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/FilterContext.tsx +70 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/BooleanFilter.tsx +50 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/DateRangeFilter.tsx +50 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/FilterFieldWrapper.tsx +26 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/MultiSelectFilter.tsx +47 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/NumericRangeFilter.tsx +78 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/SelectFilter.tsx +46 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/filters/inputs/TextFilter.tsx +52 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/components/results/DefaultResultRow.tsx +109 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/config.json +82 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/hooks/useAsyncData.ts +56 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/hooks/useDistinctValues.ts +59 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/hooks/useSearch.ts +442 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/index.ts +80 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/loadConfig.ts +14 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/queryBuilder.ts +156 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/types.ts +169 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/utils/debounce.ts +17 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/utils/fieldUtils.ts +14 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/utils/filterUtils.ts +272 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/features/search/utils/sortUtils.ts +34 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/pages/Home.tsx +11 -0
- package/src/force-app/main/default/uiBundles/feature-react-search/src/routes.tsx +10 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Run from SFDX project root: install dependencies, build, and launch the dev server
|
|
4
|
+
* for the UI bundle in force-app/main/default/uiBundles/.
|
|
5
|
+
*
|
|
6
|
+
* Usage: npm run sf-project-setup
|
|
7
|
+
* (from the directory that contains force-app/ and sfdx-project.json)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawnSync } from 'node:child_process';
|
|
11
|
+
import { resolve, dirname } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { readdirSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const ROOT = resolve(__dirname, '..');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* npm strips .gitignore from published packages — generate them on first run.
|
|
20
|
+
* Templates are stored in scripts/gitignore-templates.json (generated at build
|
|
21
|
+
* time from the actual .gitignore files). The JSON may not exist in git-cloned
|
|
22
|
+
* distributions where .gitignore is already present, so loading is best-effort.
|
|
23
|
+
*/
|
|
24
|
+
function loadGitignoreTemplates() {
|
|
25
|
+
const templatesPath = resolve(__dirname, 'gitignore-templates.json');
|
|
26
|
+
if (!existsSync(templatesPath)) return null;
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(readFileSync(templatesPath, 'utf8'));
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ensureGitignore(dir, content) {
|
|
35
|
+
if (!content) return;
|
|
36
|
+
const gitignorePath = resolve(dir, '.gitignore');
|
|
37
|
+
if (!existsSync(gitignorePath)) {
|
|
38
|
+
writeFileSync(gitignorePath, content, 'utf8');
|
|
39
|
+
console.log(`Created .gitignore in ${dir}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function resolveUIBundlesDir() {
|
|
44
|
+
const sfdxPath = resolve(ROOT, 'sfdx-project.json');
|
|
45
|
+
if (!existsSync(sfdxPath)) {
|
|
46
|
+
console.error('Error: sfdx-project.json not found at project root.');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const sfdxProject = JSON.parse(readFileSync(sfdxPath, 'utf8'));
|
|
50
|
+
const pkgDir = sfdxProject?.packageDirectories?.[0]?.path;
|
|
51
|
+
if (!pkgDir) {
|
|
52
|
+
console.error('Error: No packageDirectories[].path found in sfdx-project.json.');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
return resolve(ROOT, pkgDir, 'main', 'default', 'uiBundles');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function discoverUIBundleDir() {
|
|
59
|
+
const uiBundlesDir = resolveUIBundlesDir();
|
|
60
|
+
if (!existsSync(uiBundlesDir)) {
|
|
61
|
+
console.error(`Error: uiBundles directory not found: ${uiBundlesDir}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const entries = readdirSync(uiBundlesDir, { withFileTypes: true });
|
|
65
|
+
const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith('.'));
|
|
66
|
+
if (dirs.length === 0) {
|
|
67
|
+
console.error(`Error: No UI bundle folder found under ${uiBundlesDir}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
if (dirs.length > 1) {
|
|
71
|
+
console.log(`Multiple UI bundles found; using first: ${dirs[0].name}`);
|
|
72
|
+
}
|
|
73
|
+
return resolve(uiBundlesDir, dirs[0].name);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function run(label, cmd, args, opts) {
|
|
77
|
+
console.log(`\n--- ${label} ---\n`);
|
|
78
|
+
const result = spawnSync(cmd, args, { stdio: 'inherit', ...opts });
|
|
79
|
+
if (result.status !== 0) {
|
|
80
|
+
process.exit(result.status ?? 1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Ensure .gitignore files exist (npm strips them from published packages).
|
|
85
|
+
const gitignoreTemplates = loadGitignoreTemplates();
|
|
86
|
+
if (gitignoreTemplates) {
|
|
87
|
+
ensureGitignore(ROOT, gitignoreTemplates.sfdx);
|
|
88
|
+
const bundlesDir = resolveUIBundlesDir();
|
|
89
|
+
if (existsSync(bundlesDir)) {
|
|
90
|
+
for (const entry of readdirSync(bundlesDir, { withFileTypes: true })) {
|
|
91
|
+
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
|
92
|
+
ensureGitignore(resolve(bundlesDir, entry.name), gitignoreTemplates.webapp);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const uiBundleDir = discoverUIBundleDir();
|
|
99
|
+
console.log('SFDX project root:', ROOT);
|
|
100
|
+
console.log('UI bundle directory:', uiBundleDir);
|
|
101
|
+
|
|
102
|
+
run('npm install', 'npm', ['install', '--registry', 'https://registry.npmjs.org/'], { cwd: uiBundleDir });
|
|
103
|
+
run('npm run build', 'npm', ['run', 'build'], { cwd: uiBundleDir });
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build / CI gate: validate this package's org-setup.config.json against the
|
|
3
|
+
* shared schema (W-23043729, spec §5.2).
|
|
4
|
+
*
|
|
5
|
+
* Ships inside the SFDX project template, so a generated app can self-check its
|
|
6
|
+
* org-setup.config.json in its own CI:
|
|
7
|
+
* npm run validate:org-setup-config
|
|
8
|
+
*
|
|
9
|
+
* Uses the SAME validateConfig + schema as org-setup.mjs runtime, so build-time
|
|
10
|
+
* and runtime checks can never drift. A missing config file is OK (not every
|
|
11
|
+
* project ships one); a present-but-invalid config fails the gate.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
15
|
+
import { resolve, dirname } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
|
|
18
|
+
import { validateConfig } from './org-setup-config-schema.mjs';
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const configPath = resolve(__dirname, 'org-setup.config.json');
|
|
22
|
+
|
|
23
|
+
if (!existsSync(configPath)) {
|
|
24
|
+
console.log('✓ org-setup.config.json: not present (nothing to validate)');
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const result = validateConfig(readFileSync(configPath, 'utf8'), configPath);
|
|
29
|
+
if (result.ok) {
|
|
30
|
+
console.log('✓ org-setup.config.json');
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.error('❌ org-setup.config.json is invalid:');
|
|
35
|
+
for (const err of result.errors) {
|
|
36
|
+
console.error(` - ${err}`);
|
|
37
|
+
}
|
|
38
|
+
process.exit(1);
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@salesforce/ui-bundle-template-feature-react-search",
|
|
3
|
+
"version": "11.3.0",
|
|
4
|
+
"description": "Configuration-driven multi-source search for UI Bundles",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
|
+
"author": "",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "src/force-app/main/default/uiBundles/feature-react-search/src/features/search/index.ts",
|
|
9
|
+
"types": "src/force-app/main/default/uiBundles/feature-react-search/src/features/search/index.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./src/force-app/main/default/uiBundles/feature-react-search/src/features/search/index.ts",
|
|
13
|
+
"import": "./src/force-app/main/default/uiBundles/feature-react-search/src/features/search/index.ts",
|
|
14
|
+
"default": "./src/force-app/main/default/uiBundles/feature-react-search/src/features/search/index.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"clean": "rm -rf dist"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@radix-ui/react-label": "^2.1.10",
|
|
29
|
+
"@types/react": "^19.2.17",
|
|
30
|
+
"class-variance-authority": "^0.7.1",
|
|
31
|
+
"lucide-react": "^1.20.0",
|
|
32
|
+
"radix-ui": "^1.6.0",
|
|
33
|
+
"react-router": "^7.18.0"
|
|
34
|
+
},
|
|
35
|
+
"nx": {
|
|
36
|
+
"targets": {
|
|
37
|
+
"build": {
|
|
38
|
+
"executor": "@salesforce/ui-bundle-template-cli:apply-patches"
|
|
39
|
+
},
|
|
40
|
+
"watch": {
|
|
41
|
+
"executor": "@salesforce/ui-bundle-template-cli:watch-patches"
|
|
42
|
+
},
|
|
43
|
+
"build:dist-app": {
|
|
44
|
+
"executor": "@salesforce/ui-bundle-template-cli:build-dist-app"
|
|
45
|
+
},
|
|
46
|
+
"dev": {
|
|
47
|
+
"executor": "@salesforce/ui-bundle-template-cli:dev-server"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
const alertVariants = cva(
|
|
4
|
+
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
|
5
|
+
{
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
default: "bg-card text-card-foreground",
|
|
9
|
+
destructive:
|
|
10
|
+
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
defaultVariants: {
|
|
14
|
+
variant: "default",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
function Alert({
|
|
20
|
+
className,
|
|
21
|
+
variant,
|
|
22
|
+
role,
|
|
23
|
+
...props
|
|
24
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
|
25
|
+
return <> </>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface AlertTitleProps extends React.ComponentProps<"div"> {
|
|
29
|
+
as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "div" | "p";
|
|
30
|
+
}
|
|
31
|
+
function AlertTitle({ className, as: Component = "div", ...props }: AlertTitleProps) {
|
|
32
|
+
return <></>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function AlertDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
36
|
+
return <></>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { Alert, AlertTitle, AlertDescription };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
|
|
4
|
+
const buttonVariants = cva(
|
|
5
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
|
|
6
|
+
{
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
10
|
+
outline:
|
|
11
|
+
"border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
12
|
+
secondary:
|
|
13
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
14
|
+
ghost:
|
|
15
|
+
"hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
16
|
+
destructive:
|
|
17
|
+
"bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
|
|
18
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
19
|
+
},
|
|
20
|
+
size: {
|
|
21
|
+
default:
|
|
22
|
+
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
23
|
+
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
24
|
+
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
25
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
|
|
26
|
+
icon: "size-8",
|
|
27
|
+
"icon-xs":
|
|
28
|
+
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
29
|
+
"icon-sm":
|
|
30
|
+
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
31
|
+
"icon-lg": "size-9",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: {
|
|
35
|
+
variant: "default",
|
|
36
|
+
size: "default",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
function Button({}: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants>) {
|
|
42
|
+
return <> </>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "./__inherit__button";
|
|
3
|
+
|
|
4
|
+
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => <></>;
|
|
5
|
+
Pagination.displayName = "Pagination";
|
|
6
|
+
|
|
7
|
+
const PaginationContent = React.forwardRef<HTMLUListElement, React.ComponentProps<"ul">>(
|
|
8
|
+
({ className, ...props }, ref) => <></>,
|
|
9
|
+
);
|
|
10
|
+
PaginationContent.displayName = "PaginationContent";
|
|
11
|
+
|
|
12
|
+
const PaginationItem = React.forwardRef<HTMLLIElement, React.ComponentProps<"li">>(
|
|
13
|
+
({ className, ...props }, ref) => <></>,
|
|
14
|
+
);
|
|
15
|
+
PaginationItem.displayName = "PaginationItem";
|
|
16
|
+
|
|
17
|
+
type PaginationLinkProps = {
|
|
18
|
+
isActive?: boolean;
|
|
19
|
+
} & React.ComponentProps<"button">;
|
|
20
|
+
|
|
21
|
+
const PaginationLink = ({ className, isActive, disabled, ...props }: PaginationLinkProps) => <></>;
|
|
22
|
+
PaginationLink.displayName = "PaginationLink";
|
|
23
|
+
|
|
24
|
+
const PaginationPrevious = ({
|
|
25
|
+
className,
|
|
26
|
+
disabled,
|
|
27
|
+
...props
|
|
28
|
+
}: React.ComponentProps<typeof Button>) => <></>;
|
|
29
|
+
PaginationPrevious.displayName = "PaginationPrevious";
|
|
30
|
+
|
|
31
|
+
const PaginationNext = ({ className, disabled, ...props }: React.ComponentProps<typeof Button>) => (
|
|
32
|
+
<></>
|
|
33
|
+
);
|
|
34
|
+
PaginationNext.displayName = "PaginationNext";
|
|
35
|
+
|
|
36
|
+
const PaginationEllipsis = ({ className, ...props }: React.ComponentProps<"span">) => <></>;
|
|
37
|
+
PaginationEllipsis.displayName = "PaginationEllipsis";
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
Pagination,
|
|
41
|
+
PaginationContent,
|
|
42
|
+
PaginationEllipsis,
|
|
43
|
+
PaginationItem,
|
|
44
|
+
PaginationLink,
|
|
45
|
+
PaginationNext,
|
|
46
|
+
PaginationPrevious,
|
|
47
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Select as SelectPrimitive } from "radix-ui";
|
|
3
|
+
|
|
4
|
+
function Select({}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
|
5
|
+
return <></>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function SelectGroup({}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
|
9
|
+
return <></>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function SelectValue({}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
|
13
|
+
return <></>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function SelectTrigger({}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
|
17
|
+
size?: "sm" | "default";
|
|
18
|
+
}) {
|
|
19
|
+
return <></>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function SelectContent({}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
|
23
|
+
return <></>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function SelectLabel({}: React.ComponentProps<typeof SelectPrimitive.Label>) {
|
|
27
|
+
return <></>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function SelectItem({}: React.ComponentProps<typeof SelectPrimitive.Item>) {
|
|
31
|
+
return <></>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function SelectSeparator({}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
|
|
35
|
+
return <></>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function SelectScrollUpButton({}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
|
39
|
+
return <></>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function SelectScrollDownButton({}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
|
43
|
+
return <></>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
Select,
|
|
48
|
+
SelectContent,
|
|
49
|
+
SelectGroup,
|
|
50
|
+
SelectItem,
|
|
51
|
+
SelectLabel,
|
|
52
|
+
SelectScrollDownButton,
|
|
53
|
+
SelectScrollUpButton,
|
|
54
|
+
SelectSeparator,
|
|
55
|
+
SelectTrigger,
|
|
56
|
+
SelectValue,
|
|
57
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches distinct values for a picklist field via the uiapi aggregate
|
|
3
|
+
* `groupBy` query.
|
|
4
|
+
*
|
|
5
|
+
* Used by the auto-render pathway when a picklist filter does not declare
|
|
6
|
+
* inline `options`. Each call hits the GraphQL endpoint once and returns the
|
|
7
|
+
* sorted set of distinct values plus their display labels.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createDataSDK } from "@salesforce/platform-sdk";
|
|
11
|
+
|
|
12
|
+
export interface PicklistOption {
|
|
13
|
+
value: string;
|
|
14
|
+
label: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
18
|
+
|
|
19
|
+
function assertValidIdentifier(name: string, kind: string): void {
|
|
20
|
+
if (!KEY_PATTERN.test(name)) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Invalid ${kind} "${name}". Must match /^[A-Za-z_][A-Za-z0-9_]*$/ to be a valid GraphQL identifier.`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Builds and executes a `uiapi.aggregate.<objectName>(groupBy: { <field>: { group: true } })`
|
|
29
|
+
* query and extracts the distinct values.
|
|
30
|
+
*/
|
|
31
|
+
export async function fetchDistinctValues(
|
|
32
|
+
objectName: string,
|
|
33
|
+
fieldName: string,
|
|
34
|
+
): Promise<PicklistOption[]> {
|
|
35
|
+
assertValidIdentifier(objectName, "objectName");
|
|
36
|
+
assertValidIdentifier(fieldName, "fieldName");
|
|
37
|
+
|
|
38
|
+
const document = `query DistinctValues {
|
|
39
|
+
uiapi {
|
|
40
|
+
aggregate {
|
|
41
|
+
${objectName}(groupBy: { ${fieldName}: { group: true } }) {
|
|
42
|
+
edges {
|
|
43
|
+
node {
|
|
44
|
+
aggregate @optional {
|
|
45
|
+
${fieldName} @optional {
|
|
46
|
+
value
|
|
47
|
+
displayValue
|
|
48
|
+
label
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}`;
|
|
57
|
+
|
|
58
|
+
const data = await createDataSDK();
|
|
59
|
+
const response = await data.graphql!.query<unknown>({ query: document });
|
|
60
|
+
|
|
61
|
+
if (response.errors?.length) {
|
|
62
|
+
throw new Error(response.errors.map((e) => e.message).join("; "));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const root = response.data as Record<string, unknown> | undefined;
|
|
66
|
+
const aggregate = (root?.uiapi as Record<string, unknown> | undefined)?.aggregate as
|
|
67
|
+
| Record<string, unknown>
|
|
68
|
+
| undefined;
|
|
69
|
+
const objectAgg = aggregate?.[objectName] as
|
|
70
|
+
| { edges?: Array<{ node?: { aggregate?: Record<string, unknown> } }> }
|
|
71
|
+
| undefined;
|
|
72
|
+
const edges = objectAgg?.edges ?? [];
|
|
73
|
+
|
|
74
|
+
return edges
|
|
75
|
+
.map((edge) => {
|
|
76
|
+
const field = edge?.node?.aggregate?.[fieldName] as
|
|
77
|
+
| { value?: string | null; displayValue?: string | null; label?: string | null }
|
|
78
|
+
| undefined;
|
|
79
|
+
const value = field?.value;
|
|
80
|
+
if (value == null || value === "") return null;
|
|
81
|
+
return { value, label: field?.label ?? field?.displayValue ?? value };
|
|
82
|
+
})
|
|
83
|
+
.filter((opt): opt is PicklistOption => opt !== null);
|
|
84
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executes a search request and parses results into a per-source map.
|
|
3
|
+
*
|
|
4
|
+
* Today there's only one adapter — SObjects via the platform-sdk uiapi GraphQL
|
|
5
|
+
* bridge. Future adapters (CMS, REST, etc.) can run alongside this one and
|
|
6
|
+
* have their results merged into the same `Record<sourceKey, SourceResult>`
|
|
7
|
+
* by the hook.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createDataSDK } from "@salesforce/platform-sdk";
|
|
11
|
+
import { buildSearchQuery, type SourceRequest } from "../queryBuilder";
|
|
12
|
+
import type { SourceResult } from "../types";
|
|
13
|
+
|
|
14
|
+
interface RawSourceResult {
|
|
15
|
+
edges?: Array<{ node?: unknown }> | null;
|
|
16
|
+
pageInfo?: {
|
|
17
|
+
hasNextPage?: boolean | null;
|
|
18
|
+
hasPreviousPage?: boolean | null;
|
|
19
|
+
startCursor?: string | null;
|
|
20
|
+
endCursor?: string | null;
|
|
21
|
+
} | null;
|
|
22
|
+
totalCount?: number | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Runs a single multi-aliased GraphQL request against uiapi.query.
|
|
27
|
+
*
|
|
28
|
+
* Returns a plain object keyed by source.key. A source whose alias is missing
|
|
29
|
+
* from the response (e.g. due to a partial GraphQL error) is omitted; the
|
|
30
|
+
* caller can detect this by `result[sourceKey] == null`.
|
|
31
|
+
*/
|
|
32
|
+
export async function runSearch(requests: SourceRequest[]): Promise<Record<string, SourceResult>> {
|
|
33
|
+
const { document, variables } = buildSearchQuery(requests);
|
|
34
|
+
|
|
35
|
+
const data = await createDataSDK();
|
|
36
|
+
const response = await data.graphql!.query<unknown, Record<string, unknown>>({
|
|
37
|
+
query: document,
|
|
38
|
+
variables,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (response.errors?.length) {
|
|
42
|
+
throw new Error(response.errors.map((e) => e.message).join("; "));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const root = response.data as Record<string, unknown> | undefined;
|
|
46
|
+
const queryRoot = (root?.uiapi as Record<string, unknown> | undefined)?.query as
|
|
47
|
+
| Record<string, RawSourceResult | null | undefined>
|
|
48
|
+
| undefined;
|
|
49
|
+
|
|
50
|
+
const out: Record<string, SourceResult> = {};
|
|
51
|
+
for (const request of requests) {
|
|
52
|
+
const raw = queryRoot?.[request.source.key];
|
|
53
|
+
if (raw == null) continue;
|
|
54
|
+
const nodes = (raw.edges ?? [])
|
|
55
|
+
.map((edge) => edge?.node)
|
|
56
|
+
.filter((node): node is unknown => node != null);
|
|
57
|
+
out[request.source.key] = {
|
|
58
|
+
nodes,
|
|
59
|
+
pageInfo: raw.pageInfo
|
|
60
|
+
? {
|
|
61
|
+
hasNextPage: raw.pageInfo.hasNextPage ?? false,
|
|
62
|
+
hasPreviousPage: raw.pageInfo.hasPreviousPage ?? false,
|
|
63
|
+
startCursor: raw.pageInfo.startCursor ?? null,
|
|
64
|
+
endCursor: raw.pageInfo.endCursor ?? null,
|
|
65
|
+
}
|
|
66
|
+
: null,
|
|
67
|
+
totalCount: raw.totalCount ?? null,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|