@salesforce/webapp-template-feature-graphql-experimental 1.35.2 → 1.36.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 (95) hide show
  1. package/README.md +52 -65
  2. package/package.json +6 -43
  3. package/rules/graphql-data-access-rule.md +5 -5
  4. package/skills/graphql-data-access/SKILL.md +17 -40
  5. package/skills/graphql-data-access/docs/explore-schema.md +3 -3
  6. package/dist/.a4drules/build-validation.md +0 -81
  7. package/dist/.a4drules/code-quality.md +0 -150
  8. package/dist/.a4drules/graphql/tools/knowledge/lds-explore-graphql-schema.md +0 -227
  9. package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md +0 -212
  10. package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-readquery.md +0 -185
  11. package/dist/.a4drules/graphql/tools/knowledge/lds-guide-graphql.md +0 -205
  12. package/dist/.a4drules/graphql/tools/schemas/shared.graphqls +0 -1150
  13. package/dist/.a4drules/graphql.md +0 -408
  14. package/dist/.a4drules/images.md +0 -13
  15. package/dist/.a4drules/react.md +0 -361
  16. package/dist/.a4drules/react_image_processing.md +0 -45
  17. package/dist/.a4drules/skills/install-feature/SKILL.md +0 -67
  18. package/dist/.a4drules/skills/install-feature/scripts/copy-feature-assets.sh +0 -36
  19. package/dist/.a4drules/typescript.md +0 -224
  20. package/dist/.forceignore +0 -15
  21. package/dist/.husky/pre-commit +0 -4
  22. package/dist/.prettierignore +0 -11
  23. package/dist/.prettierrc +0 -17
  24. package/dist/CHANGELOG.md +0 -498
  25. package/dist/README.md +0 -18
  26. package/dist/config/project-scratch-def.json +0 -13
  27. package/dist/force-app/main/default/webapplications/feature-graphql/.graphqlrc.yml +0 -6
  28. package/dist/force-app/main/default/webapplications/feature-graphql/.prettierignore +0 -9
  29. package/dist/force-app/main/default/webapplications/feature-graphql/.prettierrc +0 -11
  30. package/dist/force-app/main/default/webapplications/feature-graphql/build/vite.config.d.ts +0 -2
  31. package/dist/force-app/main/default/webapplications/feature-graphql/build/vite.config.js +0 -76
  32. package/dist/force-app/main/default/webapplications/feature-graphql/e2e/app.spec.ts +0 -24
  33. package/dist/force-app/main/default/webapplications/feature-graphql/eslint.config.js +0 -113
  34. package/dist/force-app/main/default/webapplications/feature-graphql/feature-graphql.webapplication-meta.xml +0 -7
  35. package/dist/force-app/main/default/webapplications/feature-graphql/index.html +0 -13
  36. package/dist/force-app/main/default/webapplications/feature-graphql/package-lock.json +0 -11134
  37. package/dist/force-app/main/default/webapplications/feature-graphql/package.json +0 -52
  38. package/dist/force-app/main/default/webapplications/feature-graphql/playwright.config.ts +0 -24
  39. package/dist/force-app/main/default/webapplications/feature-graphql/scripts/rewrite-e2e-assets.mjs +0 -23
  40. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/graphql-operations-types.ts +0 -118
  41. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/graphql.test.ts +0 -91
  42. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/graphql.ts +0 -35
  43. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/utils/accounts.ts +0 -35
  44. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/utils/query/highRevenueAccountsQuery.graphql +0 -29
  45. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/utils/user.test.ts +0 -129
  46. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/utils/user.ts +0 -53
  47. package/dist/force-app/main/default/webapplications/feature-graphql/src/app.tsx +0 -22
  48. package/dist/force-app/main/default/webapplications/feature-graphql/src/appLayout.tsx +0 -11
  49. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/icons/book.svg +0 -3
  50. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/icons/copy.svg +0 -4
  51. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/icons/rocket.svg +0 -3
  52. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/icons/star.svg +0 -3
  53. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/images/codey-1.png +0 -0
  54. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/images/codey-2.png +0 -0
  55. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/images/codey-3.png +0 -0
  56. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/images/vibe-codey.svg +0 -194
  57. package/dist/force-app/main/default/webapplications/feature-graphql/src/components/AccountsTable.tsx +0 -121
  58. package/dist/force-app/main/default/webapplications/feature-graphql/src/index.ts +0 -7
  59. package/dist/force-app/main/default/webapplications/feature-graphql/src/navigationMenu.tsx +0 -81
  60. package/dist/force-app/main/default/webapplications/feature-graphql/src/pages/About.tsx +0 -12
  61. package/dist/force-app/main/default/webapplications/feature-graphql/src/pages/AccountsPage.tsx +0 -19
  62. package/dist/force-app/main/default/webapplications/feature-graphql/src/pages/Home.tsx +0 -12
  63. package/dist/force-app/main/default/webapplications/feature-graphql/src/pages/NotFound.tsx +0 -18
  64. package/dist/force-app/main/default/webapplications/feature-graphql/src/pages/new.tsx +0 -10
  65. package/dist/force-app/main/default/webapplications/feature-graphql/src/router-utils.tsx +0 -34
  66. package/dist/force-app/main/default/webapplications/feature-graphql/src/routes.tsx +0 -48
  67. package/dist/force-app/main/default/webapplications/feature-graphql/src/styles/global.css +0 -13
  68. package/dist/force-app/main/default/webapplications/feature-graphql/tsconfig.json +0 -36
  69. package/dist/force-app/main/default/webapplications/feature-graphql/tsconfig.node.json +0 -13
  70. package/dist/force-app/main/default/webapplications/feature-graphql/vite-env.d.ts +0 -1
  71. package/dist/force-app/main/default/webapplications/feature-graphql/vite.config.ts +0 -69
  72. package/dist/force-app/main/default/webapplications/feature-graphql/vitest-env.d.ts +0 -2
  73. package/dist/force-app/main/default/webapplications/feature-graphql/vitest.config.ts +0 -11
  74. package/dist/force-app/main/default/webapplications/feature-graphql/vitest.setup.ts +0 -1
  75. package/dist/force-app/main/default/webapplications/feature-graphql/webapplication.json +0 -7
  76. package/dist/jest.config.js +0 -6
  77. package/dist/package.json +0 -38
  78. package/dist/scripts/apex/hello.apex +0 -10
  79. package/dist/scripts/soql/account.soql +0 -6
  80. package/dist/sfdx-project.json +0 -12
  81. package/skills/graphql-data-access/scripts/codegen.js +0 -15
  82. package/skills/graphql-data-access/scripts/get-graphql-schema.mjs +0 -59
  83. package/src/force-app/main/default/webapplications/feature-graphql/.graphqlrc.yml +0 -6
  84. package/src/force-app/main/default/webapplications/feature-graphql/src/api/graphql-operations-types.ts +0 -118
  85. package/src/force-app/main/default/webapplications/feature-graphql/src/api/graphql.test.ts +0 -91
  86. package/src/force-app/main/default/webapplications/feature-graphql/src/api/graphql.ts +0 -35
  87. package/src/force-app/main/default/webapplications/feature-graphql/src/api/utils/accounts.ts +0 -35
  88. package/src/force-app/main/default/webapplications/feature-graphql/src/api/utils/query/highRevenueAccountsQuery.graphql +0 -29
  89. package/src/force-app/main/default/webapplications/feature-graphql/src/api/utils/user.test.ts +0 -129
  90. package/src/force-app/main/default/webapplications/feature-graphql/src/api/utils/user.ts +0 -53
  91. package/src/force-app/main/default/webapplications/feature-graphql/src/components/AccountsTable.tsx +0 -121
  92. package/src/force-app/main/default/webapplications/feature-graphql/src/index.ts +0 -7
  93. package/src/force-app/main/default/webapplications/feature-graphql/src/pages/AccountsPage.tsx +0 -19
  94. package/src/force-app/main/default/webapplications/feature-graphql/src/routes.tsx +0 -20
  95. package/src/force-app/main/default/webapplications/feature-graphql/vite.config.ts +0 -69
@@ -1,52 +0,0 @@
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
- },
14
- "dependencies": {
15
- "@salesforce/sdk-data": "^1.35.1",
16
- "@salesforce/webapp-experimental": "*",
17
- "@tailwindcss/vite": "^4.1.17",
18
- "react": "^19.2.0",
19
- "react-dom": "^19.2.0",
20
- "react-router": "^7.10.1",
21
- "tailwindcss": "^4.1.17"
22
- },
23
- "devDependencies": {
24
- "@eslint/js": "^9.39.1",
25
- "@graphql-codegen/cli": "^6.1.1",
26
- "@graphql-codegen/typescript": "^5.0.8",
27
- "@graphql-codegen/typescript-operations": "^5.0.8",
28
- "@playwright/test": "^1.49.0",
29
- "@salesforce/vite-plugin-webapp-experimental": "*",
30
- "@testing-library/jest-dom": "^6.6.3",
31
- "@testing-library/react": "^16.1.0",
32
- "@testing-library/user-event": "^14.5.2",
33
- "@types/node": "^24.10.1",
34
- "@types/react": "^19.2.5",
35
- "@types/react-dom": "^19.2.3",
36
- "@vitejs/plugin-react": "^5.1.1",
37
- "@vitest/ui": "^4.0.17",
38
- "eslint": "^9.39.1",
39
- "eslint-plugin-react-hooks": "^7.0.1",
40
- "eslint-plugin-react-refresh": "^0.4.24",
41
- "globals": "^16.5.0",
42
- "graphql": "^16.12.0",
43
- "graphql-codegen-typescript-operation-types": "^2.0.2",
44
- "jsdom": "^25.0.1",
45
- "serve": "^14.2.5",
46
- "typescript": "~5.9.3",
47
- "typescript-eslint": "^8.46.4",
48
- "vite": "^7.2.4",
49
- "vite-plugin-graphql-codegen": "^3.8.0",
50
- "vitest": "^4.0.17"
51
- }
52
- }
@@ -1,24 +0,0 @@
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
- });
@@ -1,23 +0,0 @@
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
- );
@@ -1,118 +0,0 @@
1
- export type Maybe<T> = T | null;
2
- export type InputMaybe<T> = Maybe<T>;
3
- export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
4
- export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
5
- export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
6
- export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = {
7
- [_ in K]?: never;
8
- };
9
- export type Incremental<T> =
10
- | T
11
- | { [P in keyof T]?: P extends " $fragmentName" | "__typename" ? T[P] : never };
12
- /** All built-in and custom scalars, mapped to their actual values */
13
- export type Scalars = {
14
- ID: { input: string; output: string };
15
- String: { input: string; output: string };
16
- Boolean: { input: boolean; output: boolean };
17
- Int: { input: number; output: number };
18
- Float: { input: number; output: number };
19
- Base64: { input: any; output: any };
20
- /** An arbitrary precision signed decimal */
21
- BigDecimal: { input: any; output: any };
22
- /** An arbitrary precision signed integer */
23
- BigInteger: { input: any; output: any };
24
- /** An 8-bit signed integer */
25
- Byte: { input: any; output: any };
26
- /** A UTF-16 code unit; a character on Unicode's BMP */
27
- Char: { input: any; output: any };
28
- Currency: { input: any; output: any };
29
- Date: { input: any; output: any };
30
- DateTime: { input: any; output: any };
31
- Double: { input: any; output: any };
32
- Email: { input: any; output: any };
33
- EncryptedString: { input: any; output: any };
34
- /** Can be set to an ID or a Reference to the result of another mutation operation. */
35
- IdOrRef: { input: any; output: any };
36
- JSON: { input: any; output: any };
37
- Latitude: { input: any; output: any };
38
- /** A 64-bit signed integer */
39
- Long: { input: any; output: any };
40
- LongTextArea: { input: any; output: any };
41
- Longitude: { input: any; output: any };
42
- MultiPicklist: { input: any; output: any };
43
- Percent: { input: any; output: any };
44
- PhoneNumber: { input: any; output: any };
45
- Picklist: { input: any; output: any };
46
- RichTextArea: { input: any; output: any };
47
- /** A 16-bit signed integer */
48
- Short: { input: any; output: any };
49
- TextArea: { input: any; output: any };
50
- Time: { input: any; output: any };
51
- Url: { input: any; output: any };
52
- };
53
-
54
- export enum DataType {
55
- Address = "ADDRESS",
56
- Anytype = "ANYTYPE",
57
- Base64 = "BASE64",
58
- Boolean = "BOOLEAN",
59
- Combobox = "COMBOBOX",
60
- Complexvalue = "COMPLEXVALUE",
61
- Currency = "CURRENCY",
62
- Date = "DATE",
63
- Datetime = "DATETIME",
64
- Double = "DOUBLE",
65
- Email = "EMAIL",
66
- Encryptedstring = "ENCRYPTEDSTRING",
67
- Int = "INT",
68
- Json = "JSON",
69
- Junctionidlist = "JUNCTIONIDLIST",
70
- Location = "LOCATION",
71
- Long = "LONG",
72
- Multipicklist = "MULTIPICKLIST",
73
- Percent = "PERCENT",
74
- Phone = "PHONE",
75
- Picklist = "PICKLIST",
76
- Reference = "REFERENCE",
77
- String = "STRING",
78
- Textarea = "TEXTAREA",
79
- Time = "TIME",
80
- Url = "URL",
81
- }
82
-
83
- export enum FieldExtraTypeInfo {
84
- ExternalLookup = "EXTERNAL_LOOKUP",
85
- ImageUrl = "IMAGE_URL",
86
- IndirectLookup = "INDIRECT_LOOKUP",
87
- Personname = "PERSONNAME",
88
- Plaintextarea = "PLAINTEXTAREA",
89
- Richtextarea = "RICHTEXTAREA",
90
- SwitchablePersonname = "SWITCHABLE_PERSONNAME",
91
- }
92
-
93
- export enum ResultOrder {
94
- Asc = "ASC",
95
- Desc = "DESC",
96
- }
97
-
98
- export type GetHighRevenueAccountsQueryVariables = Exact<{
99
- minRevenue?: InputMaybe<Scalars["Currency"]["input"]>;
100
- }>;
101
-
102
- export type GetHighRevenueAccountsQuery = {
103
- uiapi: {
104
- query: {
105
- Account?: {
106
- edges?: Array<{
107
- node?: {
108
- Id: string;
109
- Name?: { value?: string | null } | null;
110
- AnnualRevenue?: { value?: any | null } | null;
111
- Industry?: { value?: any | null } | null;
112
- Website?: { value?: any | null } | null;
113
- } | null;
114
- } | null> | null;
115
- } | null;
116
- };
117
- };
118
- };
@@ -1,91 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { executeGraphQL } from "./graphql";
3
- import { sdk } from "@salesforce/sdk-data";
4
-
5
- vi.mock("@salesforce/sdk-data", () => ({
6
- sdk: { graphql: vi.fn() },
7
- }));
8
-
9
- describe("executeGraphQL", () => {
10
- beforeEach(() => {
11
- vi.clearAllMocks();
12
- });
13
-
14
- it("should successfully execute a GraphQL query", async () => {
15
- const mockData = {
16
- uiapi: {
17
- query: {
18
- Account: {
19
- edges: [
20
- {
21
- node: {
22
- Id: "001",
23
- Name: { value: "Test Account" },
24
- },
25
- },
26
- ],
27
- },
28
- },
29
- },
30
- };
31
-
32
- vi.mocked(sdk.graphql!).mockResolvedValue({ data: mockData });
33
-
34
- const query = "query { uiapi { query { Account { edges { node { Id Name { value } } } } } } }";
35
- const result = await executeGraphQL(query);
36
-
37
- expect(sdk.graphql).toHaveBeenCalledWith(query, undefined);
38
- expect(result).toEqual(mockData);
39
- });
40
-
41
- it("should handle GraphQL errors", async () => {
42
- const mockErrors = [{ message: "Field not found" }, { message: "Invalid query" }];
43
-
44
- vi.mocked(sdk.graphql!).mockResolvedValue({ errors: mockErrors } as any);
45
-
46
- const query = "query { invalid }";
47
-
48
- await expect(executeGraphQL(query)).rejects.toThrow(
49
- "GraphQL Error: Field not found; Invalid query",
50
- );
51
- });
52
-
53
- it("should handle network errors", async () => {
54
- vi.mocked(sdk.graphql!).mockRejectedValue({
55
- status: 500,
56
- message: "Network error",
57
- });
58
-
59
- const query = "query { test }";
60
-
61
- await expect(executeGraphQL(query)).rejects.toThrow("Network error");
62
- });
63
-
64
- it("should throw when data is undefined", async () => {
65
- vi.mocked(sdk.graphql!).mockResolvedValue({} as any);
66
-
67
- const query = "query { test }";
68
-
69
- await expect(executeGraphQL(query)).rejects.toThrow("GraphQL Error: No data returned");
70
- });
71
-
72
- it("should throw when data is null", async () => {
73
- vi.mocked(sdk.graphql!).mockResolvedValue({ data: null } as any);
74
-
75
- const query = "query { test }";
76
-
77
- await expect(executeGraphQL(query)).rejects.toThrow("GraphQL Error: No data returned");
78
- });
79
-
80
- it("should pass variables to the GraphQL query", async () => {
81
- const mockData = { test: "data" };
82
- vi.mocked(sdk.graphql!).mockResolvedValue({ data: mockData });
83
-
84
- const query = "query GetAccount($id: ID!) { account(id: $id) { name } }";
85
- const variables = { id: "001" };
86
-
87
- await executeGraphQL(query, variables);
88
-
89
- expect(sdk.graphql).toHaveBeenCalledWith(query, variables);
90
- });
91
- });
@@ -1,35 +0,0 @@
1
- import { sdk } from "@salesforce/sdk-data";
2
-
3
- export type NodeOfConnection<T> = T extends {
4
- edges?: (infer E)[] | null;
5
- } | null
6
- ? E extends { node?: infer N } | null
7
- ? N
8
- : never
9
- : never;
10
-
11
- /**
12
- * Execute a GraphQL query against the Salesforce GraphQL API
13
- *
14
- * @param query - The GraphQL query string
15
- * @param variables - Optional variables for the query
16
- * @returns The GraphQL response data
17
- */
18
- export async function executeGraphQL<T, InputVariables = Record<string, unknown>>(
19
- query: string,
20
- variables?: InputVariables,
21
- ): Promise<T> {
22
- const result = await sdk.graphql!<T, InputVariables>(query, variables);
23
-
24
- if (result.errors?.length) {
25
- const errorMessages = result.errors.map((e) => e.message).join("; ");
26
- throw new Error(`GraphQL Error: ${errorMessages}`);
27
- }
28
-
29
- if (!result.data) {
30
- throw new Error("GraphQL Error: No data returned");
31
- }
32
-
33
- return result.data;
34
- }
35
- export { gql } from "@salesforce/sdk-data";
@@ -1,35 +0,0 @@
1
- /**
2
- * @attention agents
3
- * This file serves as a reference example for accessing Salesforce data via GraphQL.
4
- * Use this pattern when implementing new data fetching utilities:
5
- * 1. Define TypeScript interfaces for the response shape
6
- * 2. Write the GraphQL query using UIAPI syntax
7
- * 3. Use executeGraphQL<T>() with proper typing
8
- * 4. Extract and return the relevant data from the response
9
- */
10
-
11
- import { executeGraphQL, type NodeOfConnection } from "../graphql";
12
- import HIGH_REVENUE_ACCOUNTS_QUERY from "./query/highRevenueAccountsQuery.graphql?raw";
13
- import type {
14
- GetHighRevenueAccountsQuery,
15
- GetHighRevenueAccountsQueryVariables,
16
- } from "../graphql-operations-types";
17
-
18
- type AccountNode = NodeOfConnection<GetHighRevenueAccountsQuery["uiapi"]["query"]["Account"]>;
19
-
20
- /**
21
- * Fetch accounts with annual revenue greater than the specified amount
22
- *
23
- * @param minRevenue - Minimum annual revenue threshold (default: 100000)
24
- * @returns Array of accounts matching the criteria
25
- */
26
- export async function getHighRevenueAccounts(
27
- variables: GetHighRevenueAccountsQueryVariables,
28
- ): Promise<AccountNode[]> {
29
- const response = await executeGraphQL<GetHighRevenueAccountsQuery>(
30
- HIGH_REVENUE_ACCOUNTS_QUERY,
31
- variables,
32
- );
33
-
34
- return response.uiapi?.query?.Account?.edges?.map((edge) => edge?.node) || [];
35
- }
@@ -1,29 +0,0 @@
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
- }
@@ -1,129 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { getCurrentUser } from "./user";
3
- import { createDataSDK } from "@salesforce/sdk-data";
4
-
5
- vi.mock("@salesforce/sdk-data", () => ({
6
- createDataSDK: vi.fn(),
7
- }));
8
-
9
- const mockFetch = vi.fn();
10
- const mockSDK = { fetch: mockFetch };
11
-
12
- // Mock console.error to avoid noise in test output
13
- const mockConsoleError = vi.spyOn(console, "error").mockImplementation(() => {});
14
-
15
- describe("User API Utils", () => {
16
- beforeEach(() => {
17
- vi.clearAllMocks();
18
- vi.mocked(createDataSDK).mockResolvedValue(mockSDK);
19
- });
20
-
21
- afterEach(() => {
22
- mockConsoleError.mockClear();
23
- });
24
-
25
- describe("getCurrentUser", () => {
26
- const mockChatterUser = {
27
- id: "005000000000001",
28
- name: "John Doe",
29
- email: "john@example.com",
30
- username: "john@example.com",
31
- };
32
-
33
- it("successfully fetches current user", async () => {
34
- mockFetch.mockResolvedValue({
35
- ok: true,
36
- json: () => Promise.resolve(mockChatterUser),
37
- });
38
-
39
- const result = await getCurrentUser();
40
-
41
- expect(mockFetch).toHaveBeenCalledWith("/services/data/v65.0/chatter/users/me");
42
- expect(result).toEqual({
43
- id: "005000000000001",
44
- name: "John Doe",
45
- });
46
- });
47
-
48
- it('falls back to "User" when name is empty', async () => {
49
- mockFetch.mockResolvedValue({
50
- ok: true,
51
- json: () =>
52
- Promise.resolve({
53
- id: "005000000000003",
54
- name: "",
55
- }),
56
- });
57
-
58
- const result = await getCurrentUser();
59
-
60
- expect(result).toEqual({
61
- id: "005000000000003",
62
- name: "User",
63
- });
64
- });
65
-
66
- it('falls back to "User" when name is null', async () => {
67
- mockFetch.mockResolvedValue({
68
- ok: true,
69
- json: () =>
70
- Promise.resolve({
71
- id: "005000000000004",
72
- name: null,
73
- }),
74
- });
75
-
76
- const result = await getCurrentUser();
77
-
78
- expect(result).toEqual({
79
- id: "005000000000004",
80
- name: "User",
81
- });
82
- });
83
-
84
- it('falls back to "User" when name is undefined', async () => {
85
- mockFetch.mockResolvedValue({
86
- ok: true,
87
- json: () =>
88
- Promise.resolve({
89
- id: "005000000000005",
90
- }),
91
- });
92
-
93
- const result = await getCurrentUser();
94
-
95
- expect(result).toEqual({
96
- id: "005000000000005",
97
- name: "User",
98
- });
99
- });
100
-
101
- it("throws error when request fails", async () => {
102
- mockFetch.mockResolvedValue({
103
- ok: false,
104
- status: 500,
105
- });
106
-
107
- await expect(getCurrentUser()).rejects.toThrow("HTTP 500");
108
- expect(mockConsoleError).toHaveBeenCalled();
109
- });
110
-
111
- it("throws error when no user data is found", async () => {
112
- mockFetch.mockResolvedValue({
113
- ok: true,
114
- json: () => Promise.resolve({}),
115
- });
116
-
117
- await expect(getCurrentUser()).rejects.toThrow("No user data found in Chatter API response");
118
- expect(mockConsoleError).toHaveBeenCalled();
119
- });
120
-
121
- it("throws error when fetch rejects", async () => {
122
- const apiError = new Error("Network error");
123
- mockFetch.mockRejectedValue(apiError);
124
-
125
- await expect(getCurrentUser()).rejects.toThrow("Network error");
126
- expect(mockConsoleError).toHaveBeenCalledWith("Error fetching user data:", apiError);
127
- });
128
- });
129
- });
@@ -1,53 +0,0 @@
1
- import { createDataSDK, type DataSDK } from "@salesforce/sdk-data";
2
-
3
- interface User {
4
- id: string;
5
- name: string;
6
- }
7
-
8
- interface ChatterUser {
9
- id: string;
10
- name: string;
11
- email?: string;
12
- username?: string;
13
- }
14
-
15
- // Lazy-initialized SDK instance
16
- let sdkInstance: DataSDK | null = null;
17
-
18
- async function getSDK(): Promise<DataSDK> {
19
- if (!sdkInstance) {
20
- sdkInstance = await createDataSDK();
21
- }
22
- return sdkInstance;
23
- }
24
-
25
- /**
26
- * Fetch current user information from Salesforce
27
- * Uses Chatter API to get current user details
28
- * @returns User info or null if no session
29
- */
30
- export async function getCurrentUser(): Promise<User | null> {
31
- try {
32
- const sdk = await getSDK();
33
- const response = await sdk.fetch!("/services/data/v65.0/chatter/users/me");
34
-
35
- if (!response.ok) {
36
- throw new Error(`HTTP ${response.status}`);
37
- }
38
-
39
- const userData: ChatterUser = await response.json();
40
-
41
- if (!userData || !userData.id) {
42
- throw new Error("No user data found in Chatter API response");
43
- }
44
-
45
- return {
46
- id: userData.id,
47
- name: userData.name || "User",
48
- };
49
- } catch (error) {
50
- console.error("Error fetching user data:", error);
51
- throw error;
52
- }
53
- }
@@ -1,22 +0,0 @@
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
- );
@@ -1,11 +0,0 @@
1
- import { Outlet } from "react-router";
2
- import NavigationMenu from "./navigationMenu";
3
-
4
- export default function AppLayout() {
5
- return (
6
- <>
7
- <NavigationMenu />
8
- <Outlet />
9
- </>
10
- );
11
- }
@@ -1,3 +0,0 @@
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>
@@ -1,4 +0,0 @@
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>
@@ -1,3 +0,0 @@
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>
@@ -1,3 +0,0 @@
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>