@salesforce/webapp-template-app-react-template-b2x-experimental 1.79.1 → 1.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.80.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.79.2...v1.80.0) (2026-03-07)
7
+
8
+ **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
9
+
10
+
11
+
12
+
13
+
14
+ ## [1.79.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.79.1...v1.79.2) (2026-03-06)
15
+
16
+ **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
17
+
18
+
19
+
20
+
21
+
6
22
  ## [1.79.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.79.0...v1.79.1) (2026-03-06)
7
23
 
8
24
  **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
@@ -15,8 +15,8 @@
15
15
  "graphql:schema": "node scripts/get-graphql-schema.mjs"
16
16
  },
17
17
  "dependencies": {
18
- "@salesforce/sdk-data": "^1.79.1",
19
- "@salesforce/webapp-experimental": "^1.79.1",
18
+ "@salesforce/sdk-data": "^1.80.0",
19
+ "@salesforce/webapp-experimental": "^1.80.0",
20
20
  "@tailwindcss/vite": "^4.1.17",
21
21
  "@tanstack/react-form": "^1.28.4",
22
22
  "class-variance-authority": "^0.7.1",
@@ -40,7 +40,7 @@
40
40
  "@graphql-eslint/eslint-plugin": "^4.1.0",
41
41
  "@graphql-tools/utils": "^11.0.0",
42
42
  "@playwright/test": "^1.49.0",
43
- "@salesforce/vite-plugin-webapp-experimental": "^1.79.1",
43
+ "@salesforce/vite-plugin-webapp-experimental": "^1.80.0",
44
44
  "@testing-library/jest-dom": "^6.6.3",
45
45
  "@testing-library/react": "^16.1.0",
46
46
  "@testing-library/user-event": "^14.5.2",
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Extensible user profile fetching and updating via UI API GraphQL.
3
+ */
4
+ import { getDataSDK } from "@salesforce/sdk-data";
5
+ import { flattenGraphQLRecord } from "../utils/helpers";
6
+
7
+ const USER_PROFILE_FIELDS_FULL = `
8
+ Id
9
+ FirstName { value }
10
+ LastName { value }
11
+ Email { value }
12
+ Phone { value }
13
+ Street { value }
14
+ City { value }
15
+ State { value }
16
+ PostalCode { value }
17
+ Country { value }`;
18
+
19
+ function getUserProfileQuery(fields: string): string {
20
+ return `
21
+ query GetUserProfile($userId: ID) {
22
+ uiapi {
23
+ query {
24
+ User(where: { Id: { eq: $userId } }) {
25
+ edges {
26
+ node {${fields}}
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }`;
32
+ }
33
+
34
+ function getUserProfileMutation(fields: string): string {
35
+ return `
36
+ mutation UpdateUserProfile($input: UserUpdateInput!) {
37
+ uiapi {
38
+ UserUpdate(input: $input) {
39
+ Record {${fields}}
40
+ }
41
+ }
42
+ }`;
43
+ }
44
+
45
+ function throwOnGraphQLErrors(response: any): void {
46
+ if (response?.errors?.length) {
47
+ throw new Error(response.errors.map((e: any) => e.message).join("; "));
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Fetches the user profile via GraphQL and returns a flattened record.
53
+ * @param userId - The Salesforce User Id.
54
+ * @param fields - GraphQL field selection (defaults to USER_PROFILE_FIELDS_FULL).
55
+ */
56
+ export async function fetchUserProfile<T>(
57
+ userId: string,
58
+ fields: string = USER_PROFILE_FIELDS_FULL,
59
+ ): Promise<T> {
60
+ const data = await getDataSDK();
61
+ const response: any = await data.graphql?.(getUserProfileQuery(fields), { userId });
62
+ throwOnGraphQLErrors(response);
63
+ return flattenGraphQLRecord<T>(response?.data?.uiapi?.query?.User?.edges?.[0]?.node);
64
+ }
65
+
66
+ /**
67
+ * Updates the user profile via GraphQL and returns the flattened updated record.
68
+ * @param userId - The Salesforce User Id.
69
+ * @param values - The field values to update.
70
+ */
71
+ export async function updateUserProfile<T>(
72
+ userId: string,
73
+ values: Record<string, unknown>,
74
+ ): Promise<T> {
75
+ const data = await getDataSDK();
76
+ const response: any = await data.graphql?.(getUserProfileMutation(USER_PROFILE_FIELDS_FULL), {
77
+ input: { Id: userId, User: { ...values } },
78
+ });
79
+ throwOnGraphQLErrors(response);
80
+ return flattenGraphQLRecord<T>(response?.data?.uiapi?.UserUpdate?.Record);
81
+ }
@@ -34,13 +34,19 @@ interface CenteredPageLayoutProps
34
34
  * Optional page title. If provided, will render a <title> component that React will place in the document head.
35
35
  */
36
36
  title?: string;
37
+ /**
38
+ * When true, content is aligned to the top instead of being vertically centered.
39
+ * @default true
40
+ */
41
+ topAligned?: boolean;
37
42
  }
38
43
 
39
44
  /**
40
45
  * CenteredPageLayout component that provides consistent page structure and spacing.
41
46
  *
42
47
  * This component creates a full-viewport-height container that centers its content
43
- * both horizontally and vertically. The inner content area has a configurable maximum width
48
+ * horizontally. By default, content is top-aligned; set `topAligned={false}` to
49
+ * vertically center instead. The inner content area has a configurable maximum width
44
50
  * to prevent content from becoming too wide on large screens.
45
51
  *
46
52
  * @example
@@ -48,6 +54,10 @@ interface CenteredPageLayoutProps
48
54
  * <CenteredPageLayout contentMaxWidth="md">
49
55
  * <YourPageContent />
50
56
  * </CenteredPageLayout>
57
+ *
58
+ * <CenteredPageLayout contentMaxWidth="md" topAligned={false}>
59
+ * <VerticallyCenteredContent />
60
+ * </CenteredPageLayout>
51
61
  * ```
52
62
  */
53
63
  export function CenteredPageLayout({
@@ -55,13 +65,18 @@ export function CenteredPageLayout({
55
65
  className,
56
66
  children,
57
67
  title,
68
+ topAligned = true,
58
69
  ...props
59
70
  }: CenteredPageLayoutProps) {
60
71
  return (
61
72
  <>
62
73
  {title && <title>{title}</title>}
63
74
  <main
64
- className={cn("flex min-h-svh w-full items-center justify-center p-6 md:p-10", className)}
75
+ className={cn(
76
+ "flex min-h-svh w-full justify-center p-6 md:p-10",
77
+ topAligned ? "items-start" : "items-center",
78
+ className,
79
+ )}
65
80
  data-slot="page-layout"
66
81
  {...props}
67
82
  >
@@ -1,50 +1,15 @@
1
1
  import { useState, useEffect } from "react";
2
2
  import { z } from "zod";
3
3
 
4
- import { getDataSDK } from "@salesforce/sdk-data";
5
-
6
4
  import { CenteredPageLayout } from "../layout/centered-page-layout";
7
5
  import { CardSkeleton } from "../layout/card-skeleton";
8
6
  import { AuthForm } from "../forms/auth-form";
9
7
  import { useAppForm } from "../hooks/form";
10
8
  import { ROUTES } from "../authenticationConfig";
11
9
  import { emailSchema } from "../authHelpers";
12
- import { flattenGraphQLRecord, getErrorMessage } from "../utils/helpers";
10
+ import { getErrorMessage } from "../utils/helpers";
13
11
  import { getUser } from "../context/AuthContext";
14
-
15
- const GRAPHQL_USER_PROFILE_FIELDS = `
16
- Id
17
- FirstName { value }
18
- LastName { value }
19
- Email { value }
20
- Phone { value }
21
- Street { value }
22
- City { value }
23
- State { value }
24
- PostalCode { value }
25
- Country { value }`;
26
-
27
- const QUERY_PROFILE_GRAPHQL = `
28
- query GetUserProfile($userId: ID) {
29
- uiapi {
30
- query {
31
- User(where: { Id: { eq: $userId } }) {
32
- edges {
33
- node {${GRAPHQL_USER_PROFILE_FIELDS}}
34
- }
35
- }
36
- }
37
- }
38
- }`;
39
-
40
- const MUTATE_PROFILE_GRAPHQL = `
41
- mutation UpdateUserProfile($input: UserUpdateInput!) {
42
- uiapi {
43
- UserUpdate(input: $input) {
44
- Record {${GRAPHQL_USER_PROFILE_FIELDS}}
45
- }
46
- }
47
- }`;
12
+ import { fetchUserProfile, updateUserProfile } from "../api/userProfileApi";
48
13
 
49
14
  const optionalString = z
50
15
  .string()
@@ -81,17 +46,9 @@ export default function Profile() {
81
46
  setSubmitError(null);
82
47
  setSuccess(false);
83
48
  try {
84
- const data = await getDataSDK();
85
- const response: any = await data.graphql?.(MUTATE_PROFILE_GRAPHQL, {
86
- input: { Id: user.id, User: { ...value } },
87
- });
88
- if (response?.errors?.length) {
89
- throw new Error(response.errors.map((e: any) => e.message).join("; "));
90
- }
91
- setProfile(flattenGraphQLRecord(response?.data?.uiapi?.UserUpdate?.Record));
92
-
49
+ const updated = await updateUserProfile<ProfileFormValues>(user.id, value);
50
+ setProfile(updated);
93
51
  setSuccess(true);
94
- // Scroll to top of page after successful update so user sees it
95
52
  window.scrollTo({ top: 0, behavior: "smooth" });
96
53
  } catch (err) {
97
54
  setSubmitError(getErrorMessage(err, "Failed to update profile"));
@@ -102,15 +59,10 @@ export default function Profile() {
102
59
 
103
60
  useEffect(() => {
104
61
  let mounted = true;
105
-
106
- getDataSDK()
107
- .then((data) => data.graphql?.(QUERY_PROFILE_GRAPHQL, { userId: user.id }))
108
- .then((response: any) => {
109
- if (response?.errors?.length) {
110
- throw new Error(response.errors.map((e: any) => e.message).join("; "));
111
- }
62
+ fetchUserProfile<ProfileFormValues>(user.id)
63
+ .then((data) => {
112
64
  if (mounted) {
113
- setProfile(flattenGraphQLRecord(response?.data?.uiapi?.query?.User?.edges?.[0]?.node));
65
+ setProfile(data);
114
66
  }
115
67
  })
116
68
  .catch((err: any) => {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-base-sfdx-project-experimental",
3
- "version": "1.79.1",
3
+ "version": "1.80.0",
4
4
  "description": "Base SFDX project template",
5
5
  "private": true,
6
6
  "files": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-app-react-template-b2x-experimental",
3
- "version": "1.79.1",
3
+ "version": "1.80.0",
4
4
  "description": "Base reference app template",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "author": "",