@salesforce/webapp-template-app-react-sample-b2x-experimental 1.116.7 → 1.116.9

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.
Files changed (53) hide show
  1. package/dist/CHANGELOG.md +16 -0
  2. package/dist/force-app/main/default/webapplications/propertyrentalapp/eslint.config.js +13 -2
  3. package/dist/force-app/main/default/webapplications/propertyrentalapp/package.json +3 -3
  4. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/graphql-operations-types.ts +24594 -7234
  5. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/maintenanceRequests/maintenanceRequestApi.ts +21 -157
  6. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/maintenanceRequests/query/maintenanceRequests.graphql +60 -0
  7. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/properties/propertyDetailGraphQL.ts +45 -444
  8. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/properties/propertyNodeUtils.ts +29 -0
  9. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/properties/propertySearchService.ts +56 -0
  10. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/properties/query/distinctPropertyStatus.graphql +19 -0
  11. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/properties/query/distinctPropertyType.graphql +19 -0
  12. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/properties/query/listingById.graphql +29 -0
  13. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/properties/query/propertyAddressesByIds.graphql +17 -0
  14. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/properties/query/propertyDetailById.graphql +124 -0
  15. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/properties/query/searchProperties.graphql +85 -0
  16. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/appLayout.tsx +1 -1
  17. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/components/SkeletonPrimitives.tsx +9 -6
  18. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/components/dashboard/WeatherWidget.tsx +35 -19
  19. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/components/maintenanceRequests/MaintenanceRequestList.tsx +7 -5
  20. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/components/maintenanceRequests/MaintenanceRequestListItem.tsx +11 -10
  21. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/components/maintenanceRequests/MaintenanceSummaryDetailsModal.tsx +20 -15
  22. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/components/properties/PropertyListingCard.tsx +11 -24
  23. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/components/properties/PropertyMap.tsx +2 -1
  24. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/features/authentication/context/AuthContext.tsx +1 -1
  25. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/features/authentication/hooks/useCountdownTimer.ts +1 -1
  26. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/features/authentication/pages/Profile.tsx +3 -3
  27. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/features/authentication/pages/Register.tsx +1 -1
  28. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/features/authentication/sessionTimeout/SessionTimeoutValidator.tsx +12 -18
  29. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/features/object-search/components/FilterContext.tsx +1 -1
  30. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/features/object-search/hooks/useObjectSearchParams.ts +10 -5
  31. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/hooks/useGeocode.ts +23 -38
  32. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/hooks/useMaintenanceRequests.ts +29 -25
  33. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/hooks/usePropertyDetail.ts +42 -78
  34. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/hooks/usePropertyMapMarkers.ts +34 -41
  35. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/hooks/useWeather.ts +14 -30
  36. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/pages/Application.tsx +41 -74
  37. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/pages/Contact.tsx +44 -55
  38. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/pages/Dashboard.tsx +1 -0
  39. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/pages/Home.tsx +63 -32
  40. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/pages/Maintenance.tsx +97 -8
  41. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/pages/PropertyDetails.tsx +67 -45
  42. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/pages/PropertySearch.tsx +299 -191
  43. package/dist/package-lock.json +2 -2
  44. package/dist/package.json +1 -1
  45. package/package.json +4 -1
  46. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/properties/propertyListingGraphQL.ts +0 -380
  47. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/components/properties/PropertyListingSearchPagination.tsx +0 -136
  48. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/constants/propertyListing.ts +0 -4
  49. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/hooks/usePropertyAddresses.ts +0 -45
  50. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/hooks/usePropertyListingAmenities.ts +0 -57
  51. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/hooks/usePropertyListingSearch.ts +0 -84
  52. package/dist/force-app/main/default/webapplications/propertyrentalapp/src/hooks/usePropertyPrimaryImages.ts +0 -53
  53. /package/dist/force-app/main/default/webapplications/propertyrentalapp/src/api/{leadApi.ts → leads/leadApi.ts} +0 -0
@@ -0,0 +1,124 @@
1
+ query PropertyDetailById($propertyId: ID!) {
2
+ uiapi {
3
+ query {
4
+ Property__c(where: { Id: { eq: $propertyId } }, first: 1) {
5
+ edges {
6
+ node {
7
+ Id
8
+ Name @optional {
9
+ value
10
+ displayValue
11
+ }
12
+ Address__c @optional {
13
+ value
14
+ displayValue
15
+ }
16
+ Coordinates__Latitude__s @optional {
17
+ value
18
+ }
19
+ Coordinates__Longitude__s @optional {
20
+ value
21
+ }
22
+ Type__c @optional {
23
+ value
24
+ displayValue
25
+ }
26
+ Monthly_Rent__c @optional {
27
+ value
28
+ displayValue
29
+ }
30
+ Bedrooms__c @optional {
31
+ value
32
+ displayValue
33
+ }
34
+ Bathrooms__c @optional {
35
+ value
36
+ displayValue
37
+ }
38
+ Sq_Ft__c @optional {
39
+ value
40
+ displayValue
41
+ }
42
+ Description__c @optional {
43
+ value
44
+ displayValue
45
+ }
46
+ Property_Images__r(first: 50, orderBy: { Display_Order__c: { order: ASC } }) {
47
+ edges {
48
+ node {
49
+ Id
50
+ Name @optional {
51
+ value
52
+ }
53
+ Image_URL__c @optional {
54
+ value
55
+ }
56
+ Image_Type__c @optional {
57
+ value
58
+ }
59
+ Display_Order__c @optional {
60
+ value
61
+ }
62
+ Alt_Text__c @optional {
63
+ value
64
+ }
65
+ }
66
+ }
67
+ }
68
+ Property_Features__r(first: 100) {
69
+ edges {
70
+ node {
71
+ Id
72
+ Name @optional {
73
+ value
74
+ }
75
+ Feature_Category__c @optional {
76
+ value
77
+ }
78
+ Description__c @optional {
79
+ value
80
+ }
81
+ }
82
+ }
83
+ }
84
+ Property_Costs__r(first: 100) {
85
+ edges {
86
+ node {
87
+ Id
88
+ Cost_Category__c @optional {
89
+ value
90
+ }
91
+ Cost_Amount__c @optional {
92
+ value
93
+ }
94
+ Cost_Date__c @optional {
95
+ value
96
+ }
97
+ Description__c @optional {
98
+ value
99
+ }
100
+ }
101
+ }
102
+ }
103
+ Property_Listings__r(first: 10) {
104
+ edges {
105
+ node {
106
+ Id
107
+ Name @optional {
108
+ value
109
+ }
110
+ Listing_Price__c @optional {
111
+ value
112
+ }
113
+ Listing_Status__c @optional {
114
+ value
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
@@ -0,0 +1,85 @@
1
+ query SearchProperties(
2
+ $first: Int
3
+ $after: String
4
+ $where: Property__c_Filter
5
+ $orderBy: Property__c_OrderBy
6
+ ) {
7
+ uiapi {
8
+ query {
9
+ Property__c(first: $first, after: $after, where: $where, orderBy: $orderBy) {
10
+ edges {
11
+ node {
12
+ Id
13
+ Name {
14
+ value
15
+ displayValue
16
+ }
17
+ Address__c {
18
+ value
19
+ displayValue
20
+ }
21
+ Status__c {
22
+ value
23
+ displayValue
24
+ }
25
+ Type__c {
26
+ value
27
+ displayValue
28
+ }
29
+ Monthly_Rent__c {
30
+ value
31
+ displayValue
32
+ }
33
+ Bedrooms__c {
34
+ value
35
+ displayValue
36
+ }
37
+ Coordinates__Latitude__s {
38
+ value
39
+ }
40
+ Coordinates__Longitude__s {
41
+ value
42
+ }
43
+ CreatedDate {
44
+ value
45
+ displayValue
46
+ }
47
+ Property_Images__r(first: 5, orderBy: { Display_Order__c: { order: ASC } }) {
48
+ edges {
49
+ node {
50
+ Id
51
+ Image_URL__c {
52
+ value
53
+ }
54
+ Image_Type__c {
55
+ value
56
+ }
57
+ Display_Order__c {
58
+ value
59
+ }
60
+ }
61
+ }
62
+ }
63
+ Property_Features__r(first: 20) {
64
+ edges {
65
+ node {
66
+ Id
67
+ Description__c {
68
+ value
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ pageInfo {
76
+ hasNextPage
77
+ hasPreviousPage
78
+ endCursor
79
+ startCursor
80
+ }
81
+ totalCount
82
+ }
83
+ }
84
+ }
85
+ }
@@ -8,7 +8,7 @@ export default function AppLayout() {
8
8
  const [isNavOpen, setIsNavOpen] = useState(false);
9
9
 
10
10
  return (
11
- <div className="flex flex-col">
11
+ <div className="flex min-h-screen flex-col bg-gray-50">
12
12
  <Toaster />
13
13
  <TopBar onMenuClick={() => setIsNavOpen(true)} />
14
14
 
@@ -19,13 +19,16 @@ export function SkeletonListRows({ count = 3 }: { count?: number }) {
19
19
  return (
20
20
  <>
21
21
  {Array.from({ length: count }, (_, i) => (
22
- <div key={i} className="flex items-center gap-4 rounded-lg bg-gray-50 p-4">
23
- <Skeleton className="size-10 shrink-0 rounded-full" />
24
- <div className="min-w-0 flex-1 space-y-2">
25
- <Skeleton className="h-4 w-3/4" />
26
- <Skeleton className="h-3 w-1/2" />
22
+ <div key={i} className="flex items-center rounded-lg bg-gray-50 p-4">
23
+ <Skeleton className="h-12 w-12 shrink-0 rounded-lg" />
24
+ <div className="ml-4 min-w-0 grow space-y-1">
25
+ <div className="flex items-center gap-2">
26
+ <Skeleton className="h-5 w-24" />
27
+ <Skeleton className="h-4 w-20" />
28
+ </div>
29
+ <Skeleton className="h-5 w-3/5" />
27
30
  </div>
28
- <Skeleton className="h-6 w-16 shrink-0 rounded-full" />
31
+ <Skeleton className="ml-4 h-7 w-24 shrink-0 rounded-full" />
29
32
  </div>
30
33
  ))}
31
34
  </>
@@ -52,40 +52,56 @@ const Divider = () => <div className="my-5 border-t border-gray-200" />;
52
52
 
53
53
  function WeatherSkeleton() {
54
54
  return (
55
- <div className="mt-5 space-y-5" aria-hidden="true">
56
- <Skeleton className="h-4 w-32" />
57
- <div className="flex items-center justify-between">
58
- <div className="space-y-2">
59
- <Skeleton className="h-4 w-20" />
60
- <Skeleton className="h-14 w-32" />
55
+ <div className="mt-5" aria-hidden="true">
56
+ {/* CurrentConditions: date row + city */}
57
+ <div className="flex items-baseline justify-between">
58
+ <Skeleton className="h-5 w-32" />
59
+ <Skeleton className="h-5 w-24" />
60
+ </div>
61
+
62
+ {/* CurrentConditions: description + temperature + icon */}
63
+ <div className="mt-2 flex items-center justify-between">
64
+ <div>
65
+ <Skeleton className="h-5 w-24" />
66
+ <Skeleton className="mt-1 h-[72px] w-36" />
61
67
  </div>
62
68
  <Skeleton className="h-20 w-20 rounded-full" />
63
69
  </div>
64
- <div className="border-t border-gray-200" />
65
- <div className="grid grid-cols-3 gap-2">
70
+
71
+ {/* Divider */}
72
+ <div className="my-5 border-t border-gray-200" />
73
+
74
+ {/* CurrentConditions: stats grid */}
75
+ <div className="grid grid-cols-3 gap-2 text-center">
66
76
  {[0, 1, 2].map((i) => (
67
- <div key={i} className="flex flex-col items-center gap-1.5">
77
+ <div key={i} className="flex flex-col items-center gap-1">
68
78
  <Skeleton className="h-5 w-5 rounded-full" />
69
- <Skeleton className="h-4 w-14" />
70
- <Skeleton className="h-3 w-10" />
79
+ <Skeleton className="h-5 w-16" />
80
+ <Skeleton className="h-4 w-12" />
71
81
  </div>
72
82
  ))}
73
83
  </div>
74
- <div className="border-t border-gray-200" />
84
+
85
+ {/* Divider */}
86
+ <div className="my-5 border-t border-gray-200" />
87
+
88
+ {/* ForecastTabs */}
75
89
  <div className="flex gap-6">
76
- <Skeleton className="h-4 w-12" />
77
- <Skeleton className="h-4 w-16" />
78
- <Skeleton className="h-4 w-20" />
90
+ <Skeleton className="h-5 w-12" />
91
+ <Skeleton className="h-5 w-16" />
92
+ <Skeleton className="h-5 w-20" />
79
93
  </div>
80
- <div className="flex gap-3">
94
+
95
+ {/* HourlyForecast */}
96
+ <div className="mt-4 flex gap-3 overflow-x-auto pb-1">
81
97
  {[0, 1, 2, 3].map((i) => (
82
98
  <div
83
99
  key={i}
84
- className="flex w-[70px] flex-col items-center gap-1.5 rounded-2xl border border-gray-100 px-3 py-3"
100
+ className="flex min-w-[70px] flex-col items-center gap-1.5 rounded-2xl border border-gray-100 bg-gray-50/80 px-3 py-3"
85
101
  >
86
- <Skeleton className="h-3 w-10" />
102
+ <Skeleton className="h-4 w-10" />
87
103
  <Skeleton className="h-5 w-5 rounded-full" />
88
- <Skeleton className="h-4 w-8" />
104
+ <Skeleton className="h-5 w-8" />
89
105
  </div>
90
106
  ))}
91
107
  </div>
@@ -1,14 +1,15 @@
1
1
  import { useState } from "react";
2
- import type { MaintenanceRequestSummary } from "@/api/maintenanceRequests/maintenanceRequestApi";
2
+ import type { MaintenanceRequestNode } from "@/api/maintenanceRequests/maintenanceRequestApi";
3
3
  import MaintenanceRequestListItem from "@/components/maintenanceRequests/MaintenanceRequestListItem";
4
4
  import MaintenanceSummaryDetailsModal from "@/components/maintenanceRequests/MaintenanceSummaryDetailsModal";
5
5
  import { SkeletonListRows } from "@/components/SkeletonPrimitives";
6
6
 
7
7
  interface MaintenanceRequestListProps {
8
- requests: MaintenanceRequestSummary[];
8
+ requests: MaintenanceRequestNode[];
9
9
  loading: boolean;
10
10
  error: string | null;
11
11
  emptyMessage?: string;
12
+ skeletonCount?: number;
12
13
  }
13
14
 
14
15
  export default function MaintenanceRequestList({
@@ -16,8 +17,9 @@ export default function MaintenanceRequestList({
16
17
  loading,
17
18
  error,
18
19
  emptyMessage = "No maintenance requests",
20
+ skeletonCount = 3,
19
21
  }: MaintenanceRequestListProps) {
20
- const [selectedRequest, setSelectedRequest] = useState<MaintenanceRequestSummary | null>(null);
22
+ const [selectedRequest, setSelectedRequest] = useState<MaintenanceRequestNode | null>(null);
21
23
 
22
24
  return (
23
25
  <>
@@ -27,7 +29,7 @@ export default function MaintenanceRequestList({
27
29
  onClose={() => setSelectedRequest(null)}
28
30
  />
29
31
  )}
30
- {loading && <SkeletonListRows count={3} />}
32
+ {loading && <SkeletonListRows count={skeletonCount} />}
31
33
  {error && (
32
34
  <p className="py-4 text-sm text-destructive" role="alert">
33
35
  {error}
@@ -40,7 +42,7 @@ export default function MaintenanceRequestList({
40
42
  !error &&
41
43
  requests.map((request) => (
42
44
  <MaintenanceRequestListItem
43
- key={request.id}
45
+ key={request.Id}
44
46
  request={request}
45
47
  onClick={setSelectedRequest}
46
48
  />
@@ -2,25 +2,26 @@
2
2
  * Single maintenance request row: icon (teal) | Type & address + title | tenant (gray circle) [| status].
3
3
  */
4
4
  import { useCallback } from "react";
5
- import type { MaintenanceRequestSummary } from "@/api/maintenanceRequests/maintenanceRequestApi";
5
+ import type { MaintenanceRequestNode } from "@/api/maintenanceRequests/maintenanceRequestApi";
6
6
  import { MaintenanceRequestIcon } from "@/components/maintenanceRequests/MaintenanceRequestIcon";
7
7
  import { StatusBadge } from "@/components/maintenanceRequests/StatusBadge";
8
8
 
9
9
  export interface MaintenanceRequestListItemProps {
10
- request: MaintenanceRequestSummary;
10
+ request: MaintenanceRequestNode;
11
11
  /** When set, row is clickable and opens details (e.g. modal). */
12
- onClick?: (request: MaintenanceRequestSummary) => void;
12
+ onClick?: (request: MaintenanceRequestNode) => void;
13
13
  }
14
14
 
15
15
  export default function MaintenanceRequestListItem({
16
16
  request,
17
17
  onClick,
18
18
  }: MaintenanceRequestListItemProps) {
19
- const issueType = request.type ?? "General";
20
- const addressFirstPart = request.propertyAddress
21
- ? request.propertyAddress.split(",")[0].trim()
22
- : (request.name ?? "");
23
- const title = request.title?.trim() || request.name?.trim() || "—";
19
+ const issueType = request.Type__c?.value ?? "General";
20
+ const propertyAddress = request.Property__r?.Address__c?.value ?? null;
21
+ const addressFirstPart = propertyAddress
22
+ ? propertyAddress.split(",")[0].trim()
23
+ : (request.Name?.value ?? "—");
24
+ const title = request.Description__c?.value?.trim() || request.Name?.value?.trim() || "—";
24
25
 
25
26
  const handleClick = useCallback(() => {
26
27
  onClick?.(request);
@@ -51,7 +52,7 @@ export default function MaintenanceRequestListItem({
51
52
  : undefined
52
53
  }
53
54
  >
54
- <MaintenanceRequestIcon type={request.type} />
55
+ <MaintenanceRequestIcon type={request.Type__c?.value ?? null} />
55
56
 
56
57
  {/* Issue Type and Address - Fixed width; title below to save space (avoids clipping) */}
57
58
  <div className="ml-4 min-w-0 grow">
@@ -66,7 +67,7 @@ export default function MaintenanceRequestListItem({
66
67
  </div>
67
68
 
68
69
  <div className="ml-4 flex flex-shrink-0 items-center">
69
- <StatusBadge status={request.status ?? "—"} />
70
+ <StatusBadge status={request.Status__c?.value ?? "—"} />
70
71
  </div>
71
72
  </div>
72
73
  );
@@ -4,10 +4,10 @@
4
4
  import { useEffect } from "react";
5
5
  import { X } from "lucide-react";
6
6
  import { StatusBadge } from "@/components/maintenanceRequests/StatusBadge";
7
- import type { MaintenanceRequestSummary } from "@/api/maintenanceRequests/maintenanceRequestApi";
7
+ import type { MaintenanceRequestNode } from "@/api/maintenanceRequests/maintenanceRequestApi";
8
8
 
9
9
  export interface MaintenanceSummaryDetailsModalProps {
10
- request: MaintenanceRequestSummary;
10
+ request: MaintenanceRequestNode;
11
11
  onClose: () => void;
12
12
  }
13
13
 
@@ -49,35 +49,40 @@ export default function MaintenanceSummaryDetailsModal({
49
49
  className="relative max-h-[90vh] w-full max-w-lg overflow-y-auto rounded-lg bg-white shadow-xl"
50
50
  >
51
51
  <div className="flex items-center justify-between border-b p-4">
52
- <h2 className="text-lg font-semibold">{request.title ?? request.name ?? "Request"}</h2>
52
+ <h2 className="text-lg font-semibold">
53
+ {request.Description__c?.value ?? request.Name?.value ?? "Request"}
54
+ </h2>
53
55
  <button type="button" onClick={onClose} className="text-gray-500 hover:text-gray-800">
54
56
  <X className="h-5 w-5" />
55
57
  </button>
56
58
  </div>
57
59
  <div className="space-y-3 p-4 text-sm">
58
- {request.description && <p className="text-gray-700">{request.description}</p>}
60
+ {request.Description__c?.value && (
61
+ <p className="text-gray-700">{request.Description__c.value}</p>
62
+ )}
59
63
  <div className="flex flex-wrap gap-2">
60
- {request.type && (
61
- <span className="rounded bg-gray-100 px-2 py-0.5">{request.type}</span>
64
+ {request.Type__c?.value && (
65
+ <span className="rounded bg-gray-100 px-2 py-0.5">{request.Type__c.value}</span>
62
66
  )}
63
- {request.priority && (
64
- <span className="rounded bg-gray-100 px-2 py-0.5">{request.priority}</span>
67
+ {request.Priority__c?.value && (
68
+ <span className="rounded bg-gray-100 px-2 py-0.5">{request.Priority__c.value}</span>
65
69
  )}
66
- {request.status && <StatusBadge status={request.status} />}
70
+ {request.Status__c?.value && <StatusBadge status={request.Status__c.value} />}
67
71
  </div>
68
- {request.propertyAddress && (
72
+ {request.Property__r?.Address__c?.value && (
69
73
  <p>
70
- <span className="font-medium">Property:</span> {request.propertyAddress}
74
+ <span className="font-medium">Property:</span> {request.Property__r.Address__c.value}
71
75
  </p>
72
76
  )}
73
- {request.tenantName && (
77
+ {request.User__r?.Name?.value && (
74
78
  <p>
75
- <span className="font-medium">Tenant:</span> {request.tenantName}
79
+ <span className="font-medium">Tenant:</span> {request.User__r.Name.value}
76
80
  </p>
77
81
  )}
78
- {request.dateRequested && (
82
+ {request.Scheduled__c?.value && (
79
83
  <p>
80
- <span className="font-medium">Requested:</span> {formatDate(request.dateRequested)}
84
+ <span className="font-medium">Requested:</span>{" "}
85
+ {formatDate(request.Scheduled__c.value)}
81
86
  </p>
82
87
  )}
83
88
  </div>
@@ -6,20 +6,9 @@ import { useNavigate } from "react-router";
6
6
  import { useCallback, type MouseEvent } from "react";
7
7
  import { Button } from "@/components/ui/button";
8
8
  import { Skeleton } from "@/components/ui/skeleton";
9
- import type { SearchResultRecordData } from "@/types/searchResults.js";
9
+ import type { PropertySearchNode } from "@/api/properties/propertySearchService";
10
10
 
11
- function fieldDisplay(
12
- fields: Record<string, { value?: unknown; displayValue?: string | null }> | undefined,
13
- apiName: string,
14
- ): string | null {
15
- const f = fields?.[apiName];
16
- if (!f || typeof f !== "object") return null;
17
- if (f.displayValue != null && f.displayValue !== "") return String(f.displayValue);
18
- if (f.value != null) return typeof f.value === "object" ? null : String(f.value);
19
- return null;
20
- }
21
-
22
- function formatPrice(val: string | number | null): string {
11
+ function formatPrice(val: string | number | null | undefined): string {
23
12
  if (val == null) return "—";
24
13
  const n = typeof val === "number" ? val : Number(val);
25
14
  if (Number.isNaN(n)) return String(val);
@@ -32,8 +21,8 @@ function formatPrice(val: string | number | null): string {
32
21
  );
33
22
  }
34
23
 
35
- interface PropertyListingCardProps {
36
- record: SearchResultRecordData;
24
+ export interface PropertyListingCardProps {
25
+ node: PropertySearchNode;
37
26
  imageUrl: string | null;
38
27
  address?: string | null;
39
28
  amenities?: string | null;
@@ -65,23 +54,21 @@ export function PropertyListingCardSkeleton() {
65
54
  }
66
55
 
67
56
  export default function PropertyListingCard({
68
- record,
57
+ node,
69
58
  imageUrl,
70
59
  address,
71
60
  amenities,
72
61
  loading = false,
73
62
  }: PropertyListingCardProps) {
74
63
  const navigate = useNavigate();
75
- const name = fieldDisplay(record.fields, "Name") ?? "Untitled";
76
- const price = fieldDisplay(record.fields, "Listing_Price__c");
77
- const propertyRef = fieldDisplay(record.fields, "Property__c");
78
- const bedroomsRaw = fieldDisplay(record.fields, "Property__r.Bedrooms__c");
79
- const bedroomsNum = bedroomsRaw != null && bedroomsRaw !== "" ? Number(bedroomsRaw) : NaN;
64
+ const name = node.Name?.displayValue ?? node.Name?.value ?? "Untitled";
65
+ const price = node.Monthly_Rent__c?.value;
66
+ const bedroomsNum = typeof node.Bedrooms__c?.value === "number" ? node.Bedrooms__c.value : NaN;
80
67
  const bedroomsLabel =
81
68
  !Number.isNaN(bedroomsNum) && bedroomsNum >= 0
82
69
  ? `${bedroomsNum} Bedroom${bedroomsNum !== 1 ? "s" : ""}`
83
70
  : null;
84
- const detailPath = `/property/${record.id}`;
71
+ const detailPath = `/property/${node.Id}`;
85
72
 
86
73
  const handleClick = useCallback(() => {
87
74
  navigate(detailPath);
@@ -101,7 +88,7 @@ export default function PropertyListingCard({
101
88
  return <PropertyListingCardSkeleton />;
102
89
  }
103
90
 
104
- const displayAddress = (address ?? propertyRef ?? "").trim().replace(/\n/g, ", ") || null;
91
+ const displayAddress = (address ?? "").trim().replace(/\n/g, ", ") || null;
105
92
  const amenityLabels = (amenities ?? "")
106
93
  .split(/\s*\|\s*/)
107
94
  .map((s) => s.trim())
@@ -180,7 +167,7 @@ export default function PropertyListingCard({
180
167
  className="mt-4 w-full cursor-pointer rounded-xl bg-primary px-5 py-5 text-lg font-medium transition-colors duration-200 hover:bg-primary/90"
181
168
  onClick={(e: MouseEvent<HTMLButtonElement>) => {
182
169
  e.stopPropagation();
183
- navigate(`/application?listingId=${encodeURIComponent(record.id)}`);
170
+ navigate(`/application?propertyId=${encodeURIComponent(node.Id)}`);
184
171
  }}
185
172
  >
186
173
  Apply
@@ -63,9 +63,10 @@ interface PropertyMapProps {
63
63
 
64
64
  function MapCenterUpdater({ center, zoom = 13 }: { center: [number, number]; zoom?: number }) {
65
65
  const map = useMap() as { setView: (center: [number, number], zoom: number) => void };
66
+ const [lat, lng] = center;
66
67
  useEffect(() => {
67
68
  map.setView(center, zoom);
68
- }, [map, center[0], center[1], zoom]);
69
+ }, [map, center, lat, lng, zoom]);
69
70
  return null;
70
71
  }
71
72
 
@@ -86,7 +86,7 @@ export function useAuth(): AuthContextType {
86
86
  * @returns {User} The authenticated user object
87
87
  * @throws {Error} If not used within AuthProvider or user is not authenticated
88
88
  */
89
- export function getUser(): User {
89
+ export function useUser(): User {
90
90
  const context = useAuth();
91
91
  if (!context.user) {
92
92
  throw new Error("Authenticated context not established");
@@ -107,7 +107,7 @@ function formatAccessibilityAnnouncement(seconds: number): string {
107
107
  // @ts-expect-error - DurationFormat is not yet in TypeScript lib
108
108
  const formatter = new Intl.DurationFormat(navigator.language, { style: "long" });
109
109
  return formatter.format({ minutes, seconds: secs });
110
- } catch (e) {
110
+ } catch {
111
111
  // Fallback to manual formatting
112
112
  }
113
113
  }
@@ -8,7 +8,7 @@ import { useAppForm } from "../hooks/form";
8
8
  import { ROUTES } from "../authenticationConfig";
9
9
  import { emailSchema } from "../authHelpers";
10
10
  import { getErrorMessage } from "../utils/helpers";
11
- import { getUser } from "../context/AuthContext";
11
+ import { useUser } from "../context/AuthContext";
12
12
  import { fetchUserProfile, updateUserProfile } from "../api/userProfileApi";
13
13
 
14
14
  const optionalString = z
@@ -33,7 +33,7 @@ const profileSchema = z.object({
33
33
  type ProfileFormValues = z.infer<typeof profileSchema>;
34
34
 
35
35
  export default function Profile() {
36
- const user = getUser();
36
+ const user = useUser();
37
37
  const [profile, setProfile] = useState<ProfileFormValues | null>(null);
38
38
  const [loadError, setLoadError] = useState<string | null>(null);
39
39
  const [success, setSuccess] = useState(false);
@@ -104,7 +104,7 @@ export default function Profile() {
104
104
  const formData = profileSchema.parse(profile);
105
105
  form.reset(formData);
106
106
  }
107
- }, [profile]);
107
+ }, [profile, form]);
108
108
 
109
109
  if (!profile && !loadError) {
110
110
  return <CardSkeleton contentMaxWidth="md" loadingText="Loading profile…" />;
@@ -46,7 +46,7 @@ export default function Register() {
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
- const { confirmPassword, ...request } = formFieldValues;
49
+ const { confirmPassword: _confirmPassword, ...request } = formFieldValues;
50
50
  const sdk = await createDataSDK();
51
51
  const response = await sdk.fetch!("/services/apexrest/auth/register", {
52
52
  method: "POST",