@salesforce/webapp-template-app-react-sample-b2x-experimental 1.93.1 → 1.94.1
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/.a4drules/skills/webapp-features/SKILL.md +9 -9
- package/dist/CHANGELOG.md +19 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/package.json +4 -4
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/NavMenu.tsx +12 -3
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/TopBar.tsx +141 -74
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/authentication/pages/ChangePassword.tsx +1 -1
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/authentication/pages/ForgotPassword.tsx +1 -1
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/authentication/pages/Login.tsx +2 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/authentication/pages/Register.tsx +2 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/authentication/pages/ResetPassword.tsx +1 -1
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/routes.tsx +0 -9
- package/dist/package.json +1 -1
- package/package.json +1 -1
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/userApi.ts +0 -43
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: webapp-features
|
|
3
3
|
description: Search, describe, and install pre-built UI features (authentication, shadcn components, navigation, charts, search, GraphQL, Agentforce AI) into Salesforce webapps. Use this when the user wants to add functionality to a webapp, or when determining what salesforce-provided features are available — whether prompted by the user or on your own initiative. Always check for an existing feature before building from scratch.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -71,8 +71,8 @@ Resolves the feature name to an npm package, installs it and its dependencies (i
|
|
|
71
71
|
|
|
72
72
|
Options:
|
|
73
73
|
|
|
74
|
-
- `--webapp-dir <
|
|
75
|
-
- `--sfdx-
|
|
74
|
+
- `--webapp-dir <name>` (required) — Webapp name, resolves to `<sfdx-source>/webapplications/<name>`
|
|
75
|
+
- `--sfdx-source <path>` (default: `force-app/main/default`) — SFDX source directory
|
|
76
76
|
- `--dry-run` (default: `false`) — Preview changes without writing files
|
|
77
77
|
- `-v, --verbose` (default: `false`) — Enable verbose logging
|
|
78
78
|
- `-y, --yes` (default: `false`) — Skip all prompts (auto-skip conflicts)
|
|
@@ -82,16 +82,16 @@ Options:
|
|
|
82
82
|
```bash
|
|
83
83
|
# Install authentication (also installs shadcn dependency)
|
|
84
84
|
npx @salesforce/webapps-features-experimental install authentication \
|
|
85
|
-
--webapp-dir
|
|
85
|
+
--webapp-dir mywebapp
|
|
86
86
|
|
|
87
87
|
# Dry run to preview changes
|
|
88
88
|
npx @salesforce/webapps-features-experimental install shadcn \
|
|
89
|
-
--webapp-dir
|
|
89
|
+
--webapp-dir mywebapp \
|
|
90
90
|
--dry-run
|
|
91
91
|
|
|
92
92
|
# Non-interactive install (skip all file conflicts)
|
|
93
93
|
npx @salesforce/webapps-features-experimental install authentication \
|
|
94
|
-
--webapp-dir
|
|
94
|
+
--webapp-dir mywebapp \
|
|
95
95
|
--yes
|
|
96
96
|
```
|
|
97
97
|
|
|
@@ -106,7 +106,7 @@ Since you are running in a non-interactive environment, you cannot use `--on-con
|
|
|
106
106
|
```bash
|
|
107
107
|
# Pass 1: detect conflicts
|
|
108
108
|
npx @salesforce/webapps-features-experimental install authentication \
|
|
109
|
-
--webapp-dir
|
|
109
|
+
--webapp-dir mywebapp \
|
|
110
110
|
--on-conflict error
|
|
111
111
|
|
|
112
112
|
# The CLI will exit with an error listing every conflicting file path.
|
|
@@ -115,7 +115,7 @@ npx @salesforce/webapps-features-experimental install authentication \
|
|
|
115
115
|
echo '{ "src/styles/global.css": "overwrite", "src/lib/utils.ts": "skip" }' > resolutions.json
|
|
116
116
|
|
|
117
117
|
npx @salesforce/webapps-features-experimental install authentication \
|
|
118
|
-
--webapp-dir
|
|
118
|
+
--webapp-dir mywebapp \
|
|
119
119
|
--conflict-resolution resolutions.json
|
|
120
120
|
```
|
|
121
121
|
|
|
@@ -131,7 +131,7 @@ Some copy operations use **hint placeholders** in the `"to"` path — descriptiv
|
|
|
131
131
|
2. Rename or relocate it to the intended target (e.g., `src/pages/Home.tsx`)
|
|
132
132
|
3. Or integrate its patterns into an existing file, then delete it
|
|
133
133
|
|
|
134
|
-
**How to identify them:** Hint placeholders use `<descriptive-name>` syntax but are NOT one of the system placeholders (`<
|
|
134
|
+
**How to identify them:** Hint placeholders use `<descriptive-name>` syntax but are NOT one of the system placeholders (`<sfdxSource>`, `<webappDir>`, `<webapp>`). They always appear in the middle or end of a path, never as the leading segment.
|
|
135
135
|
|
|
136
136
|
**Example from features.json:**
|
|
137
137
|
|
package/dist/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
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.94.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.94.0...v1.94.1) (2026-03-12)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* adjust features cli @W-21452399 ([#258](https://github.com/salesforce-experience-platform-emu/webapps/issues/258)) ([524abe8](https://github.com/salesforce-experience-platform-emu/webapps/commit/524abe853684a770777b00238432a01d60dd3366))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [1.94.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.93.1...v1.94.0) (2026-03-12)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
## [1.93.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.93.0...v1.93.1) (2026-03-12)
|
|
7
26
|
|
|
8
27
|
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"graphql:schema": "node scripts/get-graphql-schema.mjs"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@salesforce/sdk-data": "^1.
|
|
19
|
-
"@salesforce/webapp-experimental": "^1.
|
|
18
|
+
"@salesforce/sdk-data": "^1.94.1",
|
|
19
|
+
"@salesforce/webapp-experimental": "^1.94.1",
|
|
20
20
|
"@tailwindcss/vite": "^4.1.17",
|
|
21
|
-
"@tanstack/react-form": "^1.28.
|
|
21
|
+
"@tanstack/react-form": "^1.28.5",
|
|
22
22
|
"@types/leaflet": "^1.9.21",
|
|
23
23
|
"class-variance-authority": "^0.7.1",
|
|
24
24
|
"clsx": "^2.1.1",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@graphql-eslint/eslint-plugin": "^4.1.0",
|
|
44
44
|
"@graphql-tools/utils": "^11.0.0",
|
|
45
45
|
"@playwright/test": "^1.49.0",
|
|
46
|
-
"@salesforce/vite-plugin-webapp-experimental": "^1.
|
|
46
|
+
"@salesforce/vite-plugin-webapp-experimental": "^1.94.1",
|
|
47
47
|
"@testing-library/jest-dom": "^6.6.3",
|
|
48
48
|
"@testing-library/react": "^16.1.0",
|
|
49
49
|
"@testing-library/user-event": "^14.5.2",
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/NavMenu.tsx
CHANGED
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
import { Link, useLocation } from "react-router";
|
|
2
2
|
import { Home, Search, BarChart3, Wrench, Phone, type LucideIcon } from "lucide-react";
|
|
3
|
+
import { useAuth } from "@/features/authentication/context/AuthContext";
|
|
4
|
+
import { useMemo } from "react";
|
|
3
5
|
|
|
4
6
|
interface NavItem {
|
|
5
7
|
path: string;
|
|
6
8
|
icon: LucideIcon;
|
|
7
9
|
label: string;
|
|
10
|
+
authRequired?: boolean;
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
const navItems: NavItem[] = [
|
|
11
14
|
{ path: "/", icon: Home, label: "Home" },
|
|
12
|
-
{ path: "/dashboard", icon: BarChart3, label: "Dashboard" },
|
|
15
|
+
{ path: "/dashboard", icon: BarChart3, label: "Dashboard", authRequired: true },
|
|
13
16
|
{ path: "/properties", icon: Search, label: "Property Search" },
|
|
14
|
-
{ path: "/maintenance
|
|
17
|
+
{ path: "/maintenance", icon: Wrench, label: "Maintenance Requests", authRequired: true },
|
|
15
18
|
{ path: "/contact", icon: Phone, label: "Contact Us" },
|
|
16
19
|
];
|
|
17
20
|
|
|
18
21
|
export function NavMenu() {
|
|
19
22
|
const location = useLocation();
|
|
23
|
+
const { isAuthenticated } = useAuth();
|
|
24
|
+
|
|
25
|
+
const visibleItems = useMemo(
|
|
26
|
+
() => navItems.filter((item) => !item.authRequired || isAuthenticated),
|
|
27
|
+
[isAuthenticated],
|
|
28
|
+
);
|
|
20
29
|
|
|
21
30
|
const isActive = (path: string) => {
|
|
22
31
|
if (path === "/") return location.pathname === "/";
|
|
@@ -28,7 +37,7 @@ export function NavMenu() {
|
|
|
28
37
|
className="flex w-24 flex-col border-r border-gray-200 bg-white py-8"
|
|
29
38
|
aria-label="Main navigation"
|
|
30
39
|
>
|
|
31
|
-
{
|
|
40
|
+
{visibleItems.map((item) => {
|
|
32
41
|
const Icon = item.icon;
|
|
33
42
|
const active = isActive(item.path);
|
|
34
43
|
return (
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/TopBar.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Search, Bell, ChevronDown, Menu } from "lucide-react";
|
|
3
|
-
import {
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Search, Bell, ChevronDown, Menu, UserPen, LogOut, User, Loader2 } from "lucide-react";
|
|
3
|
+
import { Link } from "react-router";
|
|
4
|
+
import { useAuth } from "../features/authentication/context/AuthContext";
|
|
5
|
+
import { ROUTES } from "../features/authentication/authenticationConfig";
|
|
4
6
|
import zenLogo from "@/assets/icons/zen-logo.svg";
|
|
5
7
|
|
|
6
8
|
export interface TopBarProps {
|
|
@@ -8,26 +10,7 @@ export interface TopBarProps {
|
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export function TopBar({ onMenuClick }: TopBarProps) {
|
|
11
|
-
const
|
|
12
|
-
const [showNotifications, setShowNotifications] = useState(false);
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
const loadUserInfo = async () => {
|
|
16
|
-
const userInfo = await getUserInfo();
|
|
17
|
-
if (userInfo) {
|
|
18
|
-
setUserName(userInfo.name);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
loadUserInfo();
|
|
22
|
-
}, []);
|
|
23
|
-
|
|
24
|
-
const handleNotificationClick = () => {
|
|
25
|
-
setShowNotifications(!showNotifications);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const handleCloseNotifications = () => {
|
|
29
|
-
setShowNotifications(false);
|
|
30
|
-
};
|
|
13
|
+
const { user, isAuthenticated, loading } = useAuth();
|
|
31
14
|
|
|
32
15
|
return (
|
|
33
16
|
<header
|
|
@@ -43,65 +26,149 @@ export function TopBar({ onMenuClick }: TopBarProps) {
|
|
|
43
26
|
>
|
|
44
27
|
<Menu className="size-6" aria-hidden />
|
|
45
28
|
</button>
|
|
46
|
-
<
|
|
47
|
-
<img src={zenLogo} alt="Zenlease Logo" className="size-8" />
|
|
48
|
-
<span className="text-xl tracking-wide">
|
|
49
|
-
<span className="font-light">ZEN</span>
|
|
50
|
-
<span className="font-semibold">LEASE</span>
|
|
51
|
-
</span>
|
|
52
|
-
</div>
|
|
29
|
+
<Logo />
|
|
53
30
|
</div>
|
|
54
31
|
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
32
|
+
{loading ? (
|
|
33
|
+
<Loader2 className="size-5 animate-spin" aria-label="Loading" />
|
|
34
|
+
) : isAuthenticated ? (
|
|
35
|
+
<AuthenticatedControls userName={user?.name ?? "User"} />
|
|
36
|
+
) : (
|
|
37
|
+
<LoginLink />
|
|
38
|
+
)}
|
|
39
|
+
</header>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
63
42
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
43
|
+
function Logo() {
|
|
44
|
+
return (
|
|
45
|
+
<div className="flex items-center gap-2">
|
|
46
|
+
<img src={zenLogo} alt="Zenlease Logo" className="size-8" />
|
|
47
|
+
<span className="text-xl tracking-wide">
|
|
48
|
+
<span className="font-light">ZEN</span>
|
|
49
|
+
<span className="font-semibold">LEASE</span>
|
|
50
|
+
</span>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
73
54
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
</div>
|
|
86
|
-
</>
|
|
87
|
-
)}
|
|
88
|
-
</div>
|
|
55
|
+
function LoginLink() {
|
|
56
|
+
return (
|
|
57
|
+
<a
|
|
58
|
+
href={ROUTES.LOGIN.PATH}
|
|
59
|
+
className="flex items-center gap-2 rounded-md bg-white px-4 py-2 font-medium text-teal-700 transition-colors hover:bg-teal-50"
|
|
60
|
+
>
|
|
61
|
+
<User className="size-4" aria-hidden />
|
|
62
|
+
Sign Up / Sign In
|
|
63
|
+
</a>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
89
66
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
67
|
+
function AuthenticatedControls({ userName }: { userName: string }) {
|
|
68
|
+
return (
|
|
69
|
+
<div className="flex items-center gap-4">
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
className="rounded-md p-2 transition-colors hover:bg-teal-600 md:hidden"
|
|
73
|
+
aria-label="Search"
|
|
74
|
+
>
|
|
75
|
+
<Search className="size-5" aria-hidden />
|
|
76
|
+
</button>
|
|
77
|
+
|
|
78
|
+
<NotificationBell />
|
|
79
|
+
<UserMenu userName={userName} />
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function UserMenu({ userName }: { userName: string }) {
|
|
85
|
+
const { logout } = useAuth();
|
|
86
|
+
const [open, setOpen] = useState(false);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className="relative">
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
onClick={() => setOpen((prev) => !prev)}
|
|
93
|
+
className="flex items-center gap-2 rounded-md px-3 py-2 transition-colors hover:bg-teal-600"
|
|
94
|
+
aria-label="User menu"
|
|
95
|
+
aria-expanded={open}
|
|
96
|
+
aria-haspopup="true"
|
|
97
|
+
>
|
|
98
|
+
<div
|
|
99
|
+
className="flex size-8 items-center justify-center rounded-full bg-teal-300 font-semibold text-teal-900"
|
|
100
|
+
aria-hidden
|
|
94
101
|
>
|
|
102
|
+
{userName.charAt(0).toUpperCase()}
|
|
103
|
+
</div>
|
|
104
|
+
<span className="hidden font-medium md:inline">{userName.toUpperCase()}</span>
|
|
105
|
+
<ChevronDown className="hidden size-4 md:inline" aria-hidden />
|
|
106
|
+
</button>
|
|
107
|
+
|
|
108
|
+
{open && (
|
|
109
|
+
<>
|
|
110
|
+
<div className="fixed inset-0 z-40" onClick={() => setOpen(false)} aria-hidden />
|
|
95
111
|
<div
|
|
96
|
-
className="
|
|
97
|
-
|
|
112
|
+
className="absolute right-0 top-full z-50 mt-2 w-48 overflow-hidden rounded-lg bg-white py-1 shadow-xl"
|
|
113
|
+
role="menu"
|
|
98
114
|
>
|
|
99
|
-
|
|
115
|
+
<Link
|
|
116
|
+
to={ROUTES.PROFILE.PATH}
|
|
117
|
+
onClick={() => setOpen(false)}
|
|
118
|
+
className="flex items-center gap-3 px-4 py-2.5 text-sm text-gray-700 transition-colors hover:bg-gray-100"
|
|
119
|
+
role="menuitem"
|
|
120
|
+
>
|
|
121
|
+
<UserPen className="size-4" aria-hidden />
|
|
122
|
+
Edit Profile
|
|
123
|
+
</Link>
|
|
124
|
+
<div className="mx-3 border-t border-gray-200" />
|
|
125
|
+
<button
|
|
126
|
+
type="button"
|
|
127
|
+
onClick={() => {
|
|
128
|
+
setOpen(false);
|
|
129
|
+
logout();
|
|
130
|
+
}}
|
|
131
|
+
className="flex w-full items-center gap-3 px-4 py-2.5 text-sm text-gray-700 transition-colors hover:bg-gray-100"
|
|
132
|
+
role="menuitem"
|
|
133
|
+
>
|
|
134
|
+
<LogOut className="size-4" aria-hidden />
|
|
135
|
+
Log Out
|
|
136
|
+
</button>
|
|
100
137
|
</div>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
138
|
+
</>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function NotificationBell() {
|
|
145
|
+
const [open, setOpen] = useState(false);
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div className="relative">
|
|
149
|
+
<button
|
|
150
|
+
type="button"
|
|
151
|
+
onClick={() => setOpen((prev) => !prev)}
|
|
152
|
+
className="relative rounded-md p-2 transition-colors hover:bg-teal-600"
|
|
153
|
+
aria-label="Notifications"
|
|
154
|
+
>
|
|
155
|
+
<Bell className="size-5" aria-hidden />
|
|
156
|
+
</button>
|
|
157
|
+
|
|
158
|
+
{open && (
|
|
159
|
+
<>
|
|
160
|
+
<div className="fixed inset-0 z-40" onClick={() => setOpen(false)} aria-hidden />
|
|
161
|
+
<div className="absolute right-0 top-full z-50 mt-2 w-80 overflow-hidden rounded-lg bg-white shadow-xl">
|
|
162
|
+
<div className="border-b border-gray-200 p-4">
|
|
163
|
+
<h3 className="text-sm font-semibold text-gray-900">Notifications</h3>
|
|
164
|
+
</div>
|
|
165
|
+
<div className="p-8 text-center">
|
|
166
|
+
<Bell className="mx-auto mb-3 size-12 text-gray-300" aria-hidden />
|
|
167
|
+
<p className="text-sm text-gray-500">No new notifications</p>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</>
|
|
171
|
+
)}
|
|
172
|
+
</div>
|
|
106
173
|
);
|
|
107
174
|
}
|
|
@@ -29,7 +29,7 @@ export default function ChangePassword() {
|
|
|
29
29
|
// [Dev Note] Custom Apex Endpoint: /auth/change-password
|
|
30
30
|
// You must ensure this Apex class exists in your org
|
|
31
31
|
const sdk = await getDataSDK();
|
|
32
|
-
const response = await sdk.fetch!("/
|
|
32
|
+
const response = await sdk.fetch!("/services/apexrest/auth/change-password", {
|
|
33
33
|
method: "POST",
|
|
34
34
|
body: JSON.stringify({
|
|
35
35
|
currentPassword: formFieldValues.currentPassword,
|
|
@@ -25,7 +25,7 @@ export default function ForgotPassword() {
|
|
|
25
25
|
// [Dev Note] Custom Apex Endpoint: /auth/forgot-password
|
|
26
26
|
// You must ensure this Apex class exists in your org
|
|
27
27
|
const sdk = await getDataSDK();
|
|
28
|
-
const response = await sdk.fetch!("/
|
|
28
|
+
const response = await sdk.fetch!("/services/apexrest/auth/forgot-password", {
|
|
29
29
|
method: "POST",
|
|
30
30
|
body: JSON.stringify({ username: value.username.trim() }),
|
|
31
31
|
headers: {
|
|
@@ -27,11 +27,11 @@ export default function Login() {
|
|
|
27
27
|
try {
|
|
28
28
|
// [Dev Note] Salesforce Integration:
|
|
29
29
|
// We use the Data SDK fetch to make an authenticated (or guest) call to Salesforce.
|
|
30
|
-
// "/
|
|
30
|
+
// "/services/apexrest/auth/login" refers to a custom Apex REST resource.
|
|
31
31
|
// You must ensure this Apex class exists in your org and handles the login logic
|
|
32
32
|
// (e.g., creating a session or returning a token).
|
|
33
33
|
const sdk = await getDataSDK();
|
|
34
|
-
const response = await sdk.fetch!("/
|
|
34
|
+
const response = await sdk.fetch!("/services/apexrest/auth/login", {
|
|
35
35
|
method: "POST",
|
|
36
36
|
body: JSON.stringify({
|
|
37
37
|
email: value.email.trim().toLowerCase(),
|
|
@@ -43,12 +43,12 @@ export default function Register() {
|
|
|
43
43
|
try {
|
|
44
44
|
// [Dev Note] Salesforce Integration:
|
|
45
45
|
// We use the Data SDK fetch to make an authenticated (or guest) call to Salesforce.
|
|
46
|
-
// "/
|
|
46
|
+
// "/services/apexrest/auth/register" refers to a custom Apex Class exposed as a REST resource.
|
|
47
47
|
// You must ensure this Apex class exists in your org and handles registration
|
|
48
48
|
// (e.g., duplicate checks and user creation such as Site.createExternalUser).
|
|
49
49
|
const { confirmPassword, ...request } = formFieldValues;
|
|
50
50
|
const sdk = await getDataSDK();
|
|
51
|
-
const response = await sdk.fetch!("/
|
|
51
|
+
const response = await sdk.fetch!("/services/apexrest/auth/register", {
|
|
52
52
|
method: "POST",
|
|
53
53
|
body: JSON.stringify({ request }),
|
|
54
54
|
headers: {
|
|
@@ -26,7 +26,7 @@ export default function ResetPassword() {
|
|
|
26
26
|
// [Dev Note] Custom Apex Endpoint: /auth/reset-password
|
|
27
27
|
// You must ensure this Apex class exists in your org
|
|
28
28
|
const sdk = await getDataSDK();
|
|
29
|
-
const response = await sdk.fetch!("/
|
|
29
|
+
const response = await sdk.fetch!("/services/apexrest/auth/reset-password", {
|
|
30
30
|
method: "POST",
|
|
31
31
|
body: JSON.stringify({ token, newPassword: value.newPassword }),
|
|
32
32
|
headers: {
|
|
@@ -107,15 +107,6 @@ export const routes: RouteObject[] = [
|
|
|
107
107
|
path: "object/Property_Listing__c/:id",
|
|
108
108
|
element: <PropertyDetails />
|
|
109
109
|
},
|
|
110
|
-
{
|
|
111
|
-
path: "maintenance/requests",
|
|
112
|
-
element: <Maintenance />,
|
|
113
|
-
handle: { showInNavigation: true, label: "Maintenance" }
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
path: "application",
|
|
117
|
-
element: <Application />
|
|
118
|
-
},
|
|
119
110
|
{
|
|
120
111
|
path: "contact",
|
|
121
112
|
element: <Contact />,
|
package/dist/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { gql } from "@salesforce/sdk-data";
|
|
2
|
-
import type { GetUserInfoQuery } from "@/api/graphql-operations-types.js";
|
|
3
|
-
import { executeGraphQL } from "@/api/graphqlClient.js";
|
|
4
|
-
|
|
5
|
-
const GET_USER_INFO = gql`
|
|
6
|
-
query GetUserInfo {
|
|
7
|
-
uiapi {
|
|
8
|
-
query {
|
|
9
|
-
User(first: 1) {
|
|
10
|
-
edges {
|
|
11
|
-
node {
|
|
12
|
-
Id
|
|
13
|
-
Name {
|
|
14
|
-
value
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
`;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Fetches the current user's id and name (for TopBar, etc.).
|
|
26
|
-
* Returns null on error or when no user is returned.
|
|
27
|
-
*/
|
|
28
|
-
export async function getUserInfo(): Promise<{ name: string; id: string } | null> {
|
|
29
|
-
try {
|
|
30
|
-
const data = await executeGraphQL<GetUserInfoQuery>(GET_USER_INFO);
|
|
31
|
-
const user = data?.uiapi?.query?.User?.edges?.[0]?.node;
|
|
32
|
-
if (user) {
|
|
33
|
-
return {
|
|
34
|
-
id: user.Id,
|
|
35
|
-
name: user.Name?.value ?? "User",
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
return null;
|
|
39
|
-
} catch (error) {
|
|
40
|
-
console.error("Error fetching user info:", error);
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}
|