@salesforce/webapp-template-feature-react-file-upload-experimental 1.55.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.
Files changed (126) hide show
  1. package/LICENSE.txt +82 -0
  2. package/README.md +102 -0
  3. package/dist/.a4drules/README.md +35 -0
  4. package/dist/.a4drules/a4d-webapp-generate.md +27 -0
  5. package/dist/.a4drules/build-validation.md +78 -0
  6. package/dist/.a4drules/code-quality.md +137 -0
  7. package/dist/.a4drules/graphql/tools/knowledge/lds-explore-graphql-schema.md +227 -0
  8. package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md +212 -0
  9. package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-readquery.md +185 -0
  10. package/dist/.a4drules/graphql/tools/knowledge/lds-guide-graphql.md +205 -0
  11. package/dist/.a4drules/graphql/tools/schemas/shared.graphqls +1150 -0
  12. package/dist/.a4drules/graphql.md +409 -0
  13. package/dist/.a4drules/images.md +13 -0
  14. package/dist/.a4drules/react.md +387 -0
  15. package/dist/.a4drules/react_image_processing.md +45 -0
  16. package/dist/.a4drules/typescript.md +224 -0
  17. package/dist/.a4drules/ui-layout.md +23 -0
  18. package/dist/.a4drules/webapp-nav-and-placeholders.md +33 -0
  19. package/dist/.a4drules/webapp-no-node-e.md +25 -0
  20. package/dist/.a4drules/webapp-ui-first.md +32 -0
  21. package/dist/.a4drules/webapp.md +75 -0
  22. package/dist/.forceignore +15 -0
  23. package/dist/.husky/pre-commit +4 -0
  24. package/dist/.prettierignore +11 -0
  25. package/dist/.prettierrc +17 -0
  26. package/dist/AGENT.md +75 -0
  27. package/dist/CHANGELOG.md +803 -0
  28. package/dist/README.md +18 -0
  29. package/dist/config/project-scratch-def.json +13 -0
  30. package/dist/force-app/main/default/webapplications/feature-react-file-upload/.graphqlrc.yml +2 -0
  31. package/dist/force-app/main/default/webapplications/feature-react-file-upload/.prettierignore +9 -0
  32. package/dist/force-app/main/default/webapplications/feature-react-file-upload/.prettierrc +11 -0
  33. package/dist/force-app/main/default/webapplications/feature-react-file-upload/build/vite.config.d.ts +2 -0
  34. package/dist/force-app/main/default/webapplications/feature-react-file-upload/build/vite.config.js +93 -0
  35. package/dist/force-app/main/default/webapplications/feature-react-file-upload/codegen.yml +94 -0
  36. package/dist/force-app/main/default/webapplications/feature-react-file-upload/e2e/app.spec.ts +17 -0
  37. package/dist/force-app/main/default/webapplications/feature-react-file-upload/eslint.config.js +141 -0
  38. package/dist/force-app/main/default/webapplications/feature-react-file-upload/feature-react-file-upload.webapplication-meta.xml +7 -0
  39. package/dist/force-app/main/default/webapplications/feature-react-file-upload/index.html +13 -0
  40. package/dist/force-app/main/default/webapplications/feature-react-file-upload/package-lock.json +18396 -0
  41. package/dist/force-app/main/default/webapplications/feature-react-file-upload/package.json +66 -0
  42. package/dist/force-app/main/default/webapplications/feature-react-file-upload/playwright.config.ts +24 -0
  43. package/dist/force-app/main/default/webapplications/feature-react-file-upload/scripts/get-graphql-schema.mjs +68 -0
  44. package/dist/force-app/main/default/webapplications/feature-react-file-upload/scripts/rewrite-e2e-assets.mjs +23 -0
  45. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/api/fileUpload.ts +154 -0
  46. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/api/graphql-operations-types.ts +116 -0
  47. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/api/utils/accounts.ts +41 -0
  48. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/api/utils/query/highRevenueAccountsQuery.graphql +29 -0
  49. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/app.tsx +22 -0
  50. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/appLayout.tsx +9 -0
  51. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/icons/book.svg +3 -0
  52. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/icons/copy.svg +4 -0
  53. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/icons/rocket.svg +3 -0
  54. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/icons/star.svg +3 -0
  55. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/images/codey-1.png +0 -0
  56. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/images/codey-2.png +0 -0
  57. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/images/codey-3.png +0 -0
  58. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/images/vibe-codey.svg +194 -0
  59. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/symbols.svg +1 -0
  60. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/utility.svg +1 -0
  61. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUpload.tsx +83 -0
  62. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDialog.tsx +79 -0
  63. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDropZone.tsx +82 -0
  64. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadFileItem.tsx +99 -0
  65. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadIcons.tsx +58 -0
  66. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/alert.tsx +69 -0
  67. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/button.tsx +67 -0
  68. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/card.tsx +92 -0
  69. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/dialog.tsx +143 -0
  70. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/field.tsx +222 -0
  71. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/index.ts +84 -0
  72. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/input.tsx +19 -0
  73. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/label.tsx +19 -0
  74. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/pagination.tsx +112 -0
  75. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/select.tsx +183 -0
  76. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/separator.tsx +26 -0
  77. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/skeleton.tsx +14 -0
  78. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/spinner.tsx +15 -0
  79. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/table.tsx +87 -0
  80. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/tabs.tsx +78 -0
  81. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components.json +18 -0
  82. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUpload.ts +299 -0
  83. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUploadDialog.ts +70 -0
  84. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/index.ts +56 -0
  85. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/lib/utils.ts +6 -0
  86. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/navigationMenu.tsx +80 -0
  87. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/pages/Home.tsx +25 -0
  88. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/pages/NotFound.tsx +18 -0
  89. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/router-utils.tsx +35 -0
  90. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/routes.tsx +22 -0
  91. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/styles/global.css +135 -0
  92. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/types/fileUpload.ts +26 -0
  93. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/utils/fileUploadUtils.ts +44 -0
  94. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/utils/labels.ts +21 -0
  95. package/dist/force-app/main/default/webapplications/feature-react-file-upload/tsconfig.json +36 -0
  96. package/dist/force-app/main/default/webapplications/feature-react-file-upload/tsconfig.node.json +13 -0
  97. package/dist/force-app/main/default/webapplications/feature-react-file-upload/vite-env.d.ts +1 -0
  98. package/dist/force-app/main/default/webapplications/feature-react-file-upload/vite.config.ts +43 -0
  99. package/dist/force-app/main/default/webapplications/feature-react-file-upload/vitest-env.d.ts +2 -0
  100. package/dist/force-app/main/default/webapplications/feature-react-file-upload/vitest.config.ts +11 -0
  101. package/dist/force-app/main/default/webapplications/feature-react-file-upload/vitest.setup.ts +1 -0
  102. package/dist/force-app/main/default/webapplications/feature-react-file-upload/webapplication.json +7 -0
  103. package/dist/jest.config.js +6 -0
  104. package/dist/package.json +38 -0
  105. package/dist/scripts/apex/hello.apex +10 -0
  106. package/dist/scripts/soql/account.soql +6 -0
  107. package/dist/sfdx-project.json +12 -0
  108. package/package.json +53 -0
  109. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/api/fileUpload.ts +154 -0
  110. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/appLayout.tsx +9 -0
  111. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/assets/symbols.svg +1 -0
  112. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/assets/utility.svg +1 -0
  113. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUpload.tsx +83 -0
  114. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDialog.tsx +79 -0
  115. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDropZone.tsx +82 -0
  116. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadFileItem.tsx +99 -0
  117. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadIcons.tsx +58 -0
  118. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUpload.ts +299 -0
  119. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUploadDialog.ts +70 -0
  120. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/index.ts +56 -0
  121. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/pages/Home.tsx +25 -0
  122. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/routes.tsx +17 -0
  123. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/types/fileUpload.ts +26 -0
  124. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/utils/fileUploadUtils.ts +44 -0
  125. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/utils/labels.ts +21 -0
  126. package/src/force-app/main/default/webapplications/feature-react-file-upload/vite.config.ts +43 -0
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "base-react-app",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "build:e2e": "npm run build && node scripts/rewrite-e2e-assets.mjs",
10
+ "lint": "eslint .",
11
+ "preview": "vite preview",
12
+ "test": "vitest",
13
+ "graphql:codegen": "graphql-codegen",
14
+ "graphql:schema": "node scripts/get-graphql-schema.mjs"
15
+ },
16
+ "dependencies": {
17
+ "@salesforce/sdk-data": "^1.11.2",
18
+ "@salesforce/webapp-experimental": "*",
19
+ "@tailwindcss/vite": "^4.1.17",
20
+ "@tanstack/react-form": "^1.28.3",
21
+ "class-variance-authority": "^0.7.1",
22
+ "clsx": "^2.1.1",
23
+ "lucide-react": "^0.562.0",
24
+ "radix-ui": "^1.4.3",
25
+ "react": "^19.2.0",
26
+ "react-dom": "^19.2.0",
27
+ "react-router": "^7.10.1",
28
+ "shadcn": "^3.8.5",
29
+ "tailwind-merge": "^3.5.0",
30
+ "tailwindcss": "^4.1.17",
31
+ "tw-animate-css": "^1.4.0",
32
+ "zod": "^4.3.6"
33
+ },
34
+ "devDependencies": {
35
+ "@eslint/js": "^9.39.1",
36
+ "@graphql-codegen/cli": "^6.1.0",
37
+ "@graphql-codegen/typescript": "^5.0.6",
38
+ "@graphql-codegen/typescript-operations": "^5.0.6",
39
+ "@graphql-eslint/eslint-plugin": "^4.1.0",
40
+ "@graphql-tools/utils": "^11.0.0",
41
+ "@playwright/test": "^1.49.0",
42
+ "@salesforce/vite-plugin-webapp-experimental": "*",
43
+ "@testing-library/jest-dom": "^6.6.3",
44
+ "@testing-library/react": "^16.1.0",
45
+ "@testing-library/user-event": "^14.5.2",
46
+ "@types/node": "^24.10.1",
47
+ "@types/react": "^19.2.5",
48
+ "@types/react-dom": "^19.2.3",
49
+ "@vitejs/plugin-react": "^5.1.1",
50
+ "@vitest/ui": "^4.0.17",
51
+ "eslint": "^9.39.1",
52
+ "eslint-plugin-react": "^7.37.2",
53
+ "eslint-plugin-react-hooks": "^7.0.1",
54
+ "eslint-plugin-react-refresh": "^0.4.24",
55
+ "globals": "^16.5.0",
56
+ "graphql": "^16.11.0",
57
+ "graphql-codegen-typescript-operation-types": "^2.0.2",
58
+ "jsdom": "^25.0.1",
59
+ "serve": "^14.2.5",
60
+ "typescript": "~5.9.3",
61
+ "typescript-eslint": "^8.46.4",
62
+ "vite": "^7.2.4",
63
+ "vite-plugin-graphql-codegen": "^3.6.3",
64
+ "vitest": "^4.0.17"
65
+ }
66
+ }
@@ -0,0 +1,24 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+
3
+ const E2E_PORT = 5175;
4
+
5
+ export default defineConfig({
6
+ testDir: './e2e',
7
+ fullyParallel: true,
8
+ forbidOnly: !!process.env.CI,
9
+ retries: process.env.CI ? 2 : 0,
10
+ workers: process.env.CI ? 1 : undefined,
11
+ reporter: 'html',
12
+ use: {
13
+ baseURL: `http://localhost:${E2E_PORT}`,
14
+ trace: 'on-first-retry',
15
+ },
16
+ projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
17
+ webServer: {
18
+ // Serve built dist/ with static server so e2e works in CI without SF org (vite preview runs plugin and can fail)
19
+ command: `npx serve dist -l ${E2E_PORT}`,
20
+ url: `http://localhost:${E2E_PORT}`,
21
+ reuseExistingServer: !process.env.CI,
22
+ timeout: process.env.CI ? 120_000 : 60_000,
23
+ },
24
+ });
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Downloads the full GraphQL schema from a connected Salesforce org via introspection.
3
+ *
4
+ * Usage:
5
+ * npm run graphql:schema
6
+ * node scripts/get-graphql-schema.mjs [output-path]
7
+ *
8
+ * The default output path matches the schema location expected by codegen.yml
9
+ * and .graphqlrc.yml so that codegen and IDE tooling resolve it automatically.
10
+ */
11
+ import { writeFileSync } from 'node:fs';
12
+ import { resolve } from 'node:path';
13
+ import { getOrgInfo } from '@salesforce/webapp-experimental/app';
14
+ import { buildClientSchema, getIntrospectionQuery, printSchema } from 'graphql';
15
+ import { pruneSchema } from '@graphql-tools/utils';
16
+
17
+ const DEFAULT_SCHEMA_PATH = '../../../../../schema.graphql';
18
+
19
+ async function executeSalesforceGraphQLQuery(query, variables, operationName) {
20
+ const {
21
+ rawInstanceUrl: instanceUrl,
22
+ apiVersion,
23
+ accessToken,
24
+ } = await getOrgInfo();
25
+
26
+ const targetUrl = `${instanceUrl}/services/data/v${apiVersion}/graphql`;
27
+
28
+ console.log(`Executing introspection query against ${targetUrl}`);
29
+ const response = await fetch(targetUrl, {
30
+ method: 'POST',
31
+ headers: {
32
+ Authorization: `Bearer ${accessToken}`,
33
+ 'Content-Type': 'application/json',
34
+ 'X-Chatter-Entity-Encoding': 'false',
35
+ },
36
+ body: JSON.stringify({ query, variables, operationName }),
37
+ });
38
+
39
+ if (!response.ok) {
40
+ const errorText = await response.text();
41
+ throw new Error(
42
+ `Salesforce GraphQL request failed: ${response.status} ${response.statusText} - ${errorText}`
43
+ );
44
+ }
45
+
46
+ return response.json();
47
+ }
48
+
49
+ try {
50
+ const outputPath = resolve(process.argv[2] || DEFAULT_SCHEMA_PATH);
51
+
52
+ const introspectionResult = await executeSalesforceGraphQLQuery(
53
+ getIntrospectionQuery(),
54
+ {},
55
+ 'IntrospectionQuery'
56
+ );
57
+
58
+ const schema = buildClientSchema(introspectionResult.data);
59
+ const prunedSchema = pruneSchema(schema);
60
+ const sdl = printSchema(prunedSchema);
61
+
62
+ writeFileSync(outputPath, sdl);
63
+
64
+ console.log(`Schema saved to ${outputPath}`);
65
+ } catch (error) {
66
+ console.error('Error:', error.message);
67
+ process.exit(1);
68
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Prepares dist/ for e2e: root-relative asset paths + SPA fallback for serve.
3
+ */
4
+ import { readFileSync, writeFileSync } from 'node:fs';
5
+ import { join, dirname } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const distDir = join(__dirname, '..', 'dist');
10
+
11
+ // Rewrite index.html so asset paths are root-relative (/assets/...)
12
+ const indexPath = join(distDir, 'index.html');
13
+ let html = readFileSync(indexPath, 'utf8');
14
+ html = html.replace(/(src|href)="[^"]*\/assets\//g, '$1="/assets/');
15
+ writeFileSync(indexPath, html);
16
+
17
+ // SPA fallback so /about, /non-existent-route etc. serve index.html
18
+ writeFileSync(
19
+ join(distDir, 'serve.json'),
20
+ JSON.stringify({
21
+ rewrites: [{ source: '**', destination: '/index.html' }],
22
+ })
23
+ );
@@ -0,0 +1,154 @@
1
+ /**
2
+ * File upload API – config, upload, and ContentVersion creation
3
+ */
4
+
5
+ import { createRecord, getCurrentUser } from "@salesforce/webapp-experimental/api";
6
+ import { createDataSDK, type DataSDK } from "@salesforce/sdk-data";
7
+
8
+ // Lazy-initialized SDK instance
9
+ let sdkInstance: DataSDK | null = null;
10
+ async function getSDK() {
11
+ if (!sdkInstance) {
12
+ sdkInstance = await createDataSDK();
13
+ }
14
+ return sdkInstance;
15
+ }
16
+
17
+ declare const __SF_API_VERSION__: string;
18
+
19
+ export interface UploadConfig {
20
+ token: string;
21
+ uploadUrl: string;
22
+ }
23
+
24
+ /**
25
+ * Get upload config (token, uploadUrl) from /connect/file/upload/config.
26
+ */
27
+ export async function getUploadConfig(): Promise<UploadConfig> {
28
+ const sdk = await getSDK();
29
+ if (!sdk?.fetch) {
30
+ throw new Error("Failed to initialize SDK");
31
+ }
32
+ const API_VERSION = __SF_API_VERSION__ || "65.0";
33
+ const configRes = await sdk.fetch(`/services/data/v${API_VERSION}/connect/file/upload/config`, {
34
+ method: "GET",
35
+ });
36
+ if (!configRes.ok) {
37
+ throw new Error(`Failed to get upload config: ${configRes.status} ${configRes.statusText}`);
38
+ }
39
+ const config = (await configRes.json()) as UploadConfig;
40
+ const { token, uploadUrl } = config;
41
+ if (!token || !uploadUrl) {
42
+ throw new Error("Invalid upload config: missing token or uploadUrl");
43
+ }
44
+ return { token, uploadUrl };
45
+ }
46
+
47
+ /**
48
+ * Upload file via XHR
49
+ * @param signal - Optional AbortSignal to cancel the upload
50
+ */
51
+ export function uploadToUrl(
52
+ file: File,
53
+ token: string,
54
+ uploadUrl: string,
55
+ onProgress: (percent: number) => void,
56
+ signal?: AbortSignal,
57
+ ): Promise<string> {
58
+ return new Promise((resolve, reject) => {
59
+ const xhr = new XMLHttpRequest();
60
+ const formData = new FormData();
61
+ formData.append("token", token);
62
+ formData.append("fromUITier", "true");
63
+ formData.append("target", "ContentVersion");
64
+ formData.append("origin", "LWC");
65
+ formData.append("file", file, file.name);
66
+
67
+ xhr.upload.addEventListener("progress", (e) => {
68
+ if (e.lengthComputable) {
69
+ onProgress(Math.round((e.loaded / e.total) * 100));
70
+ }
71
+ });
72
+
73
+ xhr.addEventListener("load", () => {
74
+ if (xhr.status < 200 || xhr.status >= 300) {
75
+ reject(new Error(`Upload failed: ${xhr.status} ${xhr.statusText}`));
76
+ return;
77
+ }
78
+ try {
79
+ const data = parseJsonResponse(xhr.responseText);
80
+ const id =
81
+ (data.content_body_id as string | undefined) ??
82
+ (data.contentBodyId as string | undefined);
83
+ if (!id) {
84
+ reject(new Error("Response missing content_body_id"));
85
+ return;
86
+ }
87
+ resolve(id);
88
+ } catch (err) {
89
+ reject(err instanceof Error ? err : new Error(String(err)));
90
+ }
91
+ });
92
+
93
+ xhr.addEventListener("error", () => reject(new Error("Network error during upload")));
94
+ xhr.addEventListener("abort", () => reject(new Error("Upload aborted")));
95
+
96
+ if (signal) {
97
+ signal.addEventListener("abort", () => {
98
+ xhr.abort();
99
+ });
100
+ }
101
+
102
+ xhr.open("POST", uploadUrl);
103
+ xhr.send(formData);
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Strip "while(1);" or similar JSON prefix from LWC-style responses.
109
+ */
110
+ function parseJsonResponse(text: string): Record<string, unknown> {
111
+ const trimmed = text.trim();
112
+ const prefix = "while(1);";
113
+ const jsonStr = trimmed.startsWith(prefix) ? trimmed.slice(prefix.length).trim() : trimmed;
114
+ return JSON.parse(jsonStr) as Record<string, unknown>;
115
+ }
116
+
117
+ /**
118
+ * Create ContentVersion record via ui-api createRecord.
119
+ */
120
+ export async function createContentVersion(
121
+ file: File,
122
+ contentBodyId: string,
123
+ userId: string,
124
+ ): Promise<string | undefined> {
125
+ const fields = {
126
+ FirstPublishLocationId: userId,
127
+ Title: fileNameWithoutExtension(file.name),
128
+ PathOnClient: file.name,
129
+ ContentBodyId: contentBodyId,
130
+ };
131
+
132
+ const response = await createRecord("ContentVersion", fields);
133
+ const id = (response as { id?: string }).id ?? response.fields?.Id?.value;
134
+ return id;
135
+ }
136
+
137
+ /**
138
+ * Get file name without extension.
139
+ */
140
+ function fileNameWithoutExtension(name: string): string {
141
+ const lastDot = name.lastIndexOf(".");
142
+ return lastDot > 0 ? name.slice(0, lastDot) : name;
143
+ }
144
+
145
+ /**
146
+ * Get current user Id (for FirstPublishLocationId).
147
+ */
148
+ export async function getCurrentUserId(): Promise<string> {
149
+ const user = await getCurrentUser();
150
+ if (!user?.id) {
151
+ throw new Error("Current user not found");
152
+ }
153
+ return user.id;
154
+ }
@@ -0,0 +1,116 @@
1
+ export type Maybe<T> = T | null;
2
+ export type InputMaybe<T> = Maybe<T>;
3
+ export type Exact<T extends { [key: string]: unknown }> = {
4
+ [K in keyof T]: T[K];
5
+ };
6
+ export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
7
+ [SubKey in K]?: Maybe<T[SubKey]>;
8
+ };
9
+ export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
10
+ [SubKey in K]: Maybe<T[SubKey]>;
11
+ };
12
+ export type MakeEmpty<
13
+ T extends { [key: string]: unknown },
14
+ K extends keyof T,
15
+ > = { [_ in K]?: never };
16
+ export type Incremental<T> =
17
+ | T
18
+ | {
19
+ [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never;
20
+ };
21
+ /** All built-in and custom scalars, mapped to their actual values */
22
+ export type Scalars = {
23
+ ID: { input: string; output: string };
24
+ String: { input: string; output: string };
25
+ Boolean: { input: boolean; output: boolean };
26
+ Int: { input: number; output: number };
27
+ Float: { input: number; output: number };
28
+ Base64: { input: string; output: string };
29
+ Currency: { input: number | string; output: number };
30
+ Date: { input: string; output: string };
31
+ DateTime: { input: string; output: string };
32
+ Double: { input: number | string; output: number };
33
+ Email: { input: string; output: string };
34
+ EncryptedString: { input: string; output: string };
35
+ /** Can be set to an ID or a Reference to the result of another mutation operation. */
36
+ IdOrRef: { input: string; output: string };
37
+ Latitude: { input: number | string; output: number };
38
+ /** A 64-bit signed integer */
39
+ Long: { input: number; output: number };
40
+ LongTextArea: { input: string; output: string };
41
+ Longitude: { input: number | string; output: number };
42
+ MultiPicklist: { input: string; output: string };
43
+ Percent: { input: number | string; output: number };
44
+ PhoneNumber: { input: string; output: string };
45
+ Picklist: { input: string; output: string };
46
+ RichTextArea: { input: string; output: string };
47
+ TextArea: { input: string; output: string };
48
+ Time: { input: string; output: string };
49
+ Url: { input: string; output: string };
50
+ };
51
+
52
+ export enum DataType {
53
+ Address = 'ADDRESS',
54
+ Anytype = 'ANYTYPE',
55
+ Base64 = 'BASE64',
56
+ Boolean = 'BOOLEAN',
57
+ Combobox = 'COMBOBOX',
58
+ Complexvalue = 'COMPLEXVALUE',
59
+ Currency = 'CURRENCY',
60
+ Date = 'DATE',
61
+ Datetime = 'DATETIME',
62
+ Double = 'DOUBLE',
63
+ Email = 'EMAIL',
64
+ Encryptedstring = 'ENCRYPTEDSTRING',
65
+ Int = 'INT',
66
+ Json = 'JSON',
67
+ Junctionidlist = 'JUNCTIONIDLIST',
68
+ Location = 'LOCATION',
69
+ Long = 'LONG',
70
+ Multipicklist = 'MULTIPICKLIST',
71
+ Percent = 'PERCENT',
72
+ Phone = 'PHONE',
73
+ Picklist = 'PICKLIST',
74
+ Reference = 'REFERENCE',
75
+ String = 'STRING',
76
+ Textarea = 'TEXTAREA',
77
+ Time = 'TIME',
78
+ Url = 'URL',
79
+ }
80
+
81
+ export enum FieldExtraTypeInfo {
82
+ ExternalLookup = 'EXTERNAL_LOOKUP',
83
+ ImageUrl = 'IMAGE_URL',
84
+ IndirectLookup = 'INDIRECT_LOOKUP',
85
+ Personname = 'PERSONNAME',
86
+ Plaintextarea = 'PLAINTEXTAREA',
87
+ Richtextarea = 'RICHTEXTAREA',
88
+ SwitchablePersonname = 'SWITCHABLE_PERSONNAME',
89
+ }
90
+
91
+ export enum ResultOrder {
92
+ Asc = 'ASC',
93
+ Desc = 'DESC',
94
+ }
95
+
96
+ export type GetHighRevenueAccountsQueryVariables = Exact<{
97
+ minRevenue?: InputMaybe<Scalars['Currency']['input']>;
98
+ }>;
99
+
100
+ export type GetHighRevenueAccountsQuery = {
101
+ uiapi: {
102
+ query: {
103
+ Account?: {
104
+ edges?: Array<{
105
+ node?: {
106
+ Id: string;
107
+ Name?: { value?: string | null } | null;
108
+ AnnualRevenue?: { value?: number | null } | null;
109
+ Industry?: { value?: string | null } | null;
110
+ Website?: { value?: string | null } | null;
111
+ } | null;
112
+ } | null> | null;
113
+ } | null;
114
+ };
115
+ };
116
+ };
@@ -0,0 +1,41 @@
1
+ import { getDataSDK } from '@salesforce/webapp-experimental/api';
2
+ import HIGH_REVENUE_ACCOUNTS_QUERY from './query/highRevenueAccountsQuery.graphql?raw';
3
+ import type {
4
+ GetHighRevenueAccountsQuery,
5
+ GetHighRevenueAccountsQueryVariables,
6
+ } from '../graphql-operations-types';
7
+
8
+ type AccountNode = NonNullable<
9
+ NonNullable<
10
+ NonNullable<
11
+ NonNullable<
12
+ GetHighRevenueAccountsQuery['uiapi']['query']['Account']
13
+ >['edges']
14
+ >[number]
15
+ >['node']
16
+ >;
17
+
18
+ /**
19
+ * Fetch accounts with annual revenue greater than the specified amount
20
+ *
21
+ * @param minRevenue - Minimum annual revenue threshold (default: 100000)
22
+ * @returns Array of accounts matching the criteria
23
+ */
24
+ export async function getHighRevenueAccounts(
25
+ variables: GetHighRevenueAccountsQueryVariables
26
+ ): Promise<(AccountNode | null | undefined)[]> {
27
+ const data = await getDataSDK();
28
+ const response = await data.graphql?.<
29
+ GetHighRevenueAccountsQuery,
30
+ GetHighRevenueAccountsQueryVariables
31
+ >(HIGH_REVENUE_ACCOUNTS_QUERY, variables);
32
+
33
+ if (response?.errors?.length) {
34
+ const errorMessages = response.errors.map(e => e.message).join('; ');
35
+ throw new Error(`GraphQL Error: ${errorMessages}`);
36
+ }
37
+
38
+ return (
39
+ response?.data?.uiapi?.query?.Account?.edges?.map(edge => edge?.node) || []
40
+ );
41
+ }
@@ -0,0 +1,29 @@
1
+ query GetHighRevenueAccounts($minRevenue: Currency) {
2
+ uiapi {
3
+ query {
4
+ Account(
5
+ where: { AnnualRevenue: { gt: $minRevenue } }
6
+ orderBy: { AnnualRevenue: { order: DESC } }
7
+ first: 50
8
+ ) {
9
+ edges {
10
+ node {
11
+ Id
12
+ Name {
13
+ value
14
+ }
15
+ AnnualRevenue {
16
+ value
17
+ }
18
+ Industry {
19
+ value
20
+ }
21
+ Website {
22
+ value
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,22 @@
1
+ import { createBrowserRouter, RouterProvider } from 'react-router';
2
+ import { routes } from '@/routes';
3
+ import { StrictMode } from 'react';
4
+ import { createRoot } from 'react-dom/client';
5
+ import './styles/global.css';
6
+
7
+ // Match Vite base so client-side routes work when deployed under a path (e.g. /lwr/application/ai/c-webapp2/).
8
+ // When served at root (e.g. e2e with static serve), use '/' so routes match.
9
+ const base = (import.meta.env.BASE_URL ?? '/').replace(/\/$/, '') || '/';
10
+ const basename =
11
+ typeof window !== 'undefined' &&
12
+ (window.location.pathname === '/' ||
13
+ !window.location.pathname.startsWith('/lwr/'))
14
+ ? '/'
15
+ : base;
16
+ const router = createBrowserRouter(routes, { basename });
17
+
18
+ createRoot(document.getElementById('root')!).render(
19
+ <StrictMode>
20
+ <RouterProvider router={router} />
21
+ </StrictMode>
22
+ );
@@ -0,0 +1,9 @@
1
+ import { Outlet } from "react-router";
2
+
3
+ export default function AppLayout() {
4
+ return (
5
+ <>
6
+ <Outlet />
7
+ </>
8
+ );
9
+ }
@@ -0,0 +1,3 @@
1
+ <svg width="14" height="12" viewBox="0 0 14 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M13.4935 0.000188828L13.9735 0.533522V10.5069L13.4935 11.0402H8.21348L7.36015 11.8935H6.61348L5.76015 11.0402H0.48015L0.000149727 10.5069V0.533522L0.48015 0.000188828H5.97348L6.34682 0.160189L6.98682 0.800188L7.62682 0.160189L8.00015 0.000188828H13.4935ZM6.50682 10.3469V1.76019L5.76015 1.01352H1.01348V10.0269H5.97348L6.29348 10.1869L6.50682 10.3469ZM13.0135 10.0269V1.01352H8.21348L7.52015 1.70685V10.2935L7.62682 10.1869L8.00015 10.0269H13.0135ZM5.01348 3.04019V4.00019H1.97348V3.04019H5.01348ZM5.01348 7.04019V8.00019H1.97348V7.04019H5.01348ZM1.97348 5.01352V6.02686H5.01348V5.01352H1.97348ZM12.0001 3.04019V4.00019H9.01348V3.04019H12.0001ZM9.01348 5.01352V6.02686H12.0001V5.01352H9.01348ZM9.01348 7.04019V8.00019H12.0001V7.04019H9.01348Z" fill="#0250D9"/>
3
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M5 5L6.25 3.75H13.0175L17.5 8.2325V17.5L16.25 18.75H6.25L5 17.5V5ZM16.25 8.75L12.5 5H6.25V17.5H16.25V8.75Z" fill="#2E2E2E"/>
3
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 1.25L2.5 2.5V15L3.75 16.25V2.5H11.7675L10.5175 1.25H3.75Z" fill="#2E2E2E"/>
4
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M13.4935 0.000149727L13.9735 0.480149C13.9735 2.15126 13.5646 3.80459 12.7468 5.44015C12.0713 6.7557 11.1468 8.05348 9.97348 9.33348V13.4935L9.49348 13.9735H6.50682L6.13348 13.8668L0.16015 7.84015L0.000149727 7.46682V4.48015L0.48015 4.00015H4.64015C5.92015 2.82682 7.21793 1.90237 8.53348 1.22682C10.169 0.409039 11.8224 0.000149727 13.4935 0.000149727ZM1.01348 4.96015V7.30682L1.38682 7.68015C2.06237 6.72015 2.80904 5.81348 3.62682 4.96015H1.01348ZM6.66682 12.9601H8.96015V10.3468C8.14237 11.1646 7.25348 11.9113 6.29348 12.5868L6.66682 12.9601ZM5.54682 11.8401C6.40015 11.2713 7.23571 10.6135 8.05348 9.86682C9.33348 8.62237 10.3824 7.36015 11.2002 6.08015C12.2668 4.37348 12.8535 2.68459 12.9601 1.01348C11.289 1.12015 9.60015 1.70682 7.89348 2.77348C6.61348 3.59126 5.35126 4.64015 4.10682 5.92015C3.36015 6.73793 2.70237 7.57348 2.13348 8.42682L5.54682 11.8401ZM2.98682 13.9735H0.000149727V10.9868H1.01348V12.9601H2.98682V13.9735ZM9.76015 6.29348C9.51126 6.64904 9.17348 6.88015 8.74682 6.98682C8.35571 7.05793 7.98237 6.98682 7.62682 6.77348C7.30682 6.52459 7.09348 6.20459 6.98682 5.81348C6.91571 5.38682 6.98682 5.01348 7.20015 4.69348C7.44904 4.33793 7.76904 4.12459 8.16015 4.05348C8.58682 3.94682 8.96015 4.01793 9.28015 4.26682C9.63571 4.48015 9.84904 4.80015 9.92015 5.22682C10.0268 5.61793 9.97348 5.97348 9.76015 6.29348Z" fill="#066AFE"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="14" height="13" viewBox="0 0 14 13" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M5.70684 0.533125C5.8135 0.319791 5.9735 0.177569 6.18684 0.106458C6.40017 0.0353467 6.6135 0.0353467 6.82684 0.106458C7.04017 0.177569 7.20017 0.319791 7.30684 0.533125L8.8535 3.62646L12.2135 4.10646C12.4624 4.14201 12.6579 4.26646 12.8002 4.47979C12.9424 4.65757 13.0135 4.8709 13.0135 5.11979C13.0135 5.33312 12.9246 5.5109 12.7468 5.65312L10.2935 8.05312L10.8802 11.4665C10.9157 11.6798 10.8624 11.8931 10.7202 12.1065C10.5779 12.2842 10.4002 12.4087 10.1868 12.4798C9.9735 12.5509 9.76017 12.5331 9.54684 12.4265L6.50684 10.8265L3.46684 12.4265C3.2535 12.5331 3.04017 12.5509 2.82684 12.4798C2.6135 12.4087 2.43572 12.2842 2.2935 12.1065C2.15128 11.8931 2.09795 11.6798 2.1335 11.4665L2.72017 8.05312L0.266836 5.65312C0.0890582 5.5109 0.000169277 5.33312 0.000169277 5.11979C0.000169277 4.8709 0.0712804 4.65757 0.213503 4.47979C0.355725 4.26646 0.55128 4.14201 0.800169 4.10646L4.16017 3.62646L5.70684 0.533125ZM6.50684 1.17312L5.06684 4.10646C4.92461 4.3909 4.6935 4.56868 4.3735 4.63979L1.12017 5.06646L3.46684 7.35979C3.71572 7.60868 3.80461 7.87535 3.7335 8.15979L3.20017 11.4131L6.08017 9.86646C6.36461 9.72424 6.64906 9.72424 6.9335 9.86646L9.8135 11.4131L9.28017 8.15979C9.24461 7.87535 9.3335 7.60868 9.54684 7.35979L11.8935 5.06646L8.64017 4.63979C8.32017 4.56868 8.08906 4.3909 7.94684 4.10646L6.50684 1.17312Z" fill="#066AFE"/>
3
+ </svg>