@salesforce/webapp-template-app-react-sample-b2e-experimental 1.48.3 → 1.50.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 +16 -0
- package/dist/force-app/main/default/data/Property__c.json +2 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/api/dashboard.ts +170 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/api/maintenance.ts +221 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/api/properties.ts +157 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/api/utils.ts +4 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/appLayout.tsx +20 -8
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/appliances.svg +13 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/dashboard.svg +4 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/downgraph.svg +3 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/electrical.svg +41 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/files.svg +7 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/hvac.svg +79 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/maintenance.svg +4 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/pest.svg +5 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/plumbing.svg +7 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/properties.svg +14 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/support.svg +6 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/upgraph.svg +3 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/users.svg +8 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/zen-logo.svg +5 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/AnalyticsTile.tsx +29 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ApplicationCard.tsx +43 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/IssuesDonutChart.tsx +66 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/MaintenanceRequestCard.tsx +71 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/MaintenanceTable.tsx +110 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/PriorityBadge.tsx +29 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/PropertyCard.tsx +61 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/StatCard.tsx +52 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/StatusBadge.tsx +37 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/TopBar.tsx +72 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/UserAvatar.tsx +35 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/VerticalNav.tsx +54 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/alert.tsx +69 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/button.tsx +67 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/card.tsx +92 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/dialog.tsx +143 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/field.tsx +222 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/index.ts +72 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/input.tsx +19 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/label.tsx +19 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/pagination.tsx +112 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/select.tsx +183 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/separator.tsx +26 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/skeleton.tsx +14 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/spinner.tsx +15 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/table.tsx +87 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components/ui/tabs.tsx +78 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/components.json +18 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/lib/types.ts +57 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/lib/utils.ts +6 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/pages/Home.tsx +163 -10
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/pages/Maintenance.tsx +176 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/pages/Properties.tsx +94 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/routes.tsx +19 -7
- package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/styles/global.css +160 -0
- package/dist/package.json +1 -1
- package/package.json +2 -2
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.50.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.49.0...v1.50.0) (2026-02-24)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# [1.49.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.48.3...v1.49.0) (2026-02-23)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
## [1.48.3](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.48.2...v1.48.3) (2026-02-23)
|
|
7
23
|
|
|
8
24
|
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
@@ -387,7 +387,7 @@
|
|
|
387
387
|
"Utilities__c": "Water;Trash",
|
|
388
388
|
"Description__c": "Ultra-luxury penthouse with concierge service, gym, and incredible bay and city views. Currently under maintenance.",
|
|
389
389
|
"Tour_URL__c": "https://example.com/tour/luxury-highrise",
|
|
390
|
-
"Hero_Image__c": "https://images.unsplash.com/photo-
|
|
390
|
+
"Hero_Image__c": "https://images.unsplash.com/photo-1737898378296-94dc316cd443?w=800",
|
|
391
391
|
"Agent__c": null
|
|
392
392
|
},
|
|
393
393
|
{
|
|
@@ -543,7 +543,7 @@
|
|
|
543
543
|
"Utilities__c": "Water;Trash",
|
|
544
544
|
"Description__c": "Comfortable townhouse with private patio, attached garage, and close to UC Berkeley. Great for students or faculty.",
|
|
545
545
|
"Tour_URL__c": null,
|
|
546
|
-
"Hero_Image__c": "https://images.unsplash.com/photo-
|
|
546
|
+
"Hero_Image__c": "https://images.unsplash.com/photo-1603661764782-a3c9812afada?w=800",
|
|
547
547
|
"Agent__c": null
|
|
548
548
|
},
|
|
549
549
|
{
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { executeGraphQL } from "@salesforce/webapp-experimental/api";
|
|
2
|
+
import type { DashboardMetrics, Application } from "../lib/types.js";
|
|
3
|
+
import { gql } from "./utils.js";
|
|
4
|
+
import type {
|
|
5
|
+
GetDashboardMetricsQuery,
|
|
6
|
+
GetOpenApplicationsQuery,
|
|
7
|
+
GetOpenApplicationsQueryVariables,
|
|
8
|
+
GetUserInfoQuery,
|
|
9
|
+
} from "./graphql-operations-types.js";
|
|
10
|
+
|
|
11
|
+
// Query to get property counts for dashboard metrics
|
|
12
|
+
const GET_DASHBOARD_METRICS = gql`
|
|
13
|
+
query GetDashboardMetrics {
|
|
14
|
+
uiapi {
|
|
15
|
+
query {
|
|
16
|
+
allProperties: Property__c {
|
|
17
|
+
edges {
|
|
18
|
+
node {
|
|
19
|
+
Id
|
|
20
|
+
Status__c {
|
|
21
|
+
value
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
maintenanceRequests: Maintenance_Request__c(first: 100) {
|
|
27
|
+
edges {
|
|
28
|
+
node {
|
|
29
|
+
Id
|
|
30
|
+
Type__c {
|
|
31
|
+
value
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
// Query to get open applications
|
|
42
|
+
const GET_OPEN_APPLICATIONS = gql`
|
|
43
|
+
query GetOpenApplications($first: Int) {
|
|
44
|
+
uiapi {
|
|
45
|
+
query {
|
|
46
|
+
Application__c(
|
|
47
|
+
first: $first
|
|
48
|
+
where: { Status__c: { in: ["Submitted", "Background Check"] } }
|
|
49
|
+
orderBy: { CreatedDate: { order: ASC } }
|
|
50
|
+
) {
|
|
51
|
+
edges {
|
|
52
|
+
node {
|
|
53
|
+
Id
|
|
54
|
+
Name {
|
|
55
|
+
value
|
|
56
|
+
}
|
|
57
|
+
User__r {
|
|
58
|
+
Name {
|
|
59
|
+
value
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
Property__r {
|
|
63
|
+
Address__c {
|
|
64
|
+
value
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
Status__c {
|
|
68
|
+
value
|
|
69
|
+
}
|
|
70
|
+
CreatedDate {
|
|
71
|
+
value
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
// Query to get current user information
|
|
82
|
+
const GET_USER_INFO = gql`
|
|
83
|
+
query GetUserInfo {
|
|
84
|
+
uiapi {
|
|
85
|
+
query {
|
|
86
|
+
User(first: 1) {
|
|
87
|
+
edges {
|
|
88
|
+
node {
|
|
89
|
+
Id
|
|
90
|
+
Name {
|
|
91
|
+
value
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
// Fetch dashboard metrics
|
|
102
|
+
export async function getDashboardMetrics(): Promise<{
|
|
103
|
+
properties: any[];
|
|
104
|
+
maintenanceRequests: any[];
|
|
105
|
+
}> {
|
|
106
|
+
const response = await executeGraphQL<GetDashboardMetricsQuery>(GET_DASHBOARD_METRICS);
|
|
107
|
+
const properties = response?.uiapi?.query?.allProperties?.edges?.map((edge) => edge?.node) || [];
|
|
108
|
+
const maintenanceRequests =
|
|
109
|
+
response?.uiapi?.query?.maintenanceRequests?.edges?.map((edge) => edge?.node) || [];
|
|
110
|
+
return { properties, maintenanceRequests };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Fetch open applications
|
|
114
|
+
export async function getOpenApplications(first: number = 5): Promise<Application[]> {
|
|
115
|
+
const variables: GetOpenApplicationsQueryVariables = { first };
|
|
116
|
+
const response = await executeGraphQL<GetOpenApplicationsQuery>(GET_OPEN_APPLICATIONS, variables);
|
|
117
|
+
const apps =
|
|
118
|
+
response?.uiapi?.query?.Application__c?.edges?.map((edge) =>
|
|
119
|
+
transformApplication(edge?.node),
|
|
120
|
+
) || [];
|
|
121
|
+
return apps;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Fetch current user information
|
|
125
|
+
export async function getUserInfo(): Promise<{ name: string; id: string } | null> {
|
|
126
|
+
try {
|
|
127
|
+
const response = await executeGraphQL<GetUserInfoQuery>(GET_USER_INFO);
|
|
128
|
+
const user = response?.uiapi?.query?.User?.edges?.[0]?.node;
|
|
129
|
+
if (user) {
|
|
130
|
+
return {
|
|
131
|
+
id: user.Id,
|
|
132
|
+
name: user.Name?.value || "User",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error("Error fetching user info:", error);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Helper function to calculate dashboard metrics from properties
|
|
143
|
+
export const calculateMetrics = (properties: any[]): DashboardMetrics => {
|
|
144
|
+
const total = properties.length;
|
|
145
|
+
const available = properties.filter((p) => p.Status__c?.value === "Available").length;
|
|
146
|
+
const occupied = properties.filter((p) => p.Status__c?.value === "Rented").length;
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
totalProperties: total,
|
|
150
|
+
unitsAvailable: available,
|
|
151
|
+
occupiedUnits: occupied,
|
|
152
|
+
topMaintenanceIssue: "Plumbing",
|
|
153
|
+
topMaintenanceIssueCount: 0,
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Helper function to transform application data
|
|
158
|
+
function transformApplication(node: any): Application {
|
|
159
|
+
const createdDate = new Date(node.CreatedDate?.value);
|
|
160
|
+
const daysAgo = Math.floor((Date.now() - createdDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
161
|
+
const timeAgo = daysAgo === 0 ? "today" : daysAgo === 1 ? "1 day ago" : `${daysAgo} days ago`;
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
id: node.Id,
|
|
165
|
+
applicantName: node.User__r?.Name?.value || "Unknown",
|
|
166
|
+
propertyAddress: node.Property__r?.Address__c?.value || "Unknown Address",
|
|
167
|
+
submittedDate: timeAgo,
|
|
168
|
+
status: node.Status__c?.value?.toLowerCase() || "pending",
|
|
169
|
+
};
|
|
170
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { executeGraphQL } from "@salesforce/webapp-experimental/api";
|
|
2
|
+
import type { MaintenanceRequest } from "../lib/types.js";
|
|
3
|
+
import { gql } from "./utils.js";
|
|
4
|
+
import type {
|
|
5
|
+
GetMaintenanceRequestsQuery,
|
|
6
|
+
GetMaintenanceRequestsQueryVariables,
|
|
7
|
+
GetAllMaintenanceRequestsQuery,
|
|
8
|
+
GetAllMaintenanceRequestsQueryVariables,
|
|
9
|
+
} from "./graphql-operations-types.js";
|
|
10
|
+
|
|
11
|
+
// Query to get recent maintenance requests
|
|
12
|
+
const GET_MAINTENANCE_REQUESTS = gql`
|
|
13
|
+
query GetMaintenanceRequests($first: Int) {
|
|
14
|
+
uiapi {
|
|
15
|
+
query {
|
|
16
|
+
Maintenance_Request__c(first: $first, orderBy: { Priority__c: { order: DESC } }) {
|
|
17
|
+
edges {
|
|
18
|
+
node {
|
|
19
|
+
Id
|
|
20
|
+
Name {
|
|
21
|
+
value
|
|
22
|
+
}
|
|
23
|
+
Property__r {
|
|
24
|
+
Address__c {
|
|
25
|
+
value
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
User__r {
|
|
29
|
+
Name {
|
|
30
|
+
value
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
Type__c {
|
|
34
|
+
value
|
|
35
|
+
}
|
|
36
|
+
Priority__c {
|
|
37
|
+
value
|
|
38
|
+
}
|
|
39
|
+
Status__c {
|
|
40
|
+
value
|
|
41
|
+
}
|
|
42
|
+
Description__c {
|
|
43
|
+
value
|
|
44
|
+
}
|
|
45
|
+
Scheduled__c {
|
|
46
|
+
value
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
// Query to get all maintenance requests for the maintenance page
|
|
57
|
+
const GET_ALL_MAINTENANCE_REQUESTS = gql`
|
|
58
|
+
query GetAllMaintenanceRequests($first: Int, $after: String) {
|
|
59
|
+
uiapi {
|
|
60
|
+
query {
|
|
61
|
+
Maintenance_Request__c(
|
|
62
|
+
first: $first
|
|
63
|
+
after: $after
|
|
64
|
+
orderBy: { Priority__c: { order: DESC }, Scheduled__c: { order: ASC } }
|
|
65
|
+
) {
|
|
66
|
+
edges {
|
|
67
|
+
node {
|
|
68
|
+
Id
|
|
69
|
+
Name {
|
|
70
|
+
value
|
|
71
|
+
}
|
|
72
|
+
Description__c {
|
|
73
|
+
value
|
|
74
|
+
}
|
|
75
|
+
Type__c {
|
|
76
|
+
value
|
|
77
|
+
}
|
|
78
|
+
Priority__c {
|
|
79
|
+
value
|
|
80
|
+
}
|
|
81
|
+
Status__c {
|
|
82
|
+
value
|
|
83
|
+
}
|
|
84
|
+
Scheduled__c {
|
|
85
|
+
value
|
|
86
|
+
}
|
|
87
|
+
Property__r {
|
|
88
|
+
Address__c {
|
|
89
|
+
value
|
|
90
|
+
}
|
|
91
|
+
Name {
|
|
92
|
+
value
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
User__r {
|
|
96
|
+
Name {
|
|
97
|
+
value
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
Owner {
|
|
101
|
+
... on User {
|
|
102
|
+
Name {
|
|
103
|
+
value
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
ContentDocumentLinks(first: 1) {
|
|
108
|
+
edges {
|
|
109
|
+
node {
|
|
110
|
+
ContentDocument {
|
|
111
|
+
LatestPublishedVersionId {
|
|
112
|
+
value
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
pageInfo {
|
|
121
|
+
hasNextPage
|
|
122
|
+
endCursor
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
// Fetch maintenance requests for dashboard
|
|
131
|
+
export async function getMaintenanceRequests(first: number = 5): Promise<MaintenanceRequest[]> {
|
|
132
|
+
const variables: GetMaintenanceRequestsQueryVariables = { first };
|
|
133
|
+
const response = await executeGraphQL<GetMaintenanceRequestsQuery>(
|
|
134
|
+
GET_MAINTENANCE_REQUESTS,
|
|
135
|
+
variables,
|
|
136
|
+
);
|
|
137
|
+
const requests =
|
|
138
|
+
response?.uiapi?.query?.Maintenance_Request__c?.edges?.map((edge) =>
|
|
139
|
+
transformMaintenanceRequest(edge?.node),
|
|
140
|
+
) || [];
|
|
141
|
+
return requests;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Fetch all maintenance requests for the maintenance page
|
|
145
|
+
export async function getAllMaintenanceRequests(
|
|
146
|
+
first: number = 100,
|
|
147
|
+
): Promise<MaintenanceRequest[]> {
|
|
148
|
+
const variables: GetAllMaintenanceRequestsQueryVariables = { first };
|
|
149
|
+
const response = await executeGraphQL<GetAllMaintenanceRequestsQuery>(
|
|
150
|
+
GET_ALL_MAINTENANCE_REQUESTS,
|
|
151
|
+
variables,
|
|
152
|
+
);
|
|
153
|
+
const requests =
|
|
154
|
+
response?.uiapi?.query?.Maintenance_Request__c?.edges?.map((edge: any) =>
|
|
155
|
+
transformMaintenanceTaskFull(edge?.node),
|
|
156
|
+
) || [];
|
|
157
|
+
return requests;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Helper function to transform maintenance request data
|
|
161
|
+
function transformMaintenanceRequest(node: any): MaintenanceRequest {
|
|
162
|
+
const scheduledDate = node.Scheduled__c?.value
|
|
163
|
+
? new Date(node.Scheduled__c.value).toLocaleString()
|
|
164
|
+
: undefined;
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
id: node.Id,
|
|
168
|
+
propertyAddress: node.Property__r?.Address__c?.value || "Unknown Address",
|
|
169
|
+
issueType: node.Type__c?.value || "General",
|
|
170
|
+
priority: node.Priority__c?.value?.toLowerCase() || "medium",
|
|
171
|
+
status: node.Status__c?.value?.toLowerCase() || "new",
|
|
172
|
+
assignedWorker: undefined,
|
|
173
|
+
scheduledDateTime: scheduledDate,
|
|
174
|
+
description: node.Description__c?.value || "",
|
|
175
|
+
tenantName: node.User__r?.Name?.value || "Unknown",
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Helper function to transform maintenance request data with all fields for maintenance page
|
|
180
|
+
function transformMaintenanceTaskFull(node: any): MaintenanceRequest {
|
|
181
|
+
const scheduledDate = node.Scheduled__c?.value ? new Date(node.Scheduled__c.value) : null;
|
|
182
|
+
const formattedDate = scheduledDate
|
|
183
|
+
? scheduledDate.toLocaleDateString("en-US", { month: "short", day: "numeric" }) +
|
|
184
|
+
", " +
|
|
185
|
+
scheduledDate.toLocaleTimeString("en-US", {
|
|
186
|
+
hour: "numeric",
|
|
187
|
+
minute: "2-digit",
|
|
188
|
+
hour12: true,
|
|
189
|
+
})
|
|
190
|
+
: undefined;
|
|
191
|
+
|
|
192
|
+
// Get image URL from ContentDocumentLinks
|
|
193
|
+
const imageVersionId =
|
|
194
|
+
node.ContentDocumentLinks?.edges?.[0]?.node?.ContentDocument?.LatestPublishedVersionId?.value;
|
|
195
|
+
const imageUrl = imageVersionId
|
|
196
|
+
? `/sfc/servlet.shepherd/version/download/${imageVersionId}`
|
|
197
|
+
: undefined;
|
|
198
|
+
|
|
199
|
+
// Get tenant unit from Property
|
|
200
|
+
const tenantUnit = node.Property__r?.Name?.value || node.Property__r?.Address__c?.value;
|
|
201
|
+
|
|
202
|
+
// Get assigned worker name from Owner
|
|
203
|
+
const assignedWorkerName = node.Owner?.Name?.value;
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
id: node.Id,
|
|
207
|
+
propertyAddress: node.Property__r?.Address__c?.value || "Unknown Address",
|
|
208
|
+
issueType: node.Type__c?.value || "General",
|
|
209
|
+
priority: node.Priority__c?.value?.toLowerCase() || "medium",
|
|
210
|
+
status: node.Status__c?.value?.toLowerCase().replace(" ", "_") || "new",
|
|
211
|
+
assignedWorker: assignedWorkerName,
|
|
212
|
+
scheduledDateTime: scheduledDate?.toLocaleString(),
|
|
213
|
+
description: node.Description__c?.value || "",
|
|
214
|
+
tenantName: node.User__r?.Name?.value || "Unknown",
|
|
215
|
+
imageUrl,
|
|
216
|
+
tenantUnit,
|
|
217
|
+
assignedWorkerName,
|
|
218
|
+
assignedWorkerOrg: "ABC Diamond Technicians", // This would come from a related object in real scenario
|
|
219
|
+
formattedDate,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { executeGraphQL } from "@salesforce/webapp-experimental/api";
|
|
2
|
+
import type { Property } from "../lib/types.js";
|
|
3
|
+
import { gql } from "./utils.js";
|
|
4
|
+
import type {
|
|
5
|
+
GetPropertiesQueryVariables,
|
|
6
|
+
GetPropertiesQuery,
|
|
7
|
+
} from "./graphql-operations-types.js";
|
|
8
|
+
|
|
9
|
+
// GraphQL query to get properties with pagination
|
|
10
|
+
const GET_PROPERTIES_PAGINATED = gql`
|
|
11
|
+
query GetProperties($first: Int, $after: String) {
|
|
12
|
+
uiapi {
|
|
13
|
+
query {
|
|
14
|
+
Property__c(first: $first, after: $after, orderBy: { CreatedDate: { order: DESC } }) {
|
|
15
|
+
edges {
|
|
16
|
+
node {
|
|
17
|
+
Id
|
|
18
|
+
Name {
|
|
19
|
+
value
|
|
20
|
+
}
|
|
21
|
+
Address__c {
|
|
22
|
+
value
|
|
23
|
+
}
|
|
24
|
+
Description__c {
|
|
25
|
+
value
|
|
26
|
+
}
|
|
27
|
+
Type__c {
|
|
28
|
+
value
|
|
29
|
+
}
|
|
30
|
+
Status__c {
|
|
31
|
+
value
|
|
32
|
+
}
|
|
33
|
+
Monthly_Rent__c {
|
|
34
|
+
value
|
|
35
|
+
}
|
|
36
|
+
Bedrooms__c {
|
|
37
|
+
value
|
|
38
|
+
}
|
|
39
|
+
Bathrooms__c {
|
|
40
|
+
value
|
|
41
|
+
}
|
|
42
|
+
Sq_Ft__c {
|
|
43
|
+
value
|
|
44
|
+
}
|
|
45
|
+
Year_Built__c {
|
|
46
|
+
value
|
|
47
|
+
}
|
|
48
|
+
Hero_Image__c {
|
|
49
|
+
value
|
|
50
|
+
}
|
|
51
|
+
Deposit__c {
|
|
52
|
+
value
|
|
53
|
+
}
|
|
54
|
+
Parking__c {
|
|
55
|
+
value
|
|
56
|
+
}
|
|
57
|
+
Pet_Friendly__c {
|
|
58
|
+
value
|
|
59
|
+
}
|
|
60
|
+
Available_Date__c {
|
|
61
|
+
value
|
|
62
|
+
}
|
|
63
|
+
Lease_Term__c {
|
|
64
|
+
value
|
|
65
|
+
}
|
|
66
|
+
Features__c {
|
|
67
|
+
value
|
|
68
|
+
}
|
|
69
|
+
Utilities__c {
|
|
70
|
+
value
|
|
71
|
+
}
|
|
72
|
+
Tour_URL__c {
|
|
73
|
+
value
|
|
74
|
+
}
|
|
75
|
+
CreatedDate {
|
|
76
|
+
value
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
pageInfo {
|
|
81
|
+
hasNextPage
|
|
82
|
+
endCursor
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
export interface PropertiesResult {
|
|
91
|
+
properties: Property[];
|
|
92
|
+
pageInfo: {
|
|
93
|
+
hasNextPage: boolean;
|
|
94
|
+
endCursor?: string | null;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Fetch properties with pagination
|
|
99
|
+
export async function getProperties(first: number = 12, after?: string): Promise<PropertiesResult> {
|
|
100
|
+
const variables: GetPropertiesQueryVariables = { first };
|
|
101
|
+
if (after) {
|
|
102
|
+
variables.after = after;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const response = await executeGraphQL<GetPropertiesQuery>(GET_PROPERTIES_PAGINATED, variables);
|
|
106
|
+
const edges = response?.uiapi?.query?.Property__c?.edges || [];
|
|
107
|
+
const pageInfo = response?.uiapi?.query?.Property__c?.pageInfo || {
|
|
108
|
+
hasNextPage: false,
|
|
109
|
+
endCursor: null,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const properties = edges.map((edge) => transformProperty(edge?.node));
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
properties,
|
|
116
|
+
pageInfo,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Helper function to transform property data from GraphQL to Property type
|
|
121
|
+
function transformProperty(node: any): Property {
|
|
122
|
+
// Extract year from CreatedDate for "Since [year]" display
|
|
123
|
+
const createdYear = node.CreatedDate?.value
|
|
124
|
+
? new Date(node.CreatedDate.value).getFullYear().toString()
|
|
125
|
+
: undefined;
|
|
126
|
+
|
|
127
|
+
// Parse multi-picklist values (comma-separated strings to arrays)
|
|
128
|
+
const features = node.Features__c?.value ? node.Features__c.value.split(";") : undefined;
|
|
129
|
+
const utilities = node.Utilities__c?.value ? node.Utilities__c.value.split(";") : undefined;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
id: node.Id,
|
|
133
|
+
name: node.Name?.value || "Unnamed Property",
|
|
134
|
+
address: node.Address__c?.value || "Address not available",
|
|
135
|
+
type:
|
|
136
|
+
(node.Type__c?.value?.toLowerCase() as "apartment" | "house" | "commercial") || "apartment",
|
|
137
|
+
status:
|
|
138
|
+
(node.Status__c?.value?.toLowerCase() as "available" | "rented" | "maintenance") ||
|
|
139
|
+
"available",
|
|
140
|
+
monthlyRent: node.Monthly_Rent__c?.value || 0,
|
|
141
|
+
bedrooms: node.Bedrooms__c?.value,
|
|
142
|
+
bathrooms: node.Bathrooms__c?.value,
|
|
143
|
+
heroImage: node.Hero_Image__c?.value,
|
|
144
|
+
description: node.Description__c?.value,
|
|
145
|
+
sqFt: node.Sq_Ft__c?.value,
|
|
146
|
+
yearBuilt: node.Year_Built__c?.value,
|
|
147
|
+
deposit: node.Deposit__c?.value,
|
|
148
|
+
parking: node.Parking__c?.value,
|
|
149
|
+
petFriendly: node.Pet_Friendly__c?.value,
|
|
150
|
+
availableDate: node.Available_Date__c?.value,
|
|
151
|
+
leaseTerm: node.Lease_Term__c?.value,
|
|
152
|
+
features,
|
|
153
|
+
utilities,
|
|
154
|
+
tourUrl: node.Tour_URL__c?.value,
|
|
155
|
+
createdDate: createdYear,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
|
-
import { Outlet } from
|
|
2
|
-
import
|
|
1
|
+
import { Outlet } from "react-router";
|
|
2
|
+
import { TopBar } from "./components/TopBar.js";
|
|
3
|
+
import { VerticalNav } from "./components/VerticalNav.js";
|
|
3
4
|
|
|
4
5
|
export default function AppLayout() {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
return (
|
|
7
|
+
<div className="flex flex-col h-screen">
|
|
8
|
+
{/* Top Bar */}
|
|
9
|
+
<TopBar />
|
|
10
|
+
|
|
11
|
+
{/* Main Content Area with Sidebar */}
|
|
12
|
+
<div className="flex flex-1 overflow-hidden">
|
|
13
|
+
{/* Vertical Navigation */}
|
|
14
|
+
<VerticalNav />
|
|
15
|
+
|
|
16
|
+
{/* Page Content */}
|
|
17
|
+
<main className="flex-1 overflow-auto">
|
|
18
|
+
<Outlet />
|
|
19
|
+
</main>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
11
23
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version='1.0' encoding='iso-8859-1'?>
|
|
2
|
+
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
3
|
+
<svg fill="#000000" height="800px" width="800px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 463 463" xmlns:xlink="http://www.w3.org/1999/xlink" enable-background="new 0 0 463 463">
|
|
4
|
+
<g>
|
|
5
|
+
<path d="m367.5,0h-272c-21.78,0-39.5,17.72-39.5,39.5v368c0,10.336 6.71,19.128 16,22.266v9.734c0,12.958 10.542,23.5 23.5,23.5h272c12.958,0 23.5-10.542 23.5-23.5v-9.734c9.29-3.138 16-11.93 16-22.266v-368c0-21.78-17.72-39.5-39.5-39.5zm-272,15h272c13.51,0 24.5,10.991 24.5,24.5v56.5h-321v-56.5c0-13.509 10.99-24.5 24.5-24.5zm272,433h-272c-4.687,0-8.5-3.813-8.5-8.5v-8.5h289v8.5c0,4.687-3.813,8.5-8.5,8.5zm16-32h-304c-4.687,0-8.5-3.813-8.5-8.5v-296.5h321v296.5c0,4.687-3.813,8.5-8.5,8.5z"/>
|
|
6
|
+
<path d="M231.5,136C161.196,136,104,193.196,104,263.5S161.196,391,231.5,391S359,333.804,359,263.5S301.804,136,231.5,136z M231.5,376C169.468,376,119,325.533,119,263.5S169.468,151,231.5,151S344,201.467,344,263.5S293.532,376,231.5,376z"/>
|
|
7
|
+
<path d="m279.5,79c12.958,0 23.5-10.542 23.5-23.5s-10.542-23.5-23.5-23.5-23.5,10.542-23.5,23.5 10.542,23.5 23.5,23.5zm0-32c4.687,0 8.5,3.813 8.5,8.5s-3.813,8.5-8.5,8.5-8.5-3.813-8.5-8.5 3.813-8.5 8.5-8.5z"/>
|
|
8
|
+
<path d="m343.5,79c12.958,0 23.5-10.542 23.5-23.5s-10.542-23.5-23.5-23.5-23.5,10.542-23.5,23.5 10.542,23.5 23.5,23.5zm0-32c4.687,0 8.5,3.813 8.5,8.5s-3.813,8.5-8.5,8.5-8.5-3.813-8.5-8.5 3.813-8.5 8.5-8.5z"/>
|
|
9
|
+
<path d="m111.5,79h104c8.547,0 15.5-6.953 15.5-15.5v-16c0-8.547-6.953-15.5-15.5-15.5h-104c-8.547,0-15.5,6.953-15.5,15.5v16c0,8.547 6.953,15.5 15.5,15.5zm-.5-31.5c0-0.276 0.225-0.5 0.5-0.5h104c0.275,0 0.5,0.224 0.5,0.5v16c0,0.276-0.225,0.5-0.5,0.5h-104c-0.275,0-0.5-0.224-0.5-0.5v-16z"/>
|
|
10
|
+
<path d="m231.5,168c-52.659,0-95.5,42.841-95.5,95.5s42.841,95.5 95.5,95.5 95.5-42.841 95.5-95.5-42.841-95.5-95.5-95.5zm0,176c-44.388,0-80.5-36.112-80.5-80.5s36.112-80.5 80.5-80.5 80.5,36.112 80.5,80.5-36.112,80.5-80.5,80.5z"/>
|
|
11
|
+
<path d="m231.5,200c-4.143,0-7.5,3.358-7.5,7.5s3.357,7.5 7.5,7.5c26.743,0 48.5,21.757 48.5,48.5 0,4.142 3.357,7.5 7.5,7.5s7.5-3.358 7.5-7.5c0-35.014-28.486-63.5-63.5-63.5z"/>
|
|
12
|
+
</g>
|
|
13
|
+
</svg>
|
package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/dashboard.svg
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 21C7.125 21 3 16.875 3 12C3 8.89234 4.67627 6.08945 7.15451 4.46562C8.48099 3.59646 10.0372 3.0651 11.6892 3.00559L12 3V6.75C9.10051 6.75 6.75 9.10051 6.75 12C6.75 14.8995 9.10051 17.25 12 17.25C14.8391 17.25 17.1518 14.9964 17.247 12.1805L17.25 12H21C21 16.8 17.0009 20.8729 12.2245 20.9971L12 21Z" stroke="#65185C" stroke-width="2" stroke-linecap="round"/>
|
|
3
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.0006 7.69119L15.0003 3.5128C16.2044 3.91976 17.3294 4.62383 18.3753 5.625C19.3399 6.54825 20.0166 7.5898 20.4054 8.74965L20.4844 9L16.3089 8.9994C15.9535 8.49 15.51 8.04657 15.0006 7.69119Z" fill="#65185C" stroke="#65185C" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
</svg>
|
package/dist/force-app/main/default/webapplications/appreactsampleb2e/src/assets/icons/downgraph.svg
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.2117 8.64608C11.1612 8.76858 11.0637 8.86608 10.9412 8.91658C10.8802 8.94208 10.8152 8.95508 10.7502 8.95508H8.2957C8.0192 8.95508 7.7957 8.73158 7.7957 8.45508C7.7957 8.17858 8.0192 7.95508 8.2957 7.95508H9.5427L6.8637 5.2771L5.1722 6.9691C4.98469 7.15658 4.65319 7.15658 4.46519 6.9691L1.39619 3.8986C1.20119 3.7031 1.20119 3.3866 1.39669 3.1916C1.49419 3.0941 1.62219 3.0451 1.75019 3.0451C1.87819 3.0451 2.00619 3.0941 2.10369 3.1916L4.81869 5.9081L6.5097 4.2166C6.7052 4.0211 7.0217 4.0211 7.2172 4.2166L10.2502 7.24858V6.0001C10.2502 5.7236 10.4737 5.5001 10.7502 5.5001C11.0267 5.5001 11.2502 5.7236 11.2502 6.0001V8.45508C11.2502 8.52008 11.2367 8.58508 11.2117 8.64608Z" fill="#114C50"/>
|
|
3
|
+
</svg>
|