diff --git a/package-lock.json b/package-lock.json index ef761af..24e13d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "@salesforce/afv-skills", - "version": "1.6.7", + "version": "1.6.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@salesforce/afv-skills", - "version": "1.6.7", + "version": "1.6.8", "license": "CC-BY-NC-4.0", "devDependencies": { - "@salesforce/ui-bundle-template-app-react-sample-b2e": "^1.120.3", - "@salesforce/ui-bundle-template-app-react-sample-b2x": "^1.120.3", + "@salesforce/ui-bundle-template-app-react-sample-b2e": "^1.120.6", + "@salesforce/ui-bundle-template-app-react-sample-b2x": "^1.120.6", "@salesforce/webapp-template-app-react-sample-b2e-experimental": "^1.117.1", "@salesforce/webapp-template-app-react-sample-b2x-experimental": "^1.117.1", "@types/js-yaml": "^4.0.9", @@ -690,22 +690,22 @@ } }, "node_modules/@salesforce/sdk-core": { - "version": "1.120.3", - "resolved": "https://registry.npmjs.org/@salesforce/sdk-core/-/sdk-core-1.120.3.tgz", - "integrity": "sha512-fzzQwWvN8GCK5peEf37sedsb1gBIHwiAOWXKrohPcwRjUd1wfhMoe45LvV8bOMgO4CBjKrNA21sf0D3FOLtzdQ==", + "version": "1.120.6", + "resolved": "https://registry.npmjs.org/@salesforce/sdk-core/-/sdk-core-1.120.6.tgz", + "integrity": "sha512-yqd3Cf5hPzMJTy9GEsy9N41oFFIrcAOU/QuDDZ+nAmmXNmprM3kt93JCdhHr2L1P6JP2SHMhNMJQ1lKmSPl/uw==", "dev": true, "license": "SEE LICENSE IN LICENSE.txt" }, "node_modules/@salesforce/sdk-data": { - "version": "1.120.3", - "resolved": "https://registry.npmjs.org/@salesforce/sdk-data/-/sdk-data-1.120.3.tgz", - "integrity": "sha512-pw7Bx7Zo0K+apB+waxgbGpGigIsuoPmqDxbMIEdTepW/2/oxFx7RH7edG2qjjt96oHQcTCyPbNdzY/6HjC6TLw==", + "version": "1.120.6", + "resolved": "https://registry.npmjs.org/@salesforce/sdk-data/-/sdk-data-1.120.6.tgz", + "integrity": "sha512-ejVCIU3EJTt2uv1kKx/3Jg98ESHbuG5zh+sn1Z4rfxtU/jVzeLZQyS3dIjw1krhSFIl9He+JU5EW8/s5W6aQSA==", "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.120.3" + "@salesforce/sdk-core": "^1.120.6" } }, "node_modules/@salesforce/ts-types": { @@ -719,14 +719,14 @@ } }, "node_modules/@salesforce/ui-bundle": { - "version": "1.120.3", - "resolved": "https://registry.npmjs.org/@salesforce/ui-bundle/-/ui-bundle-1.120.3.tgz", - "integrity": "sha512-rZAWPKRxTWwFbwudESFqromtF9OFIH7LcqrkJMkq0Wfdt6mRh3t+jVNg8z3Fj4aQf/BqpY4efLofmSnAhjABAQ==", + "version": "1.120.6", + "resolved": "https://registry.npmjs.org/@salesforce/ui-bundle/-/ui-bundle-1.120.6.tgz", + "integrity": "sha512-sIHzbSMkMwvhh7IzAO/TzTMjIdphf8dTu+A4+LHobQ0XLp9Y0dii1ckMhLTb/i97qdCDq+aeIIKVQpRedgjsTw==", "dev": true, "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@salesforce/core": "^8.23.4", - "@salesforce/sdk-data": "^1.120.3", + "@salesforce/sdk-data": "^1.120.6", "micromatch": "^4.0.8", "path-to-regexp": "^8.3.0" }, @@ -735,20 +735,20 @@ } }, "node_modules/@salesforce/ui-bundle-template-app-react-sample-b2e": { - "version": "1.120.3", - "resolved": "https://registry.npmjs.org/@salesforce/ui-bundle-template-app-react-sample-b2e/-/ui-bundle-template-app-react-sample-b2e-1.120.3.tgz", - "integrity": "sha512-1Fn0gPRe4p91+nUptRgN1AcHCuYLe+ooQBSjJPdq/hVmU6E2oISIpntgDj7Z2FIc6O+F3DZDkGXyhcDs0B8j7w==", + "version": "1.120.6", + "resolved": "https://registry.npmjs.org/@salesforce/ui-bundle-template-app-react-sample-b2e/-/ui-bundle-template-app-react-sample-b2e-1.120.6.tgz", + "integrity": "sha512-/A31Oon0gRvs+kshC55T2VoaEqDNfnwsSfJNfCi8Y4Y2UeGuMNtd4tvRrahQ6+QBup4NWUdOQNIwJWaXUWDC4A==", "dev": true, "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { - "@salesforce/ui-bundle": "^1.120.3", + "@salesforce/ui-bundle": "^1.120.6", "sonner": "^1.7.0" } }, "node_modules/@salesforce/ui-bundle-template-app-react-sample-b2x": { - "version": "1.120.3", - "resolved": "https://registry.npmjs.org/@salesforce/ui-bundle-template-app-react-sample-b2x/-/ui-bundle-template-app-react-sample-b2x-1.120.3.tgz", - "integrity": "sha512-waF/yPAjnsM5y7RhcypNaj+WPiy1fD5FdYXPSQyZqngfc/SyVEqtuDr8J7l+pLjkyLoAAIBCRAA0I/uEQfxArQ==", + "version": "1.120.6", + "resolved": "https://registry.npmjs.org/@salesforce/ui-bundle-template-app-react-sample-b2x/-/ui-bundle-template-app-react-sample-b2x-1.120.6.tgz", + "integrity": "sha512-j0joo4RYXM9OnVpZX4vb8nI/AMw9Uxz6xzm9o8KQ8WkU79P20xBdRTKBS1H379rpWV0KcxaMFBx6qGV82jgLNA==", "dev": true, "license": "SEE LICENSE IN LICENSE.txt" }, diff --git a/package.json b/package.json index 018cf58..69eb54a 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "registry": "https://registry.npmjs.org" }, "devDependencies": { - "@salesforce/ui-bundle-template-app-react-sample-b2e": "^1.120.3", - "@salesforce/ui-bundle-template-app-react-sample-b2x": "^1.120.3", + "@salesforce/ui-bundle-template-app-react-sample-b2e": "^1.120.6", + "@salesforce/ui-bundle-template-app-react-sample-b2x": "^1.120.6", "@salesforce/webapp-template-app-react-sample-b2e-experimental": "^1.117.1", "@salesforce/webapp-template-app-react-sample-b2x-experimental": "^1.117.1", "@types/js-yaml": "^4.0.9", diff --git a/samples/ui-bundle-template-app-react-sample-b2e/.version b/samples/ui-bundle-template-app-react-sample-b2e/.version index 8f605fe..4d879ca 100644 --- a/samples/ui-bundle-template-app-react-sample-b2e/.version +++ b/samples/ui-bundle-template-app-react-sample-b2e/.version @@ -1 +1 @@ -1.120.3 +1.120.6 diff --git a/samples/ui-bundle-template-app-react-sample-b2e/CHANGELOG.md b/samples/ui-bundle-template-app-react-sample-b2e/CHANGELOG.md index 3a8ddce..4aebef2 100644 --- a/samples/ui-bundle-template-app-react-sample-b2e/CHANGELOG.md +++ b/samples/ui-bundle-template-app-react-sample-b2e/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.120.6](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.120.5...v1.120.6) (2026-04-07) + +**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project + + + + + +## [1.120.5](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.120.4...v1.120.5) (2026-04-07) + +**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project + + + + + +## [1.120.4](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.120.3...v1.120.4) (2026-04-07) + + +### Bug Fixes + +* make tenant owner of own record ([#414](https://github.com/salesforce-experience-platform-emu/webapps/issues/414)) ([bf1f289](https://github.com/salesforce-experience-platform-emu/webapps/commit/bf1f289a8d56a381adde51e642e67db38099f193)) + + + + + ## [1.120.3](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.120.2...v1.120.3) (2026-04-02) **Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project diff --git a/samples/ui-bundle-template-app-react-sample-b2e/README.md b/samples/ui-bundle-template-app-react-sample-b2e/README.md index ac729b6..ccf690a 100644 --- a/samples/ui-bundle-template-app-react-sample-b2e/README.md +++ b/samples/ui-bundle-template-app-react-sample-b2e/README.md @@ -16,6 +16,7 @@ A property management sample React UI Bundle for the Salesforce platform. Demons - [6. Generate GraphQL Types](#6-generate-graphql-types) - [7. Rebuild the UI Bundle](#7-rebuild-the-ui-bundle) - [8. Deploy the UI Bundle](#8-deploy-the-ui-bundle) + - [9. Agentforce Conversation Client](#9-agentforce-conversation-client) 5. [Local Development](#local-development) 6. [Resources](#resources) @@ -23,13 +24,14 @@ A property management sample React UI Bundle for the Salesforce platform. Demons ## What's Included -| Path | Description | -| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `force-app/main/default/uiBundles/propertymanagementapp/` | React UI Bundle (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 — full CRUD access to all custom objects | -| `force-app/main/default/data/` | Sample data (JSON) for all objects, importable via `sf data import tree` | +``` +force-app/main/default/ +├── uiBundles/ # React UI Bundle (source, config, tests) +├── objects/ # 17 Custom Objects (Property, Tenant, Lease, etc.) +├── layouts/ # Page layouts for all custom objects +├── permissionsets/ # Property_Management_Access (Full CRUD) +└── data/ # Sample JSON data (use: sf data import tree) +``` --- @@ -224,6 +226,13 @@ Once the build is complete, deploy the UI Bundle to your org: sf project deploy start --source-dir force-app/main/default/uiBundles --target-org ``` +### 9. Agentforce Conversation Client + +The sample app includes an Agentforce Conversation Client (ACC) placeholder component in `applayout.tsx`. The ACC component requires a valid `agentId` for an agent that exists in the org. The options to create an agent specific to this use-case are: + +- **Agentforce Vibes:** Generate agent metadata with the extension, then deploy it to the org. +- **Agentforce Builder:** Create the agent in the UI. See [Set Up Your Agent](https://help.salesforce.com/s/articleView?id=ai.agent_parent_setup.htm&type=5). + --- ## Local Development diff --git a/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/scripts/org-setup.config.json b/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/scripts/org-setup.config.json new file mode 100644 index 0000000..d8d76b7 --- /dev/null +++ b/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/scripts/org-setup.config.json @@ -0,0 +1,9 @@ +{ + "permsetAssignments": { + "defaultAssignee": "skip", + "assignments": { + "Property_Management_Access": { "assignee": "currentUser" }, + "Tenant_Maintenance_Access": { "assignee": "currentUser" } + } + } +} diff --git a/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/uiBundles/propertymanagementapp/package.json b/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/uiBundles/propertymanagementapp/package.json index 8930c06..3f8f1cf 100644 --- a/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/uiBundles/propertymanagementapp/package.json +++ b/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/uiBundles/propertymanagementapp/package.json @@ -18,8 +18,8 @@ "graphql:schema": "node scripts/get-graphql-schema.mjs" }, "dependencies": { - "@salesforce/sdk-data": "^1.120.3", - "@salesforce/ui-bundle": "^1.120.3", + "@salesforce/sdk-data": "^1.120.6", + "@salesforce/ui-bundle": "^1.120.6", "@tailwindcss/vite": "^4.1.17", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -46,7 +46,7 @@ "@graphql-eslint/eslint-plugin": "^4.1.0", "@graphql-tools/utils": "^11.0.0", "@playwright/test": "^1.49.0", - "@salesforce/vite-plugin-ui-bundle": "^1.120.3", + "@salesforce/vite-plugin-ui-bundle": "^1.120.6", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.1.0", "@testing-library/user-event": "^14.5.2", @@ -69,5 +69,8 @@ "vite": "^7.2.4", "vite-plugin-graphql-codegen": "^3.6.3", "vitest": "^4.0.17" + }, + "overrides": { + "lodash": "^4.18.1" } } diff --git a/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/uiBundles/propertymanagementapp/src/api/applications/applications.ts b/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/uiBundles/propertymanagementapp/src/api/applications/applications.ts index a2de4be..da1dfd5 100644 --- a/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/uiBundles/propertymanagementapp/src/api/applications/applications.ts +++ b/samples/ui-bundle-template-app-react-sample-b2e/force-app/main/default/uiBundles/propertymanagementapp/src/api/applications/applications.ts @@ -113,6 +113,7 @@ async function ensureTenantForApprovedApplication(applicationId: string): Promis Property__c: propertyId, User_Status__c: "Tenant", Status__c: "Active", + OwnerId: userId, }; if (startDate) { tenantFields.Start_Date__c = String(startDate); diff --git a/samples/ui-bundle-template-app-react-sample-b2e/package-lock.json b/samples/ui-bundle-template-app-react-sample-b2e/package-lock.json index 8f93223..0c9b447 100644 --- a/samples/ui-bundle-template-app-react-sample-b2e/package-lock.json +++ b/samples/ui-bundle-template-app-react-sample-b2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "@salesforce/webapp-template-base-sfdx-project-experimental", - "version": "1.120.3", + "version": "1.120.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@salesforce/webapp-template-base-sfdx-project-experimental", - "version": "1.120.3", + "version": "1.120.6", "license": "SEE LICENSE IN LICENSE.txt", "devDependencies": { "@lwc/eslint-plugin-lwc": "^3.3.0", diff --git a/samples/ui-bundle-template-app-react-sample-b2e/package.json b/samples/ui-bundle-template-app-react-sample-b2e/package.json index 28d059f..c8be9be 100644 --- a/samples/ui-bundle-template-app-react-sample-b2e/package.json +++ b/samples/ui-bundle-template-app-react-sample-b2e/package.json @@ -1,6 +1,6 @@ { "name": "@salesforce/ui-bundle-template-base-sfdx-project", - "version": "1.120.3", + "version": "1.120.6", "description": "Base SFDX project template", "license": "SEE LICENSE IN LICENSE.txt", "publishConfig": { diff --git a/samples/ui-bundle-template-app-react-sample-b2e/scripts/org-setup.config.json b/samples/ui-bundle-template-app-react-sample-b2e/scripts/org-setup.config.json new file mode 100644 index 0000000..98e85e0 --- /dev/null +++ b/samples/ui-bundle-template-app-react-sample-b2e/scripts/org-setup.config.json @@ -0,0 +1,6 @@ +{ + "permsetAssignments": { + "defaultAssignee": "currentUser", + "assignments": {} + } +} \ No newline at end of file diff --git a/samples/ui-bundle-template-app-react-sample-b2e/scripts/org-setup.mjs b/samples/ui-bundle-template-app-react-sample-b2e/scripts/org-setup.mjs index 818b9ad..c5a09d3 100644 --- a/samples/ui-bundle-template-app-react-sample-b2e/scripts/org-setup.mjs +++ b/samples/ui-bundle-template-app-react-sample-b2e/scripts/org-setup.mjs @@ -14,10 +14,22 @@ * 1. login — sf org login web only if org not already connected (skip with --skip-login) * 2. uiBundle — (all UI bundles) npm install && npm run build so dist exists for deploy (skip with --skip-ui-bundle-build) * 3. deploy — sf project deploy start --target-org (requires dist for entity deployment) - * 4. permset — sf org assign permset for each *.permissionset-meta.xml (skip with --skip-permset; override via --permset-name) + * 4. permset — assign permsets per org-setup.config.json (skip with --skip-permset; override via --permset-name) * 5. data — prepare unique fields + sf data import tree (skipped if no data dir/plan) * 6. graphql — (in UI bundle) npm run graphql:schema then npm run graphql:codegen * 7. dev — (in UI bundle) npm run dev — launch dev server (skip with --skip-dev) + * + * Permset assignment config (scripts/org-setup.config.json): + * { + * "permsetAssignments": { + * "defaultAssignee": "currentUser", + * "assignments": { + * "Guest_Permset": { "assignee": "guest@mysite.example.com" }, + * "Internal_Only": { "assignee": "skip" } + * } + * } + * } + * Assignee values: "currentUser" (default), "skip", or a specific username. */ import { spawnSync } from 'node:child_process'; @@ -127,6 +139,20 @@ Options: --skip-dev Do not launch the dev server at the end -y, --yes Skip interactive step picker; run all enabled steps immediately -h, --help Show this help + +Permset config (scripts/org-setup.config.json): + Control per-permset assignment via a config file. Example: + { + "permsetAssignments": { + "defaultAssignee": "currentUser", + "assignments": { + "Guest_Permset": { "assignee": "guest@mysite.example.com" }, + "Internal_Only": { "assignee": "skip" } + } + } + } + Assignee values: "currentUser" (default), "skip", or a specific username. + Without this file, all discovered permsets are assigned to the current user. `); process.exit(0); } @@ -181,6 +207,51 @@ function discoverPermissionSetNames() { return names.sort(); } +/** + * Load permset assignment configuration from org-setup.config.json. + * + * Config shape: + * { + * "permsetAssignments": { + * "defaultAssignee": "currentUser", // "currentUser" (default) or "skip" + * "assignments": { + * "My_Guest_Permset": { "assignee": "guest@mysite.example.com" }, + * "Internal_Only": { "assignee": "skip" } + * } + * } + * } + * + * Assignee values: + * "currentUser" — assign to the user running the script (default) + * "skip" — do not assign this permset + * "" — assign to a specific user via --on-behalf-of + * + * Returns { defaultAssignee: string, assignments: Record } + */ +function loadPermsetConfig() { + const configPath = resolve(__dirname, 'org-setup.config.json'); + const defaults = { defaultAssignee: 'currentUser', assignments: {} }; + if (!existsSync(configPath)) return defaults; + try { + const raw = JSON.parse(readFileSync(configPath, 'utf8')); + const section = raw?.permsetAssignments; + if (!section) return defaults; + return { + defaultAssignee: section.defaultAssignee || 'currentUser', + assignments: section.assignments || {}, + }; + } catch (err) { + console.warn(`Warning: failed to parse org-setup.config.json: ${err.message}; using defaults.`); + return defaults; + } +} + +/** Resolve the effective assignee for a given permset name. */ +function resolveAssignee(permsetName, permsetConfig) { + const override = permsetConfig.assignments[permsetName]; + return override?.assignee || permsetConfig.defaultAssignee; +} + function isOrgConnected(targetOrg) { const result = spawnSync('sf', ['org', 'display', '--target-org', targetOrg, '--json'], { cwd: ROOT, @@ -422,28 +493,35 @@ async function main() { } if (!skipPermset) { + const permsetConfig = loadPermsetConfig(); if (permsetNames.length === 0) { console.log('\n--- Assign permission sets ---'); console.log('No permission sets found under permissionsets/ and none passed via --permset-name; skipping.'); } else { console.log('\n--- Assign permission sets ---'); for (const permsetName of permsetNames) { - const permsetResult = spawnSync( - 'sf', - ['org', 'assign', 'permset', '--name', permsetName, '--target-org', targetOrg], - { - cwd: ROOT, - stdio: 'pipe', - shell: true, - } - ); + const assignee = resolveAssignee(permsetName, permsetConfig); + if (assignee === 'skip') { + console.log(`Permission set "${permsetName}" — skipped (config).`); + continue; + } + const sfArgs = ['org', 'assign', 'permset', '--name', permsetName, '--target-org', targetOrg]; + if (assignee !== 'currentUser') { + sfArgs.push('--on-behalf-of', assignee); + } + const assigneeLabel = assignee === 'currentUser' ? 'current user' : assignee; + const permsetResult = spawnSync('sf', sfArgs, { + cwd: ROOT, + stdio: 'pipe', + shell: true, + }); if (permsetResult.status === 0) { - console.log(`Permission set "${permsetName}" assigned.`); + console.log(`Permission set "${permsetName}" assigned to ${assigneeLabel}.`); } else { const out = (permsetResult.stderr?.toString() || '') + (permsetResult.stdout?.toString() || ''); if (out.includes('Duplicate') && out.includes('PermissionSet')) { - console.log(`Permission set "${permsetName}" already assigned; skipping.`); + console.log(`Permission set "${permsetName}" already assigned to ${assigneeLabel}; skipping.`); } else if (out.includes('not found') && out.includes('target org')) { console.log(`Permission set "${permsetName}" not in org; skipping.`); } else { diff --git a/samples/ui-bundle-template-app-react-sample-b2x/.version b/samples/ui-bundle-template-app-react-sample-b2x/.version index 8f605fe..4d879ca 100644 --- a/samples/ui-bundle-template-app-react-sample-b2x/.version +++ b/samples/ui-bundle-template-app-react-sample-b2x/.version @@ -1 +1 @@ -1.120.3 +1.120.6 diff --git a/samples/ui-bundle-template-app-react-sample-b2x/CHANGELOG.md b/samples/ui-bundle-template-app-react-sample-b2x/CHANGELOG.md index 3a8ddce..4aebef2 100644 --- a/samples/ui-bundle-template-app-react-sample-b2x/CHANGELOG.md +++ b/samples/ui-bundle-template-app-react-sample-b2x/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.120.6](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.120.5...v1.120.6) (2026-04-07) + +**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project + + + + + +## [1.120.5](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.120.4...v1.120.5) (2026-04-07) + +**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project + + + + + +## [1.120.4](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.120.3...v1.120.4) (2026-04-07) + + +### Bug Fixes + +* make tenant owner of own record ([#414](https://github.com/salesforce-experience-platform-emu/webapps/issues/414)) ([bf1f289](https://github.com/salesforce-experience-platform-emu/webapps/commit/bf1f289a8d56a381adde51e642e67db38099f193)) + + + + + ## [1.120.3](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.120.2...v1.120.3) (2026-04-02) **Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project diff --git a/samples/ui-bundle-template-app-react-sample-b2x/README.md b/samples/ui-bundle-template-app-react-sample-b2x/README.md index 123a98b..bd08c4b 100644 --- a/samples/ui-bundle-template-app-react-sample-b2x/README.md +++ b/samples/ui-bundle-template-app-react-sample-b2x/README.md @@ -29,20 +29,25 @@ A property rental sample React UI Bundle for Salesforce Experience Cloud. Demons ## What's Included -| Path | Description | -| ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `force-app/main/default/uiBundles/propertyrentalapp/` | React UI Bundle (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` (full admin access) and `Tenant_Maintenance_Access` (scoped tenant access) | -| `force-app/main/default/classes/` | Apex classes — `MaintenanceRequestTriggerHandler`, `TenantTriggerHandler`, `UIBundleAuthUtils`, `UIBundleChangePassword`, `UIBundleForgotPassword`, `UIBundleLogin`, `UIBundleAppRegistration` | -| `force-app/main/default/triggers/` | Apex triggers — `MaintenanceRequestTrigger`, `TenantTrigger` | -| `force-app/main/default/cspTrustedSites/` | CSP trusted sites for external resources (Google Fonts, Pexels, Unsplash, GitHub Avatars, OpenStreetMap, Open-Meteo) | -| `force-app/main/default/data/` | Sample data (JSON) for all objects, importable via `sf data import tree` | -| `force-app/main/default/digitalExperienceConfigs/` | Experience Cloud site configuration | -| `force-app/main/default/digitalExperiences/` | Experience Cloud site definition | -| `force-app/main/default/networks/` | Experience Cloud network metadata | -| `force-app/main/default/sites/` | Salesforce Sites configuration | +``` +force-app/main/default/ +├── classes/ # Apex Logic: Triggers, Auth, and Registration +│ ├── MaintenanceRequestTriggerHandler.cls +│ ├── TenantTriggerHandler.cls +│ └── UIBundle (Auth, Login, Registration, etc.) +├── cspTrustedSites/ # External resource security (Maps, Fonts, Avatars) +├── data/ # Sample JSON data for "sf data import tree" +├── digitalExperienceConfigs/ # Experience Cloud site configurations +├── digitalExperiences/ # Experience Cloud site definitions +├── layouts/ # Page layouts for all 17 custom objects +├── networks/ # Experience Cloud network metadata +├── objects/ # 17 Custom Objects (Property, Tenant, Lease, etc.) +├── permissionsets/ # Access: Property_Management (Admin) & Tenant (Scoped) +├── sites/ # Salesforce Sites configuration +├── triggers/ # Apex Triggers: MaintenanceRequest & Tenant +└── uiBundles/ + └── propertyrentalapp/ # React UI Bundle (source, config, tests) +``` --- @@ -462,7 +467,7 @@ By default, Salesforce does not expose any records to guest users. You must crea 2. Scroll to the **Property Sharing Rules** section and click **New**. 3. Configure the rule: - **Label**: `Available Properties for Guest Users` - - **Rule Type**: Select **Based on criteria** + - **Rule Type**: Select **Guest user access, based on criteria** - **Criteria**: `Status Equals Available` (adjust the field and value to match your data model) - **Share with**: Select **Guests of the Property Rental App site** (the guest user group for your Experience Cloud site) - **Access Level**: `Read Only` @@ -473,7 +478,7 @@ By default, Salesforce does not expose any records to guest users. You must crea Repeat the same steps in the **Property Listing Sharing Rules** section: - **Label**: `Available Property Listings for Guest Users` -- **Rule Type**: `Based on criteria` +- **Rule Type**: `Guest user access, based on criteria` - **Criteria**: Match listings you want to expose (e.g., `Status Equals Active`) - **Share with**: `Guests of the Property Rental App site` - **Access Level**: `Read Only` diff --git a/samples/ui-bundle-template-app-react-sample-b2x/force-app/main/default/scripts/org-setup.config.json b/samples/ui-bundle-template-app-react-sample-b2x/force-app/main/default/scripts/org-setup.config.json new file mode 100644 index 0000000..d8d76b7 --- /dev/null +++ b/samples/ui-bundle-template-app-react-sample-b2x/force-app/main/default/scripts/org-setup.config.json @@ -0,0 +1,9 @@ +{ + "permsetAssignments": { + "defaultAssignee": "skip", + "assignments": { + "Property_Management_Access": { "assignee": "currentUser" }, + "Tenant_Maintenance_Access": { "assignee": "currentUser" } + } + } +} diff --git a/samples/ui-bundle-template-app-react-sample-b2x/force-app/main/default/uiBundles/propertyrentalapp/package.json b/samples/ui-bundle-template-app-react-sample-b2x/force-app/main/default/uiBundles/propertyrentalapp/package.json index 84abce6..9593696 100644 --- a/samples/ui-bundle-template-app-react-sample-b2x/force-app/main/default/uiBundles/propertyrentalapp/package.json +++ b/samples/ui-bundle-template-app-react-sample-b2x/force-app/main/default/uiBundles/propertyrentalapp/package.json @@ -18,8 +18,8 @@ "graphql:schema": "node scripts/get-graphql-schema.mjs" }, "dependencies": { - "@salesforce/sdk-data": "^1.120.3", - "@salesforce/ui-bundle": "^1.120.3", + "@salesforce/sdk-data": "^1.120.6", + "@salesforce/ui-bundle": "^1.120.6", "@tailwindcss/vite": "^4.1.17", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -50,7 +50,7 @@ "@graphql-eslint/eslint-plugin": "^4.1.0", "@graphql-tools/utils": "^11.0.0", "@playwright/test": "^1.49.0", - "@salesforce/vite-plugin-ui-bundle": "^1.120.3", + "@salesforce/vite-plugin-ui-bundle": "^1.120.6", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.1.0", "@testing-library/user-event": "^14.5.2", @@ -73,5 +73,8 @@ "vite": "^7.2.4", "vite-plugin-graphql-codegen": "^3.6.3", "vitest": "^4.0.17" + }, + "overrides": { + "lodash": "^4.18.1" } } diff --git a/samples/ui-bundle-template-app-react-sample-b2x/package-lock.json b/samples/ui-bundle-template-app-react-sample-b2x/package-lock.json index 8f93223..0c9b447 100644 --- a/samples/ui-bundle-template-app-react-sample-b2x/package-lock.json +++ b/samples/ui-bundle-template-app-react-sample-b2x/package-lock.json @@ -1,12 +1,12 @@ { "name": "@salesforce/webapp-template-base-sfdx-project-experimental", - "version": "1.120.3", + "version": "1.120.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@salesforce/webapp-template-base-sfdx-project-experimental", - "version": "1.120.3", + "version": "1.120.6", "license": "SEE LICENSE IN LICENSE.txt", "devDependencies": { "@lwc/eslint-plugin-lwc": "^3.3.0", diff --git a/samples/ui-bundle-template-app-react-sample-b2x/package.json b/samples/ui-bundle-template-app-react-sample-b2x/package.json index 28d059f..c8be9be 100644 --- a/samples/ui-bundle-template-app-react-sample-b2x/package.json +++ b/samples/ui-bundle-template-app-react-sample-b2x/package.json @@ -1,6 +1,6 @@ { "name": "@salesforce/ui-bundle-template-base-sfdx-project", - "version": "1.120.3", + "version": "1.120.6", "description": "Base SFDX project template", "license": "SEE LICENSE IN LICENSE.txt", "publishConfig": { diff --git a/samples/ui-bundle-template-app-react-sample-b2x/scripts/org-setup.config.json b/samples/ui-bundle-template-app-react-sample-b2x/scripts/org-setup.config.json new file mode 100644 index 0000000..98e85e0 --- /dev/null +++ b/samples/ui-bundle-template-app-react-sample-b2x/scripts/org-setup.config.json @@ -0,0 +1,6 @@ +{ + "permsetAssignments": { + "defaultAssignee": "currentUser", + "assignments": {} + } +} \ No newline at end of file diff --git a/samples/ui-bundle-template-app-react-sample-b2x/scripts/org-setup.mjs b/samples/ui-bundle-template-app-react-sample-b2x/scripts/org-setup.mjs index 818b9ad..c5a09d3 100644 --- a/samples/ui-bundle-template-app-react-sample-b2x/scripts/org-setup.mjs +++ b/samples/ui-bundle-template-app-react-sample-b2x/scripts/org-setup.mjs @@ -14,10 +14,22 @@ * 1. login — sf org login web only if org not already connected (skip with --skip-login) * 2. uiBundle — (all UI bundles) npm install && npm run build so dist exists for deploy (skip with --skip-ui-bundle-build) * 3. deploy — sf project deploy start --target-org (requires dist for entity deployment) - * 4. permset — sf org assign permset for each *.permissionset-meta.xml (skip with --skip-permset; override via --permset-name) + * 4. permset — assign permsets per org-setup.config.json (skip with --skip-permset; override via --permset-name) * 5. data — prepare unique fields + sf data import tree (skipped if no data dir/plan) * 6. graphql — (in UI bundle) npm run graphql:schema then npm run graphql:codegen * 7. dev — (in UI bundle) npm run dev — launch dev server (skip with --skip-dev) + * + * Permset assignment config (scripts/org-setup.config.json): + * { + * "permsetAssignments": { + * "defaultAssignee": "currentUser", + * "assignments": { + * "Guest_Permset": { "assignee": "guest@mysite.example.com" }, + * "Internal_Only": { "assignee": "skip" } + * } + * } + * } + * Assignee values: "currentUser" (default), "skip", or a specific username. */ import { spawnSync } from 'node:child_process'; @@ -127,6 +139,20 @@ Options: --skip-dev Do not launch the dev server at the end -y, --yes Skip interactive step picker; run all enabled steps immediately -h, --help Show this help + +Permset config (scripts/org-setup.config.json): + Control per-permset assignment via a config file. Example: + { + "permsetAssignments": { + "defaultAssignee": "currentUser", + "assignments": { + "Guest_Permset": { "assignee": "guest@mysite.example.com" }, + "Internal_Only": { "assignee": "skip" } + } + } + } + Assignee values: "currentUser" (default), "skip", or a specific username. + Without this file, all discovered permsets are assigned to the current user. `); process.exit(0); } @@ -181,6 +207,51 @@ function discoverPermissionSetNames() { return names.sort(); } +/** + * Load permset assignment configuration from org-setup.config.json. + * + * Config shape: + * { + * "permsetAssignments": { + * "defaultAssignee": "currentUser", // "currentUser" (default) or "skip" + * "assignments": { + * "My_Guest_Permset": { "assignee": "guest@mysite.example.com" }, + * "Internal_Only": { "assignee": "skip" } + * } + * } + * } + * + * Assignee values: + * "currentUser" — assign to the user running the script (default) + * "skip" — do not assign this permset + * "" — assign to a specific user via --on-behalf-of + * + * Returns { defaultAssignee: string, assignments: Record } + */ +function loadPermsetConfig() { + const configPath = resolve(__dirname, 'org-setup.config.json'); + const defaults = { defaultAssignee: 'currentUser', assignments: {} }; + if (!existsSync(configPath)) return defaults; + try { + const raw = JSON.parse(readFileSync(configPath, 'utf8')); + const section = raw?.permsetAssignments; + if (!section) return defaults; + return { + defaultAssignee: section.defaultAssignee || 'currentUser', + assignments: section.assignments || {}, + }; + } catch (err) { + console.warn(`Warning: failed to parse org-setup.config.json: ${err.message}; using defaults.`); + return defaults; + } +} + +/** Resolve the effective assignee for a given permset name. */ +function resolveAssignee(permsetName, permsetConfig) { + const override = permsetConfig.assignments[permsetName]; + return override?.assignee || permsetConfig.defaultAssignee; +} + function isOrgConnected(targetOrg) { const result = spawnSync('sf', ['org', 'display', '--target-org', targetOrg, '--json'], { cwd: ROOT, @@ -422,28 +493,35 @@ async function main() { } if (!skipPermset) { + const permsetConfig = loadPermsetConfig(); if (permsetNames.length === 0) { console.log('\n--- Assign permission sets ---'); console.log('No permission sets found under permissionsets/ and none passed via --permset-name; skipping.'); } else { console.log('\n--- Assign permission sets ---'); for (const permsetName of permsetNames) { - const permsetResult = spawnSync( - 'sf', - ['org', 'assign', 'permset', '--name', permsetName, '--target-org', targetOrg], - { - cwd: ROOT, - stdio: 'pipe', - shell: true, - } - ); + const assignee = resolveAssignee(permsetName, permsetConfig); + if (assignee === 'skip') { + console.log(`Permission set "${permsetName}" — skipped (config).`); + continue; + } + const sfArgs = ['org', 'assign', 'permset', '--name', permsetName, '--target-org', targetOrg]; + if (assignee !== 'currentUser') { + sfArgs.push('--on-behalf-of', assignee); + } + const assigneeLabel = assignee === 'currentUser' ? 'current user' : assignee; + const permsetResult = spawnSync('sf', sfArgs, { + cwd: ROOT, + stdio: 'pipe', + shell: true, + }); if (permsetResult.status === 0) { - console.log(`Permission set "${permsetName}" assigned.`); + console.log(`Permission set "${permsetName}" assigned to ${assigneeLabel}.`); } else { const out = (permsetResult.stderr?.toString() || '') + (permsetResult.stdout?.toString() || ''); if (out.includes('Duplicate') && out.includes('PermissionSet')) { - console.log(`Permission set "${permsetName}" already assigned; skipping.`); + console.log(`Permission set "${permsetName}" already assigned to ${assigneeLabel}; skipping.`); } else if (out.includes('not found') && out.includes('target org')) { console.log(`Permission set "${permsetName}" not in org; skipping.`); } else {