chore: sync React B2E (1.116.6) & B2X (1.116.6) samples from npm (#114)

This commit is contained in:
svc-idee-bot 2026-03-26 12:07:03 -05:00 committed by GitHub
parent a2c26f310d
commit 1624c21a5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
395 changed files with 951 additions and 1656 deletions

46
package-lock.json generated
View File

@ -9,8 +9,8 @@
"version": "1.5.0",
"license": "CC-BY-NC-4.0",
"devDependencies": {
"@salesforce/webapp-template-app-react-sample-b2e-experimental": "^1.112.10",
"@salesforce/webapp-template-app-react-sample-b2x-experimental": "^1.112.10",
"@salesforce/webapp-template-app-react-sample-b2e-experimental": "^1.116.6",
"@salesforce/webapp-template-app-react-sample-b2x-experimental": "^1.116.6",
"@types/js-yaml": "^4.0.9",
"js-yaml": "^4.1.1",
"tsx": "^4.21.0"
@ -678,9 +678,9 @@
}
},
"node_modules/@salesforce/kit": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/@salesforce/kit/-/kit-3.2.5.tgz",
"integrity": "sha512-Ad3H423ebBvi+q09BQL4pZC2ffwVUPoeuxwos+Sbr53TamkC3g5YT+ASB4MqqqAhMxFgyalFWIjzx9230tts7g==",
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/@salesforce/kit/-/kit-3.2.6.tgz",
"integrity": "sha512-O8S4LWerHa9Zosqh+IoQjgLtpxMOfObRxaRnUdRV4MLtFUi+bQxQiyFvve6eEaBaMP1b1xVDQpvSvQ+PXEDGFQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@ -688,22 +688,22 @@
}
},
"node_modules/@salesforce/sdk-core": {
"version": "1.112.10",
"resolved": "https://registry.npmjs.org/@salesforce/sdk-core/-/sdk-core-1.112.10.tgz",
"integrity": "sha512-JUbzrDIg5PMuxGlABkNyMmXmQdlXIqdKrstR/lZGhrqektrI7TWc/owun2iXALLsODS3nh3nNV5p87KqYltpGA==",
"version": "1.116.6",
"resolved": "https://registry.npmjs.org/@salesforce/sdk-core/-/sdk-core-1.116.6.tgz",
"integrity": "sha512-5IWfcnQRuaOxtWBbGiW00rk6f4ImNrDDnVhqZOphJhRePOvSLqWLirfIIhRK4j00Tr8w0tgn5H+hIAWCRZvIHw==",
"dev": true,
"license": "SEE LICENSE IN LICENSE.txt"
},
"node_modules/@salesforce/sdk-data": {
"version": "1.112.10",
"resolved": "https://registry.npmjs.org/@salesforce/sdk-data/-/sdk-data-1.112.10.tgz",
"integrity": "sha512-YcQQUiPmdbq4wak9iRjY0FV3Xs+RRwZB0UEQ3ovWxy11rN3ZQBghdxNORYuYkjb4TCiiS6cWVy4/a+o4NspFHQ==",
"version": "1.116.6",
"resolved": "https://registry.npmjs.org/@salesforce/sdk-data/-/sdk-data-1.116.6.tgz",
"integrity": "sha512-yjc1CQvBYAXWrouMmD3buu35u4DwgUGTaDZKU4//F5vIJJitsmPGrJCLDd0HdBzyu/J+77RuVGZBgsal7+zyZQ==",
"dev": true,
"license": "SEE LICENSE IN LICENSE.txt",
"dependencies": {
"@conduit-client/service-fetch-network": "3.17.0",
"@conduit-client/utils": "3.17.0",
"@salesforce/sdk-core": "^1.112.10"
"@salesforce/sdk-core": "^1.116.6"
}
},
"node_modules/@salesforce/ts-types": {
@ -717,14 +717,14 @@
}
},
"node_modules/@salesforce/webapp-experimental": {
"version": "1.112.10",
"resolved": "https://registry.npmjs.org/@salesforce/webapp-experimental/-/webapp-experimental-1.112.10.tgz",
"integrity": "sha512-QwV0j4XIkYt75W78m362H+bJEejX78y1TUvhdcQZLRt5zJWi5BdNg8IhSV+aVSsCM0k6cGuAxgfixgCOX8yHfg==",
"version": "1.116.6",
"resolved": "https://registry.npmjs.org/@salesforce/webapp-experimental/-/webapp-experimental-1.116.6.tgz",
"integrity": "sha512-bbWVK70rv5Y+e26NLyTB2McTZ3MCQFe+NEYZDW/T4FCaAZ3Y57u1HYzgr8p4iWKlu7DU4V1sNTUGjt01mrN0Dg==",
"dev": true,
"license": "SEE LICENSE IN LICENSE.txt",
"dependencies": {
"@salesforce/core": "^8.23.4",
"@salesforce/sdk-data": "^1.112.10",
"@salesforce/sdk-data": "^1.116.6",
"axios": "^1.7.7",
"micromatch": "^4.0.8",
"path-to-regexp": "^8.3.0"
@ -734,20 +734,20 @@
}
},
"node_modules/@salesforce/webapp-template-app-react-sample-b2e-experimental": {
"version": "1.112.10",
"resolved": "https://registry.npmjs.org/@salesforce/webapp-template-app-react-sample-b2e-experimental/-/webapp-template-app-react-sample-b2e-experimental-1.112.10.tgz",
"integrity": "sha512-LZCC61D3HCiGH3kxdQNaF/Z/lwj8s+vbPM/sHGC16amxfByRaxYnuIVJwITxmiKFxbrm19gqSvf9BQ6aH8Cnfg==",
"version": "1.116.6",
"resolved": "https://registry.npmjs.org/@salesforce/webapp-template-app-react-sample-b2e-experimental/-/webapp-template-app-react-sample-b2e-experimental-1.116.6.tgz",
"integrity": "sha512-RctkFfBfK+7HLAnP2ERmXG3Nn8apFF4ywh5LlSyOJVUjbQI48kd7PeiCX5v5G4C5Kvqp7wyhhucY0Mk4k8PLEQ==",
"dev": true,
"license": "SEE LICENSE IN LICENSE.txt",
"dependencies": {
"@salesforce/webapp-experimental": "^1.112.10",
"@salesforce/webapp-experimental": "^1.116.6",
"sonner": "^1.7.0"
}
},
"node_modules/@salesforce/webapp-template-app-react-sample-b2x-experimental": {
"version": "1.112.10",
"resolved": "https://registry.npmjs.org/@salesforce/webapp-template-app-react-sample-b2x-experimental/-/webapp-template-app-react-sample-b2x-experimental-1.112.10.tgz",
"integrity": "sha512-/VPf79iRzyiRjvKn7XzNRnYwhR6sBNRW9qV2wSvgLQhDQ7BIVU8nW5TPKcQOsTUtbgp2GxrV8I1B3XBkgx4TOw==",
"version": "1.116.6",
"resolved": "https://registry.npmjs.org/@salesforce/webapp-template-app-react-sample-b2x-experimental/-/webapp-template-app-react-sample-b2x-experimental-1.116.6.tgz",
"integrity": "sha512-Ql5l+WRvAIQ0iNTutj3k0GDjHRI7wwAbXjzBRKeCR3Tp0pHEXeoKuqj1AACxCv1APAvTOne4cvzb11eJblDOLA==",
"dev": true,
"license": "SEE LICENSE IN LICENSE.txt"
},

View File

@ -11,8 +11,8 @@
"registry": "https://registry.npmjs.org"
},
"devDependencies": {
"@salesforce/webapp-template-app-react-sample-b2e-experimental": "^1.112.10",
"@salesforce/webapp-template-app-react-sample-b2x-experimental": "^1.112.10",
"@salesforce/webapp-template-app-react-sample-b2e-experimental": "^1.116.6",
"@salesforce/webapp-template-app-react-sample-b2x-experimental": "^1.116.6",
"@types/js-yaml": "^4.0.9",
"js-yaml": "^4.1.1",
"tsx": "^4.21.0"

View File

@ -1,353 +0,0 @@
# Salesforce Data Access
- **Fetch or display Salesforce data** — Query records (Account, Contact, Opportunity, custom objects) to show in a component
- **Create, update, or delete records** — Perform mutations on Salesforce data
- **Add data fetching to a component** — Wire up a React component to Salesforce data
- **Call REST APIs** — Use Connect REST, Apex REST, or UI API endpoints
- **Explore the org schema** — Discover available objects, fields, or relationships
## Data SDK Requirement
> **All Salesforce data access MUST use the Data SDK** (`@salesforce/sdk-data`). The SDK handles authentication, CSRF, and base URL resolution. Never use `fetch()` or `axios` directly.
```typescript
import { createDataSDK, gql } from "@salesforce/sdk-data";
const sdk = await createDataSDK();
// GraphQL for record queries/mutations (PREFERRED)
const response = await sdk.graphql?.<ResponseType>(query, variables);
// REST for Connect REST, Apex REST, UI API (when GraphQL insufficient)
const res = await sdk.fetch?.("/services/apexrest/my-resource");
```
**Always use optional chaining** (`sdk.graphql?.()`, `sdk.fetch?.()`) — these methods may be undefined in some surfaces.
## Supported APIs
**Only the following APIs are permitted.** Any endpoint not listed here must not be used.
| API | Method | Endpoints / Use Case |
|-----|--------|----------------------|
| GraphQL | `sdk.graphql` | All record queries and mutations via `uiapi { }` namespace |
| UI API REST | `sdk.fetch` | `/services/data/v{ver}/ui-api/records/{id}` — record metadata when GraphQL is insufficient |
| Apex REST | `sdk.fetch` | `/services/apexrest/{resource}` — custom server-side logic, aggregates, multi-step transactions |
| Connect REST | `sdk.fetch` | `/services/data/v{ver}/connect/file/upload/config` — file upload config |
| Einstein LLM | `sdk.fetch` | `/services/data/v{ver}/einstein/llm/prompt/generations` — AI text generation |
**Not supported:**
- **Enterprise REST query endpoint** (`/services/data/v*/query` with SOQL) — blocked at the proxy level. Use GraphQL for record reads; use Apex REST if server-side SOQL aggregates are required.
- **Aura-enabled Apex** (`@AuraEnabled`) — an LWC/Aura pattern with no invocation path from React webapps.
- **Chatter API** (`/chatter/users/me`) — use `uiapi { currentUser { ... } }` in a GraphQL query instead.
- **Any other Salesforce REST endpoint** not listed in the supported table above.
## Decision: GraphQL vs REST
| Need | Method | Example |
|------|--------|---------|
| Query/mutate records | `sdk.graphql` | Account, Contact, custom objects |
| Current user info | `sdk.graphql` | `uiapi { currentUser { Id Name { value } } }` |
| UI API record metadata | `sdk.fetch` | `/ui-api/records/{id}` |
| Connect REST | `sdk.fetch` | `/connect/file/upload/config` |
| Apex REST | `sdk.fetch` | `/services/apexrest/auth/login` |
| Einstein LLM | `sdk.fetch` | `/einstein/llm/prompt/generations` |
**GraphQL is preferred** for record operations. Use REST only when GraphQL doesn't cover the use case.
---
## GraphQL Workflow
### Step 1: Acquire Schema
The `schema.graphql` file (265K+ lines) is the source of truth. **Never open or parse it directly.**
1. Check if `schema.graphql` exists at the SFDX project root
2. If missing, run from the **webapp dir**: `npm run graphql:schema`
3. Custom objects appear only after metadata is deployed
### Step 2: Look Up Entity Schema
Map user intent to PascalCase names ("accounts" → `Account`), then **run the search script from the project root**:
```bash
# From project root — look up all relevant schema info for one or more entities
bash scripts/graphql-search.sh Account
# Multiple entities at once
bash scripts/graphql-search.sh Account Contact Opportunity
```
The script outputs five sections per entity:
1. **Type definition** — all queryable fields and relationships
2. **Filter options** — available fields for `where:` conditions
3. **Sort options** — available fields for `orderBy:`
4. **Create input** — fields accepted by create mutations
5. **Update input** — fields accepted by update mutations
Use this output to determine exact field names before writing any query or mutation. **Maximum 2 script runs.** If the entity still can't be found, ask the user — the object may not be deployed.
### Step 3: Generate Query
Use the templates below. Every field name **must** be verified from the script output in Step 2.
#### Read Query Template
```graphql
query GetAccounts {
uiapi {
query {
Account(where: { Industry: { eq: "Technology" } }, first: 10) {
edges {
node {
Id
Name @optional { value }
Industry @optional { value }
# Parent relationship
Owner @optional { Name { value } }
# Child relationship
Contacts @optional {
edges { node { Name @optional { value } } }
}
}
}
}
}
}
}
```
**FLS Resilience**: Apply `@optional` to all record fields. The server omits inaccessible fields instead of failing. Consuming code must use optional chaining:
```typescript
const name = node.Name?.value ?? "";
```
#### Mutation Template
```graphql
mutation CreateAccount($input: AccountCreateInput!) {
uiapi {
AccountCreate(input: $input) {
Record { Id Name { value } }
}
}
}
```
**Mutation constraints:**
- Create: Include required fields, only `createable` fields, no child relationships
- Update: Include `Id`, only `updateable` fields
- Delete: Include `Id` only
#### Object Metadata & Picklist Values
Use `uiapi { objectInfos(...) }` to fetch field metadata or picklist values. Pass **either** `apiNames` or `objectInfoInputs` — never both in the same query.
**Object metadata** (field labels, data types, CRUD flags):
```typescript
const GET_OBJECT_INFO = gql`
query GetObjectInfo($apiNames: [String!]!) {
uiapi {
objectInfos(apiNames: $apiNames) {
ApiName
label
labelPlural
fields {
ApiName
label
dataType
updateable
createable
}
}
}
}
`;
const sdk = await createDataSDK();
const response = await sdk.graphql?.(GET_OBJECT_INFO, { apiNames: ["Account"] });
const objectInfos = response?.data?.uiapi?.objectInfos ?? [];
```
**Picklist values** (use `objectInfoInputs` + `... on PicklistField` inline fragment):
```typescript
const GET_PICKLIST_VALUES = gql`
query GetPicklistValues($objectInfoInputs: [ObjectInfoInput!]!) {
uiapi {
objectInfos(objectInfoInputs: $objectInfoInputs) {
ApiName
fields {
ApiName
... on PicklistField {
picklistValuesByRecordTypeIDs {
recordTypeID
picklistValues {
label
value
}
}
}
}
}
}
}
`;
const response = await sdk.graphql?.(GET_PICKLIST_VALUES, {
objectInfoInputs: [{ objectApiName: "Account" }],
});
const fields = response?.data?.uiapi?.objectInfos?.[0]?.fields ?? [];
```
### Step 4: Validate & Test
1. **Lint**: `npx eslint <file>` from webapp dir
2. **Test**: Ask user before testing. For mutations, request input values — never fabricate data.
**If ESLint reports a GraphQL error** (e.g. `Cannot query field`, `Unknown type`, `Unknown argument`), the field or type name is wrong. Re-run the schema search script to find the correct name — do not guess:
```bash
# From project root — re-check the entity that caused the error
bash scripts/graphql-search.sh <EntityName>
```
Then fix the query using the exact names from the script output.
---
## Webapp Integration (React)
```typescript
import { createDataSDK, gql } from "@salesforce/sdk-data";
const GET_ACCOUNTS = gql`
query GetAccounts {
uiapi {
query {
Account(first: 10) {
edges {
node {
Id
Name @optional { value }
Industry @optional { value }
}
}
}
}
}
}
`;
const sdk = await createDataSDK();
const response = await sdk.graphql?.(GET_ACCOUNTS);
if (response?.errors?.length) {
throw new Error(response.errors.map(e => e.message).join("; "));
}
const accounts = response?.data?.uiapi?.query?.Account?.edges?.map(e => e.node) ?? [];
---
## REST API Patterns
Use `sdk.fetch` when GraphQL is insufficient. See the [Supported APIs](#supported-apis) table for the full allowlist.
```typescript
declare const __SF_API_VERSION__: string;
const API_VERSION = typeof __SF_API_VERSION__ !== "undefined" ? __SF_API_VERSION__ : "65.0";
// Connect — file upload config
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/connect/file/upload/config`);
// Apex REST (no version in path)
const res = await sdk.fetch?.("/services/apexrest/auth/login", {
method: "POST",
body: JSON.stringify({ email, password }),
headers: { "Content-Type": "application/json" },
});
// UI API — record with metadata (prefer GraphQL for simple reads)
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/ui-api/records/${recordId}`);
// Einstein LLM
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/einstein/llm/prompt/generations`, {
method: "POST",
body: JSON.stringify({ promptTextorId: prompt }),
});
```
**Current user**: Do not use Chatter (`/chatter/users/me`). Use GraphQL instead:
```typescript
const GET_CURRENT_USER = gql`
query CurrentUser {
uiapi { currentUser { Id Name { value } } }
}
`;
const response = await sdk.graphql?.(GET_CURRENT_USER);
```
---
## Directory Structure
```
<project-root>/ ← SFDX project root
├── schema.graphql ← grep target (lives here)
├── sfdx-project.json
└── force-app/main/default/webapplications/<app-name>/ ← webapp dir
├── package.json ← npm scripts
└── src/
```
| Command | Run From | Why |
|---------|----------|-----|
| `npm run graphql:schema` | webapp dir | Script in webapp's package.json |
| `npx eslint <file>` | webapp dir | Reads eslint.config.js |
| `bash scripts/graphql-search.sh <Entity>` | project root | Schema lookup |
| `sf api request rest` | project root | Needs sfdx-project.json |
---
## Quick Reference
### Schema Lookup (from project root)
Run the search script to get all relevant schema info in one step:
```bash
bash scripts/graphql-search.sh <EntityName>
```
| Script Output Section | Used For |
|-----------------------|----------|
| Type definition | Field names, parent/child relationships |
| Filter options | `where:` conditions |
| Sort options | `orderBy:` |
| CreateRepresentation | Create mutation field list |
| UpdateRepresentation | Update mutation field list |
### Error Categories
| Error Contains | Resolution |
|----------------|------------|
| `Cannot query field` | Field name is wrong — run `graphql-search.sh <Entity>` and use the exact name from the Type definition section |
| `Unknown type` | Type name is wrong — run `graphql-search.sh <Entity>` to confirm the correct PascalCase entity name |
| `Unknown argument` | Argument name is wrong — run `graphql-search.sh <Entity>` and check Filter or OrderBy sections |
| `invalid syntax` | Fix syntax per error message |
| `validation error` | Field name is wrong — run `graphql-search.sh <Entity>` to verify |
| `VariableTypeMismatch` | Correct argument type from schema |
| `invalid cross reference id` | Entity deleted — ask for valid Id |
### Checklist
- [ ] All field names verified via search script (Step 2)
- [ ] `@optional` applied to record fields (reads)
- [ ] Optional chaining in consuming code
- [ ] Lint passes: `npx eslint <file>`

View File

@ -1,16 +0,0 @@
# UI Platform Rule
**All new UI must be built as a Salesforce Web Application.**
When any task involves creating a new UI, frontend, page, dashboard, form, or user-facing feature:
1. Use `sf webapp generate` to scaffold the web app inside the SFDX project — do not use `create-react-app`, standalone Vite, or any other scaffold.
2. The app must live under `<sfdx-source>/webapplications/<AppName>/` as a WebApplication bundle.
3. Do not build new UIs as LWC components, Aura components, or Visualforce pages.
For setup from the project root, use `node scripts/setup-cli.mjs --help` and the options documented there.
## Data Access (MUST FOLLOW)
- **Never hardcode data in the app.** All data displayed in the UI must come from live Salesforce data fetching — no static arrays, mock objects, or placeholder values in production code.
- **Follow `.a4drules/webapp-data.md`** before writing any data access code. All implementation must match those rules (Data SDK, supported APIs, GraphQL workflow).

View File

@ -3,6 +3,89 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.116.6](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.116.5...v1.116.6) (2026-03-26)
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
## [1.116.5](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.116.4...v1.116.5) (2026-03-26)
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
## [1.116.4](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.116.3...v1.116.4) (2026-03-26)
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
## [1.116.3](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.116.2...v1.116.3) (2026-03-26)
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
## [1.116.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.116.1...v1.116.2) (2026-03-25)
### Bug Fixes
* use public npm registry in sf-project-setup @W-21746844@ ([#355](https://github.com/salesforce-experience-platform-emu/webapps/issues/355)) ([4047d1e](https://github.com/salesforce-experience-platform-emu/webapps/commit/4047d1e0b79fcda14badc5a1826da2ae7ef68a68))
## [1.116.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.116.0...v1.116.1) (2026-03-25)
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
# [1.116.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.115.0...v1.116.0) (2026-03-25)
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
# [1.115.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.114.0...v1.115.0) (2026-03-25)
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
# [1.114.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.113.0...v1.114.0) (2026-03-25)
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
# [1.113.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.112.10...v1.113.0) (2026-03-25)
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
## [1.112.10](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.112.9...v1.112.10) (2026-03-24)
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental

View File

@ -1,42 +1,42 @@
# App React Sample B2E
# Property Management App
A B2E (Business-to-Employee) sample React web app for the Salesforce platform. Demonstrates property management, maintenance requests, tenant applications, a dashboard, and an Agentforce conversation client. Built with React, Vite, TypeScript, and Tailwind/shadcn.
A property management sample React web app for the Salesforce platform. Demonstrates property management, maintenance requests, tenant applications, a dashboard, and an Agentforce conversation client. Built with React, Vite, TypeScript, and Tailwind/shadcn.
## What's included
| Path | Description |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `force-app/main/default/webapplications/appreactsampleb2e/` | React web app (source, config, tests) |
| `force-app/main/default/objects/` | 17 custom objects — Agent\_\_c, Application\_\_c, KPI_Snapshot\_\_c, Lease\_\_c, Maintenance_Request\_\_c, Maintenance_Worker\_\_c, Notification\_\_c, Payment\_\_c, Property\_\_c, Property_Cost\_\_c, Property_Feature\_\_c, Property_Image\_\_c, Property_Listing\_\_c, Property_Management_Company\_\_c, Property_Owner\_\_c, Property_Sale\_\_c, Tenant\_\_c |
| `force-app/main/default/layouts/` | Page layouts for each custom object |
| `force-app/main/default/permissionsets/` | `Property_Management_Access` permission set |
| `force-app/main/default/data/` | Sample data (JSON) for all objects, importable via `sf data import tree` |
| Path | Description |
| --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `force-app/main/default/webapplications/propertymanagementapp/` | React web app (source, config, tests) |
| `force-app/main/default/objects/` | 17 custom objects — Agent\_\_c, Application\_\_c, KPI_Snapshot\_\_c, Lease\_\_c, Maintenance_Request\_\_c, Maintenance_Worker\_\_c, Notification\_\_c, Payment\_\_c, Property\_\_c, Property_Cost\_\_c, Property_Feature\_\_c, Property_Image\_\_c, Property_Listing\_\_c, Property_Management_Company\_\_c, Property_Owner\_\_c, Property_Sale\_\_c, Tenant\_\_c |
| `force-app/main/default/layouts/` | Page layouts for each custom object |
| `force-app/main/default/permissionsets/` | `Property_Management_Access` permission set |
| `force-app/main/default/data/` | Sample data (JSON) for all objects, importable via `sf data import tree` |
## Getting started
Navigate to the web app and install dependencies:
```bash
cd force-app/main/default/webapplications/appreactsampleb2e
cd force-app/main/default/webapplications/propertymanagementapp
npm install
npm run dev
```
Opens at http://localhost:5173 by default. For build and test instructions, see the [web app README](force-app/main/default/webapplications/appreactsampleb2e/README.md).
Opens at http://localhost:5173 by default. For build and test instructions, see the [web app README](force-app/main/default/webapplications/propertymanagementapp/README.md).
## Deploy
### Deploy everything (metadata + web app)
```bash
cd force-app/main/default/webapplications/appreactsampleb2e && npm install && npm run build && cd -
cd force-app/main/default/webapplications/propertymanagementapp && npm install && npm run build && cd -
sf project deploy start --source-dir force-app --target-org <alias>
```
### Deploy the web app only
```bash
cd force-app/main/default/webapplications/appreactsampleb2e && npm install && npm run build && cd -
cd force-app/main/default/webapplications/propertymanagementapp && npm install && npm run build && cd -
sf project deploy start --source-dir force-app/main/default/webapplications --target-org <alias>
```

View File

@ -24,7 +24,9 @@
"Description__c": "Modern 2-bedroom apartment with stunning city views. Features include balcony, in-unit washer/dryer, and central AC. Great location near restaurants and shopping.",
"Tour_URL__c": "https://example.com/tour/sunset-4b",
"Hero_Image__c": "https://images.unsplash.com/photo-1522708323590-d24dbb6b0267?w=800",
"Agent__c": "@AgentRef2"
"Agent__c": "@AgentRef2",
"Coordinates__Latitude__s": 34.1007222,
"Coordinates__Longitude__s": -118.329413
},
{
"attributes": {
@ -50,7 +52,9 @@
"Description__c": "Spacious downtown loft with high ceilings and modern finishes. Building amenities include gym, pool, and secured parking.",
"Tour_URL__c": "https://example.com/tour/downtown-loft",
"Hero_Image__c": "https://images.unsplash.com/photo-1554995207-c18c203602cb?w=800",
"Agent__c": "@AgentRef2"
"Agent__c": "@AgentRef2",
"Coordinates__Latitude__s": 34.052235,
"Coordinates__Longitude__s": -118.243683
},
{
"attributes": {
@ -76,7 +80,9 @@
"Description__c": "Cozy garden studio in quiet neighborhood. Features private balcony and additional storage unit. No pets allowed.",
"Tour_URL__c": null,
"Hero_Image__c": "https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?w=800",
"Agent__c": "@AgentRef3"
"Agent__c": "@AgentRef3",
"Coordinates__Latitude__s": 34.18045,
"Coordinates__Longitude__s": -118.3101988
},
{
"attributes": {
@ -102,7 +108,9 @@
"Description__c": "Elegant 3-bedroom penthouse in gated community with luxury amenities. Currently under maintenance, available mid-December.",
"Tour_URL__c": "https://example.com/tour/luxury-penthouse",
"Hero_Image__c": "https://images.unsplash.com/photo-1512917774080-9991f1c4c750?w=800",
"Agent__c": "@AgentRef4"
"Agent__c": "@AgentRef4",
"Coordinates__Latitude__s": 33.7777777,
"Coordinates__Longitude__s": -84.39
},
{
"attributes": {
@ -128,7 +136,9 @@
"Description__c": "Modern high-rise apartment with floor-to-ceiling windows and stunning sunset views. Building features 24/7 doorman.",
"Tour_URL__c": "https://example.com/tour/sunset-towers",
"Hero_Image__c": "https://images.unsplash.com/photo-1545324418-cc1a3fa10c00?w=800",
"Agent__c": "@AgentRef5"
"Agent__c": "@AgentRef5",
"Coordinates__Latitude__s": 34.0914289,
"Coordinates__Longitude__s": -118.3617537
},
{
"attributes": {
@ -154,7 +164,9 @@
"Description__c": "Stunning beachfront home with direct ocean access, modern kitchen, panoramic views, and private pool. Premium property.",
"Tour_URL__c": "https://example.com/tour/ocean-villa",
"Hero_Image__c": "https://images.unsplash.com/photo-1564013799919-ab600027ffc6?w=800",
"Agent__c": "@AgentRef3"
"Agent__c": "@AgentRef3",
"Coordinates__Latitude__s": 34.022352,
"Coordinates__Longitude__s": -118.4911912
},
{
"attributes": {
@ -180,7 +192,9 @@
"Description__c": "Modern condo in Silicon Valley with smart home features, rooftop deck, and premium amenities. Perfect for tech professionals.",
"Tour_URL__c": "https://example.com/tour/tech-hub",
"Hero_Image__c": "https://images.unsplash.com/photo-1580587771525-78b9dba3b914?w=800",
"Agent__c": "@AgentRef2"
"Agent__c": "@AgentRef2",
"Coordinates__Latitude__s": 37.4446046,
"Coordinates__Longitude__s": -122.1595598
},
{
"attributes": {
@ -206,7 +220,9 @@
"Description__c": "Charming historic brownstone with original hardwood floors, exposed brick, and modern updates. Rare Telegraph Hill location.",
"Tour_URL__c": "https://example.com/tour/brownstone",
"Hero_Image__c": "https://images.unsplash.com/photo-1568605114967-8130f3a36994?w=800",
"Agent__c": "@AgentRef3"
"Agent__c": "@AgentRef3",
"Coordinates__Latitude__s": 37.8022765,
"Coordinates__Longitude__s": -122.4062978
},
{
"attributes": {
@ -232,7 +248,9 @@
"Description__c": "Bright studio in trendy Marina District with city views and modern amenities. Walking distance to shops and restaurants.",
"Tour_URL__c": null,
"Hero_Image__c": "https://images.unsplash.com/photo-1502672023488-70e25813eb80?w=800",
"Agent__c": "@AgentRef4"
"Agent__c": "@AgentRef4",
"Coordinates__Latitude__s": 37.7990915,
"Coordinates__Longitude__s": -122.4319382
},
{
"attributes": {
@ -258,7 +276,9 @@
"Description__c": "Magnificent estate with pool, tennis court, wine cellar, and breathtaking city views. Ultimate luxury living.",
"Tour_URL__c": "https://example.com/tour/beverly-estate",
"Hero_Image__c": "https://images.unsplash.com/photo-1613490493576-7fde63acd811?w=800",
"Agent__c": "@AgentRef5"
"Agent__c": "@AgentRef5",
"Coordinates__Latitude__s": 34.0897964,
"Coordinates__Longitude__s": -118.392847
},
{
"attributes": {
@ -284,7 +304,9 @@
"Description__c": "Modern office space with open floor plan, conference rooms, and panoramic bay views. Premium downtown location.",
"Tour_URL__c": "https://example.com/tour/market-office",
"Hero_Image__c": "https://images.unsplash.com/photo-1497366216548-37526070297c?w=800",
"Agent__c": "@AgentRef2"
"Agent__c": "@AgentRef2",
"Coordinates__Latitude__s": 37.7886951,
"Coordinates__Longitude__s": -122.397125
},
{
"attributes": {
@ -310,7 +332,9 @@
"Description__c": "Beautiful wine country home with vineyard views, gourmet kitchen, and outdoor entertainment area. Perfect for wine enthusiasts.",
"Tour_URL__c": "https://example.com/tour/napa-retreat",
"Hero_Image__c": "https://images.unsplash.com/photo-1600596542815-ffad4c1539a9?w=800",
"Agent__c": "@AgentRef4"
"Agent__c": "@AgentRef4",
"Coordinates__Latitude__s": 38.3134748,
"Coordinates__Longitude__s": -122.2863822
},
{
"attributes": {
@ -336,7 +360,9 @@
"Description__c": "Contemporary townhome near tech companies with private garage and community amenities. Ideal for families.",
"Tour_URL__c": null,
"Hero_Image__c": "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=800",
"Agent__c": "@AgentRef5"
"Agent__c": "@AgentRef5",
"Coordinates__Latitude__s": 37.407126,
"Coordinates__Longitude__s": -122.0883981
},
{
"attributes": {
@ -362,7 +388,9 @@
"Description__c": "Converted warehouse loft with soaring ceilings, exposed beams, and artistic neighborhood vibe.",
"Tour_URL__c": "https://example.com/tour/artistic-loft",
"Hero_Image__c": "https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?w=800",
"Agent__c": "@AgentRef1"
"Agent__c": "@AgentRef1",
"Coordinates__Latitude__s": 37.8034013,
"Coordinates__Longitude__s": -122.2798006
},
{
"attributes": {
@ -388,7 +416,9 @@
"Description__c": "Ultra-luxury penthouse with concierge service, gym, and incredible bay and city views. Currently under maintenance.",
"Tour_URL__c": "https://example.com/tour/luxury-highrise",
"Hero_Image__c": "https://images.unsplash.com/photo-1737898378296-94dc316cd443?w=800",
"Agent__c": "@AgentRef2"
"Agent__c": "@AgentRef2",
"Coordinates__Latitude__s": 37.7886951,
"Coordinates__Longitude__s": -122.397125
},
{
"attributes": {
@ -414,7 +444,9 @@
"Description__c": "Charming family home with large backyard, updated kitchen, and excellent school district. Perfect for families.",
"Tour_URL__c": null,
"Hero_Image__c": "https://images.unsplash.com/photo-1600566753190-17f0baa2a6c3?w=800",
"Agent__c": "@AgentRef3"
"Agent__c": "@AgentRef3",
"Coordinates__Latitude__s": 37.4863302,
"Coordinates__Longitude__s": -122.2247151
},
{
"attributes": {
@ -440,7 +472,9 @@
"Description__c": "Brand new studio with smart home features, in-unit laundry, and rooftop terrace access. Modern living at its best.",
"Tour_URL__c": "https://example.com/tour/modern-studio",
"Hero_Image__c": "https://images.unsplash.com/photo-1600585154084-4e5fe7c39198?w=800",
"Agent__c": "@AgentRef4"
"Agent__c": "@AgentRef4",
"Coordinates__Latitude__s": 37.7873686,
"Coordinates__Longitude__s": -122.4055017
},
{
"attributes": {
@ -466,7 +500,9 @@
"Description__c": "Sophisticated executive condo with marble finishes, city views, and luxury building amenities. Premium Nob Hill location.",
"Tour_URL__c": "https://example.com/tour/executive-suite",
"Hero_Image__c": "https://images.unsplash.com/photo-1600607687920-4e2a09cf159d?w=800",
"Agent__c": "@AgentRef5"
"Agent__c": "@AgentRef5",
"Coordinates__Latitude__s": 37.7907054,
"Coordinates__Longitude__s": -122.406344
},
{
"attributes": {
@ -492,7 +528,9 @@
"Description__c": "Fully restored Victorian mansion with period details, modern conveniences, and beautiful garden. Rare Pacific Heights gem.",
"Tour_URL__c": "https://example.com/tour/victorian",
"Hero_Image__c": "https://images.unsplash.com/photo-1600566753151-384129cf4e3e?w=800",
"Agent__c": "@AgentRef1"
"Agent__c": "@AgentRef1",
"Coordinates__Latitude__s": 37.7933344,
"Coordinates__Longitude__s": -122.4200908
},
{
"attributes": {
@ -518,7 +556,9 @@
"Description__c": "Bright apartment with bay views, hardwood floors, and walking distance to Marina Green. Great neighborhood.",
"Tour_URL__c": null,
"Hero_Image__c": "https://images.unsplash.com/photo-1600585154526-990dced4db0d?w=800",
"Agent__c": "@AgentRef2"
"Agent__c": "@AgentRef2",
"Coordinates__Latitude__s": 37.7990915,
"Coordinates__Longitude__s": -122.4319382
},
{
"attributes": {
@ -544,7 +584,9 @@
"Description__c": "Comfortable townhouse with private patio, attached garage, and close to UC Berkeley. Great for students or faculty.",
"Tour_URL__c": null,
"Hero_Image__c": "https://images.unsplash.com/photo-1603661764782-a3c9812afada?w=800",
"Agent__c": "@AgentRef3"
"Agent__c": "@AgentRef3",
"Coordinates__Latitude__s": 37.8774835,
"Coordinates__Longitude__s": -122.2660899
},
{
"attributes": {
@ -570,7 +612,9 @@
"Description__c": "Modern open office space perfect for tech startups with high-speed internet and flexible layout. Prime SOMA location.",
"Tour_URL__c": "https://example.com/tour/startup-hub",
"Hero_Image__c": "https://images.unsplash.com/photo-1497366811353-6870744d04b2?w=800",
"Agent__c": "@AgentRef4"
"Agent__c": "@AgentRef4",
"Coordinates__Latitude__s": 37.7751904,
"Coordinates__Longitude__s": -122.4107713
},
{
"attributes": {
@ -596,7 +640,9 @@
"Description__c": "Spacious family home with modern kitchen, large basement, and proximity to Golden Gate Park. Perfect for families.",
"Tour_URL__c": null,
"Hero_Image__c": "https://images.unsplash.com/photo-1600566753086-00f18fb6b3ea?w=800",
"Agent__c": "@AgentRef5"
"Agent__c": "@AgentRef5",
"Coordinates__Latitude__s": 37.7463285,
"Coordinates__Longitude__s": -122.4732595
},
{
"attributes": {
@ -622,7 +668,9 @@
"Description__c": "Contemporary loft in vibrant Mission Bay with waterfront access and modern amenities. Great location near ballpark.",
"Tour_URL__c": "https://example.com/tour/mission-loft",
"Hero_Image__c": "https://images.unsplash.com/photo-1600607687920-4e2a09cf159d?w=800",
"Agent__c": "@AgentRef1"
"Agent__c": "@AgentRef1",
"Coordinates__Latitude__s": 37.7433669,
"Coordinates__Longitude__s": -122.4666667
},
{
"attributes": {
@ -648,7 +696,9 @@
"Description__c": "Elegant apartment in prestigious Pacific Heights with hardwood floors, in-unit laundry, and 24/7 doorman service.",
"Tour_URL__c": "https://example.com/tour/pac-heights",
"Hero_Image__c": "https://images.unsplash.com/photo-1600585152220-90363fe7e115?w=800",
"Agent__c": "@AgentRef2"
"Agent__c": "@AgentRef2",
"Coordinates__Latitude__s": 37.7851601,
"Coordinates__Longitude__s": -122.4351546
}
]
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Coordinates__c</fullName>
<description>Location coordinates</description>
<displayLocationInDecimal>true</displayLocationInDecimal>
<label>Coordinates</label>
<required>false</required>
<scale>6</scale>
<trackHistory>false</trackHistory>
<trackTrending>false</trackTrending>
<type>Location</type>
</CustomField>

View File

@ -416,6 +416,11 @@
<field>Property__c.Address__c</field>
<readable>true</readable>
</fieldPermissions>
<fieldPermissions>
<editable>true</editable>
<field>Property__c.Coordinates__c</field>
<readable>true</readable>
</fieldPermissions>
<fieldPermissions>
<editable>true</editable>
<field>Property__c.Type__c</field>

View File

@ -97,6 +97,11 @@
<field>Property__c.Address__c</field>
<readable>true</readable>
</fieldPermissions>
<fieldPermissions>
<editable>false</editable>
<field>Property__c.Coordinates__c</field>
<readable>true</readable>
</fieldPermissions>
<!-- Field Permissions: Tenant__c -->
<fieldPermissions>

View File

@ -1,60 +0,0 @@
/**
* Application-wide constants.
*/
import type { PaginationConfig } from "../features/object-search/hooks/useObjectSearchParams";
export const PAGE_SIZE_LIST = 12;
export const DASHBOARD_MAINTENANCE_LIMIT = 5;
export const PROPERTY_FILTER_EXCLUDED_FIELD_PATHS = new Set([
"CreatedDate",
"Hero_Image__c",
"Year_Built__c",
"Available_Date__c",
]);
export const MAINTENANCE_WORKER_FILTER_EXCLUDED_FIELD_PATHS = new Set<string>([]);
export const MAINTENANCE_FILTER_EXCLUDED_FIELD_PATHS = new Set(["Scheduled__c"]);
export const APPLICATION_FILTER_EXCLUDED_FIELD_PATHS = new Set<string>([]);
export const MAINTENANCE_WORKER_OBJECT_API_NAME = "Maintenance_Worker__c" as const;
export const FALLBACK_LABEL_PROPERTIES_PLURAL = "Properties";
export const FALLBACK_LABEL_MAINTENANCE_PLURAL = "Maintenance Requests";
export const FALLBACK_LABEL_MAINTENANCE_WORKERS_PLURAL = "Maintenance Workers";
export const FALLBACK_LABEL_APPLICATIONS_PLURAL = "Applications";
export const PROPERTY_OBJECT_API_NAME = "Property__c" as const;
export const MAINTENANCE_OBJECT_API_NAME = "Maintenance_Request__c" as const;
export const APPLICATION_OBJECT_API_NAME = "Application__c" as const;
export const SEARCHABLE_OBJECTS = [
{
objectApiName: "Property__c" as const,
path: "/properties",
fallbackLabelPlural: FALLBACK_LABEL_PROPERTIES_PLURAL,
},
{
objectApiName: "Maintenance_Request__c" as const,
path: "/maintenance/requests",
fallbackLabelPlural: FALLBACK_LABEL_MAINTENANCE_PLURAL,
},
{
objectApiName: MAINTENANCE_WORKER_OBJECT_API_NAME,
path: "/maintenance/workers",
fallbackLabelPlural: FALLBACK_LABEL_MAINTENANCE_WORKERS_PLURAL,
},
{
objectApiName: "Application__c" as const,
path: "/applications",
fallbackLabelPlural: FALLBACK_LABEL_APPLICATIONS_PLURAL,
},
] as const;
export type SearchableObjectConfig = (typeof SEARCHABLE_OBJECTS)[number];
export const PAGINATION_CONFIG: PaginationConfig = {
defaultPageSize: 6,
validPageSizes: [6, 12, 24, 48],
};

View File

@ -1,35 +0,0 @@
/**
* Central route configuration for list pages and navigation.
* Use these paths for links and search redirects to avoid duplication.
*/
export const PATHS = {
HOME: "/",
PROPERTIES: "/properties",
MAINTENANCE_REQUESTS: "/maintenance/requests",
MAINTENANCE_WORKERS: "/maintenance/workers",
APPLICATIONS: "/applications",
} as const;
export interface ListPageRoute {
path: string;
label: string;
searchParamKey?: string;
}
/** List pages that appear in the Home search object dropdown and in nav */
export const LIST_PAGE_ROUTES: Record<string, ListPageRoute> = {
properties: { path: PATHS.PROPERTIES, label: "Properties", searchParamKey: "q" },
maintenance_requests: {
path: PATHS.MAINTENANCE_REQUESTS,
label: "Maintenance Requests",
searchParamKey: "q",
},
maintenance_workers: {
path: PATHS.MAINTENANCE_WORKERS,
label: "Maintenance Workers",
searchParamKey: "q",
},
applications: { path: PATHS.APPLICATIONS, label: "Applications", searchParamKey: "q" },
} as const;
export type ListPageKey = keyof typeof LIST_PAGE_ROUTES;

View File

@ -1,6 +0,0 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

View File

@ -1,6 +1,6 @@
# App React Sample B2E
# Property Management App
A sample B2E (Business-to-Employee) React web app on the Salesforce platform. Includes property management, maintenance requests, tenant applications, a dashboard, and an Agentforce conversation client. Built with React, Vite, TypeScript, and Tailwind/shadcn.
Property management sample React web app on the Salesforce platform. Includes property management, maintenance requests, tenant applications, a dashboard, and an Agentforce conversation client. Built with React, Vite, TypeScript, and Tailwind/shadcn.
For project-level details (metadata, deploy, sample data), see the [project README](../../../../../../README.md).

View File

@ -15,8 +15,8 @@
"graphql:schema": "node scripts/get-graphql-schema.mjs"
},
"dependencies": {
"@salesforce/sdk-data": "^1.112.10",
"@salesforce/webapp-experimental": "^1.112.10",
"@salesforce/sdk-data": "^1.116.6",
"@salesforce/webapp-experimental": "^1.116.6",
"@tailwindcss/vite": "^4.1.17",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@ -43,7 +43,7 @@
"@graphql-eslint/eslint-plugin": "^4.1.0",
"@graphql-tools/utils": "^11.0.0",
"@playwright/test": "^1.49.0",
"@salesforce/vite-plugin-webapp-experimental": "^1.112.10",
"@salesforce/vite-plugin-webapp-experimental": "^1.116.6",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^14.5.2",

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<WebApplication xmlns="http://soap.sforce.com/2006/04/metadata">
<masterLabel>appreactsampleb2e</masterLabel>
<masterLabel>propertymanagementapp</masterLabel>
<description>A Salesforce web application.</description>
<isActive>true</isActive>
<version>1</version>

Some files were not shown because too many files have changed in this diff Show More