@salesforce/webapp-template-app-react-sample-b2x-experimental 1.36.3

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 (96) hide show
  1. package/LICENSE.txt +82 -0
  2. package/dist/.a4drules/build-validation.md +81 -0
  3. package/dist/.a4drules/code-quality.md +150 -0
  4. package/dist/.a4drules/graphql/tools/knowledge/lds-explore-graphql-schema.md +227 -0
  5. package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md +212 -0
  6. package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-readquery.md +185 -0
  7. package/dist/.a4drules/graphql/tools/knowledge/lds-guide-graphql.md +205 -0
  8. package/dist/.a4drules/graphql/tools/schemas/shared.graphqls +1150 -0
  9. package/dist/.a4drules/graphql.md +408 -0
  10. package/dist/.a4drules/images.md +13 -0
  11. package/dist/.a4drules/react.md +361 -0
  12. package/dist/.a4drules/react_image_processing.md +45 -0
  13. package/dist/.a4drules/skills/install-feature/SKILL.md +67 -0
  14. package/dist/.a4drules/skills/install-feature/scripts/copy-feature-assets.sh +36 -0
  15. package/dist/.a4drules/typescript.md +224 -0
  16. package/dist/.forceignore +15 -0
  17. package/dist/.husky/pre-commit +4 -0
  18. package/dist/.prettierignore +11 -0
  19. package/dist/.prettierrc +17 -0
  20. package/dist/CHANGELOG.md +533 -0
  21. package/dist/README.md +18 -0
  22. package/dist/config/project-scratch-def.json +13 -0
  23. package/dist/force-app/main/default/digitalExperienceConfigs/appreactsampleb2x1.digitalExperienceConfig +8 -0
  24. package/dist/force-app/main/default/digitalExperiences/site/appreactsampleb2x1/appreactsampleb2x1.digitalExperience-meta.xml +11 -0
  25. package/dist/force-app/main/default/digitalExperiences/site/appreactsampleb2x1/sfdc_cms__site/appreactsampleb2x1/_meta.json +5 -0
  26. package/dist/force-app/main/default/digitalExperiences/site/appreactsampleb2x1/sfdc_cms__site/appreactsampleb2x1/content.json +10 -0
  27. package/dist/force-app/main/default/networks/appreactsampleb2x.network +60 -0
  28. package/dist/force-app/main/default/package.xml +20 -0
  29. package/dist/force-app/main/default/sites/appreactsampleb2x.site +31 -0
  30. package/dist/force-app/main/default/webapplications/appreactsampleb2x/.graphqlrc.yml +2 -0
  31. package/dist/force-app/main/default/webapplications/appreactsampleb2x/.prettierignore +9 -0
  32. package/dist/force-app/main/default/webapplications/appreactsampleb2x/.prettierrc +11 -0
  33. package/dist/force-app/main/default/webapplications/appreactsampleb2x/appreactsampleb2x.webapplication-meta.xml +7 -0
  34. package/dist/force-app/main/default/webapplications/appreactsampleb2x/build/vite.config.d.ts +2 -0
  35. package/dist/force-app/main/default/webapplications/appreactsampleb2x/build/vite.config.js +94 -0
  36. package/dist/force-app/main/default/webapplications/appreactsampleb2x/codegen.yml +12 -0
  37. package/dist/force-app/main/default/webapplications/appreactsampleb2x/e2e/app.spec.ts +24 -0
  38. package/dist/force-app/main/default/webapplications/appreactsampleb2x/eslint.config.js +141 -0
  39. package/dist/force-app/main/default/webapplications/appreactsampleb2x/index.html +13 -0
  40. package/dist/force-app/main/default/webapplications/appreactsampleb2x/package-lock.json +12805 -0
  41. package/dist/force-app/main/default/webapplications/appreactsampleb2x/package.json +57 -0
  42. package/dist/force-app/main/default/webapplications/appreactsampleb2x/playwright.config.ts +24 -0
  43. package/dist/force-app/main/default/webapplications/appreactsampleb2x/scripts/get-graphql-schema.mjs +68 -0
  44. package/dist/force-app/main/default/webapplications/appreactsampleb2x/scripts/rewrite-e2e-assets.mjs +23 -0
  45. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/graphql-operations-types.ts +129 -0
  46. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/utils/accounts.ts +33 -0
  47. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/utils/query/highRevenueAccountsQuery.graphql +29 -0
  48. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/app.tsx +41 -0
  49. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/appLayout.tsx +161 -0
  50. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/icons/book.svg +3 -0
  51. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/icons/copy.svg +4 -0
  52. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/icons/rocket.svg +3 -0
  53. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/icons/star.svg +3 -0
  54. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/images/codey-1.png +0 -0
  55. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/images/codey-2.png +0 -0
  56. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/images/codey-3.png +0 -0
  57. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/images/vibe-codey.svg +194 -0
  58. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/alert.tsx +65 -0
  59. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/button.tsx +54 -0
  60. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/card.tsx +77 -0
  61. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/field.tsx +111 -0
  62. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/index.ts +71 -0
  63. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/input.tsx +19 -0
  64. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/label.tsx +19 -0
  65. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/pagination.tsx +99 -0
  66. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/select.tsx +151 -0
  67. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/skeleton.tsx +7 -0
  68. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/spinner.tsx +26 -0
  69. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/table.tsx +114 -0
  70. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/ui/tabs.tsx +115 -0
  71. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/lib/utils.ts +6 -0
  72. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/About.tsx +8 -0
  73. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Application.tsx +101 -0
  74. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Dashboard.tsx +101 -0
  75. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/HelpCenter.tsx +29 -0
  76. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Home.tsx +30 -0
  77. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Maintenance.tsx +132 -0
  78. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/NotFound.tsx +14 -0
  79. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyDetails.tsx +68 -0
  80. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyListings.tsx +84 -0
  81. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/routes.tsx +62 -0
  82. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/styles/global.css +80 -0
  83. package/dist/force-app/main/default/webapplications/appreactsampleb2x/tsconfig.json +36 -0
  84. package/dist/force-app/main/default/webapplications/appreactsampleb2x/tsconfig.node.json +13 -0
  85. package/dist/force-app/main/default/webapplications/appreactsampleb2x/vite-env.d.ts +1 -0
  86. package/dist/force-app/main/default/webapplications/appreactsampleb2x/vite.config.ts +102 -0
  87. package/dist/force-app/main/default/webapplications/appreactsampleb2x/vitest-env.d.ts +2 -0
  88. package/dist/force-app/main/default/webapplications/appreactsampleb2x/vitest.config.ts +11 -0
  89. package/dist/force-app/main/default/webapplications/appreactsampleb2x/vitest.setup.ts +1 -0
  90. package/dist/force-app/main/default/webapplications/appreactsampleb2x/webapplication.json +7 -0
  91. package/dist/jest.config.js +6 -0
  92. package/dist/package.json +38 -0
  93. package/dist/scripts/apex/hello.apex +10 -0
  94. package/dist/scripts/soql/account.soql +6 -0
  95. package/dist/sfdx-project.json +12 -0
  96. package/package.json +28 -0
@@ -0,0 +1,57 @@
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
+ "react": "^19.2.0",
21
+ "react-dom": "^19.2.0",
22
+ "react-router": "^7.10.1",
23
+ "tailwindcss": "^4.1.17"
24
+ },
25
+ "devDependencies": {
26
+ "@eslint/js": "^9.39.1",
27
+ "@graphql-codegen/cli": "^6.1.0",
28
+ "@graphql-codegen/typescript": "^5.0.6",
29
+ "@graphql-codegen/typescript-operations": "^5.0.6",
30
+ "@graphql-eslint/eslint-plugin": "^4.1.0",
31
+ "@graphql-tools/utils": "^11.0.0",
32
+ "@playwright/test": "^1.49.0",
33
+ "@salesforce/vite-plugin-webapp-experimental": "*",
34
+ "@testing-library/jest-dom": "^6.6.3",
35
+ "@testing-library/react": "^16.1.0",
36
+ "@testing-library/user-event": "^14.5.2",
37
+ "@types/node": "^24.10.1",
38
+ "@types/react": "^19.2.5",
39
+ "@types/react-dom": "^19.2.3",
40
+ "@vitejs/plugin-react": "^5.1.1",
41
+ "@vitest/ui": "^4.0.17",
42
+ "eslint": "^9.39.1",
43
+ "eslint-plugin-react": "^7.37.2",
44
+ "eslint-plugin-react-hooks": "^7.0.1",
45
+ "eslint-plugin-react-refresh": "^0.4.24",
46
+ "globals": "^16.5.0",
47
+ "graphql": "^16.11.0",
48
+ "graphql-codegen-typescript-operation-types": "^2.0.2",
49
+ "jsdom": "^25.0.1",
50
+ "serve": "^14.2.5",
51
+ "typescript": "~5.9.3",
52
+ "typescript-eslint": "^8.46.4",
53
+ "vite": "^7.2.4",
54
+ "vite-plugin-graphql-codegen": "^3.6.3",
55
+ "vitest": "^4.0.17"
56
+ }
57
+ }
@@ -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,129 @@
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
+ > = {
16
+ [_ in K]?: never;
17
+ };
18
+ export type Incremental<T> =
19
+ | T
20
+ | {
21
+ [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never;
22
+ };
23
+ /** All built-in and custom scalars, mapped to their actual values */
24
+ export type Scalars = {
25
+ ID: { input: string; output: string };
26
+ String: { input: string; output: string };
27
+ Boolean: { input: boolean; output: boolean };
28
+ Int: { input: number; output: number };
29
+ Float: { input: number; output: number };
30
+ Base64: { input: any; output: any };
31
+ /** An arbitrary precision signed decimal */
32
+ BigDecimal: { input: any; output: any };
33
+ /** An arbitrary precision signed integer */
34
+ BigInteger: { input: any; output: any };
35
+ /** An 8-bit signed integer */
36
+ Byte: { input: any; output: any };
37
+ /** A UTF-16 code unit; a character on Unicode's BMP */
38
+ Char: { input: any; output: any };
39
+ Currency: { input: any; output: any };
40
+ Date: { input: any; output: any };
41
+ DateTime: { input: any; output: any };
42
+ Double: { input: any; output: any };
43
+ Email: { input: any; output: any };
44
+ EncryptedString: { input: any; output: any };
45
+ /** Can be set to an ID or a Reference to the result of another mutation operation. */
46
+ IdOrRef: { input: any; output: any };
47
+ JSON: { input: any; output: any };
48
+ Latitude: { input: any; output: any };
49
+ /** A 64-bit signed integer */
50
+ Long: { input: any; output: any };
51
+ LongTextArea: { input: any; output: any };
52
+ Longitude: { input: any; output: any };
53
+ MultiPicklist: { input: any; output: any };
54
+ Percent: { input: any; output: any };
55
+ PhoneNumber: { input: any; output: any };
56
+ Picklist: { input: any; output: any };
57
+ RichTextArea: { input: any; output: any };
58
+ /** A 16-bit signed integer */
59
+ Short: { input: any; output: any };
60
+ TextArea: { input: any; output: any };
61
+ Time: { input: any; output: any };
62
+ Url: { input: any; output: any };
63
+ };
64
+
65
+ export enum DataType {
66
+ Address = 'ADDRESS',
67
+ Anytype = 'ANYTYPE',
68
+ Base64 = 'BASE64',
69
+ Boolean = 'BOOLEAN',
70
+ Combobox = 'COMBOBOX',
71
+ Complexvalue = 'COMPLEXVALUE',
72
+ Currency = 'CURRENCY',
73
+ Date = 'DATE',
74
+ Datetime = 'DATETIME',
75
+ Double = 'DOUBLE',
76
+ Email = 'EMAIL',
77
+ Encryptedstring = 'ENCRYPTEDSTRING',
78
+ Int = 'INT',
79
+ Json = 'JSON',
80
+ Junctionidlist = 'JUNCTIONIDLIST',
81
+ Location = 'LOCATION',
82
+ Long = 'LONG',
83
+ Multipicklist = 'MULTIPICKLIST',
84
+ Percent = 'PERCENT',
85
+ Phone = 'PHONE',
86
+ Picklist = 'PICKLIST',
87
+ Reference = 'REFERENCE',
88
+ String = 'STRING',
89
+ Textarea = 'TEXTAREA',
90
+ Time = 'TIME',
91
+ Url = 'URL',
92
+ }
93
+
94
+ export enum FieldExtraTypeInfo {
95
+ ExternalLookup = 'EXTERNAL_LOOKUP',
96
+ ImageUrl = 'IMAGE_URL',
97
+ IndirectLookup = 'INDIRECT_LOOKUP',
98
+ Personname = 'PERSONNAME',
99
+ Plaintextarea = 'PLAINTEXTAREA',
100
+ Richtextarea = 'RICHTEXTAREA',
101
+ SwitchablePersonname = 'SWITCHABLE_PERSONNAME',
102
+ }
103
+
104
+ export enum ResultOrder {
105
+ Asc = 'ASC',
106
+ Desc = 'DESC',
107
+ }
108
+
109
+ export type GetHighRevenueAccountsQueryVariables = Exact<{
110
+ minRevenue?: InputMaybe<Scalars['Currency']['input']>;
111
+ }>;
112
+
113
+ export type GetHighRevenueAccountsQuery = {
114
+ uiapi: {
115
+ query: {
116
+ Account?: {
117
+ edges?: Array<{
118
+ node?: {
119
+ Id: string;
120
+ Name?: { value?: string | null } | null;
121
+ AnnualRevenue?: { value?: any | null } | null;
122
+ Industry?: { value?: any | null } | null;
123
+ Website?: { value?: any | null } | null;
124
+ } | null;
125
+ } | null> | null;
126
+ } | null;
127
+ };
128
+ };
129
+ };
@@ -0,0 +1,33 @@
1
+ import { executeGraphQL } 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 response = await executeGraphQL<GetHighRevenueAccountsQuery>(
28
+ HIGH_REVENUE_ACCOUNTS_QUERY,
29
+ variables
30
+ );
31
+
32
+ return response.uiapi?.query?.Account?.edges?.map(edge => edge?.node) || [];
33
+ }
@@ -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,41 @@
1
+ import { createBrowserRouter, RouterProvider } from "react-router";
2
+ import { routes } from "@/routes";
3
+ import { StrictMode, Component, type ReactNode } from "react";
4
+ import { createRoot } from "react-dom/client";
5
+ import "./styles/global.css";
6
+
7
+ class ErrorBoundary extends Component<
8
+ { children: ReactNode },
9
+ { hasError: boolean; error: Error | null }
10
+ > {
11
+ state = { hasError: false, error: null as Error | null };
12
+ static getDerivedStateFromError(error: Error) {
13
+ return { hasError: true, error };
14
+ }
15
+ render() {
16
+ if (this.state.hasError && this.state.error) {
17
+ return (
18
+ <div className="mx-auto max-w-[600px] p-8 font-sans">
19
+ <h1 className="text-xl text-destructive">Something went wrong</h1>
20
+ <pre className="mt-2 overflow-auto rounded-md bg-destructive/10 p-4 text-sm">
21
+ {this.state.error.message}
22
+ </pre>
23
+ </div>
24
+ );
25
+ }
26
+ return this.props.children;
27
+ }
28
+ }
29
+
30
+ const baseUrl = import.meta.env.BASE_URL ?? "/";
31
+ const router = createBrowserRouter(routes, { basename: baseUrl === "/" ? undefined : baseUrl });
32
+ const rootEl = document.getElementById("root");
33
+ if (rootEl) {
34
+ createRoot(rootEl).render(
35
+ <StrictMode>
36
+ <ErrorBoundary>
37
+ <RouterProvider router={router} />
38
+ </ErrorBoundary>
39
+ </StrictMode>,
40
+ );
41
+ }
@@ -0,0 +1,161 @@
1
+ import { useState } from "react";
2
+ import { Outlet, NavLink } from "react-router";
3
+ import { Button } from "./components/ui/button";
4
+ import { cn } from "./lib/utils";
5
+ import {
6
+ Home,
7
+ Search,
8
+ BarChart3,
9
+ Wrench,
10
+ HelpCircle,
11
+ Menu,
12
+ Heart,
13
+ Bell,
14
+ Building2,
15
+ } from "lucide-react";
16
+
17
+ const SIDEBAR_WIDTH = 200;
18
+ const FLOAT_INSET = 20;
19
+ const FLOAT_GAP = 20;
20
+
21
+ function AppShell() {
22
+ const [navHidden, setNavHidden] = useState(false);
23
+
24
+ return (
25
+ <div className="flex min-h-screen flex-col">
26
+ <header
27
+ className="flex shrink-0 items-center justify-between bg-primary px-6 py-3 text-primary-foreground"
28
+ role="banner"
29
+ >
30
+ <div className="flex items-center gap-3">
31
+ <Button
32
+ type="button"
33
+ variant="ghost"
34
+ size="icon"
35
+ className="min-h-11 min-w-11 text-primary-foreground hover:bg-primary-foreground/20"
36
+ aria-label={navHidden ? "Show menu" : "Hide menu"}
37
+ onClick={() => setNavHidden((h) => !h)}
38
+ >
39
+ <Menu className="size-6" aria-hidden />
40
+ </Button>
41
+ <div className="flex size-8 items-center justify-center">
42
+ <Building2 className="size-7" aria-hidden />
43
+ </div>
44
+ <span className="text-xl font-semibold tracking-wide">ZENLEASE</span>
45
+ </div>
46
+ <div className="flex items-center gap-4">
47
+ <Button
48
+ type="button"
49
+ variant="ghost"
50
+ size="icon"
51
+ className="text-primary-foreground hover:bg-primary-foreground/20"
52
+ aria-label="Favorites"
53
+ >
54
+ <Heart className="size-5" aria-hidden />
55
+ </Button>
56
+ <Button
57
+ type="button"
58
+ variant="ghost"
59
+ size="icon"
60
+ className="text-primary-foreground hover:bg-primary-foreground/20"
61
+ aria-label="Notifications"
62
+ >
63
+ <Bell className="size-5" aria-hidden />
64
+ </Button>
65
+ <div className="flex items-center gap-2">
66
+ <div className="size-9 shrink-0 rounded-full bg-primary-foreground/25" aria-hidden />
67
+ <span className="text-sm font-medium">SARAH</span>
68
+ </div>
69
+ </div>
70
+ </header>
71
+ <div className="relative flex min-h-0 flex-1">
72
+ {!navHidden && (
73
+ <nav
74
+ className="absolute left-5 top-5 bottom-5 z-10 flex w-[200px] flex-col items-start gap-1 overflow-hidden rounded-2xl border border-border bg-card p-4 py-2 shadow-lg"
75
+ aria-label="Main navigation"
76
+ >
77
+ <NavLink
78
+ to="/"
79
+ end
80
+ className={({ isActive }) =>
81
+ cn(
82
+ "flex min-h-11 w-full flex-shrink-0 items-center justify-start gap-3 rounded-xl px-3 py-2 text-muted-foreground no-underline transition-colors",
83
+ isActive && "bg-primary text-primary-foreground",
84
+ )
85
+ }
86
+ aria-label="Home"
87
+ >
88
+ <Home className="size-[22px] shrink-0" aria-hidden />
89
+ <span className="text-sm font-medium">Home</span>
90
+ </NavLink>
91
+ <NavLink
92
+ to="/properties"
93
+ className={({ isActive }) =>
94
+ cn(
95
+ "flex min-h-11 w-full flex-shrink-0 items-center justify-start gap-3 rounded-xl px-3 py-2 text-muted-foreground no-underline transition-colors",
96
+ isActive && "bg-primary text-primary-foreground",
97
+ )
98
+ }
99
+ aria-label="Property Search"
100
+ >
101
+ <Search className="size-[22px] shrink-0" aria-hidden />
102
+ <span className="text-sm font-medium">Property Search</span>
103
+ </NavLink>
104
+ <NavLink
105
+ to="/dashboard"
106
+ className={({ isActive }) =>
107
+ cn(
108
+ "flex min-h-11 w-full flex-shrink-0 items-center justify-start gap-3 rounded-xl px-3 py-2 text-muted-foreground no-underline transition-colors",
109
+ isActive && "bg-primary text-primary-foreground",
110
+ )
111
+ }
112
+ aria-label="Dashboard"
113
+ >
114
+ <BarChart3 className="size-[22px] shrink-0" aria-hidden />
115
+ <span className="text-sm font-medium">Dashboard</span>
116
+ </NavLink>
117
+ <NavLink
118
+ to="/maintenance"
119
+ className={({ isActive }) =>
120
+ cn(
121
+ "flex min-h-11 w-full flex-shrink-0 items-center justify-start gap-3 rounded-xl px-3 py-2 text-muted-foreground no-underline transition-colors",
122
+ isActive && "bg-primary text-primary-foreground",
123
+ )
124
+ }
125
+ aria-label="Maintenance"
126
+ >
127
+ <Wrench className="size-[22px] shrink-0" aria-hidden />
128
+ <span className="text-sm font-medium">Maintenance</span>
129
+ </NavLink>
130
+ <NavLink
131
+ to="/help"
132
+ className={({ isActive }) =>
133
+ cn(
134
+ "flex min-h-11 w-full flex-shrink-0 items-center justify-start gap-3 rounded-xl px-3 py-2 text-muted-foreground no-underline transition-colors",
135
+ isActive && "bg-primary text-primary-foreground",
136
+ )
137
+ }
138
+ aria-label="Help Center"
139
+ >
140
+ <HelpCircle className="size-[22px] shrink-0" aria-hidden />
141
+ <span className="text-sm font-medium">Help Center</span>
142
+ </NavLink>
143
+ </nav>
144
+ )}
145
+ <main
146
+ className="min-h-full flex-1 overflow-auto bg-muted/30 p-6 transition-[margin-left] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]"
147
+ style={{
148
+ marginLeft: navHidden ? 0 : FLOAT_INSET + SIDEBAR_WIDTH + FLOAT_GAP,
149
+ }}
150
+ role="main"
151
+ >
152
+ <Outlet />
153
+ </main>
154
+ </div>
155
+ </div>
156
+ );
157
+ }
158
+
159
+ export default function AppLayout() {
160
+ return <AppShell />;
161
+ }
@@ -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>