@salesforce/webapp-template-app-react-sample-b2x-experimental 1.116.4 → 1.116.6

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.
@@ -101,6 +101,14 @@ const PROPERTY_QUERY = gql`
101
101
  value
102
102
  displayValue
103
103
  }
104
+ Coordinates__Latitude__s @optional {
105
+ value
106
+ displayValue
107
+ }
108
+ Coordinates__Longitude__s @optional {
109
+ value
110
+ displayValue
111
+ }
104
112
  Type__c @optional {
105
113
  value
106
114
  displayValue
@@ -143,6 +151,10 @@ export interface PropertyDetail {
143
151
  bathrooms: number | string | null;
144
152
  squareFootage: number | string | null;
145
153
  description: string | null;
154
+ coordinates: {
155
+ lat: number | null;
156
+ lng: number | null;
157
+ } | null;
146
158
  }
147
159
 
148
160
  export async function fetchPropertyById(propertyId: string): Promise<PropertyDetail | null> {
@@ -178,6 +190,23 @@ export async function fetchPropertyById(propertyId: string): Promise<PropertyDet
178
190
  node.Description__c?.value != null
179
191
  ? String(node.Description__c.value)
180
192
  : (node.Description__c?.displayValue ?? null),
193
+ coordinates:
194
+ node.Coordinates__Latitude__s && node.Coordinates__Longitude__s
195
+ ? {
196
+ lat:
197
+ typeof node.Coordinates__Latitude__s.value === "number"
198
+ ? node.Coordinates__Latitude__s.value
199
+ : node.Coordinates__Latitude__s.displayValue != null
200
+ ? Number(node.Coordinates__Latitude__s.displayValue)
201
+ : null,
202
+ lng:
203
+ typeof node.Coordinates__Longitude__s.value === "number"
204
+ ? node.Coordinates__Longitude__s.value
205
+ : node.Coordinates__Longitude__s.displayValue != null
206
+ ? Number(node.Coordinates__Longitude__s.displayValue)
207
+ : null,
208
+ }
209
+ : null,
181
210
  };
182
211
  }
183
212
 
@@ -28,6 +28,8 @@ type PropertyListingNode = {
28
28
  Property__r?: {
29
29
  Name?: { value?: string | null; displayValue?: string | null } | null;
30
30
  Address__c?: { value?: string | null; displayValue?: string | null } | null;
31
+ Coordinates__Latitude__s?: { value?: number | null; displayValue?: string | null } | null;
32
+ Coordinates__Longitude__s?: { value?: number | null; displayValue?: string | null } | null;
31
33
  Bedrooms__c?: { value?: number | null; displayValue?: string | null } | null;
32
34
  } | null;
33
35
  };
@@ -83,6 +85,18 @@ function nodeToSearchResultRecordData(node: PropertyListingNode): SearchResultRe
83
85
  prop.Bedrooms__c as { value?: unknown; displayValue?: string | null },
84
86
  );
85
87
  }
88
+ const lat = prop?.Coordinates__Latitude__s;
89
+ if (lat != null && typeof lat === "object" && "value" in lat) {
90
+ fields["Property__r.Coordinates__Latitude__s"] = nodeToFieldValue(
91
+ lat as { value?: unknown; displayValue?: string | null },
92
+ );
93
+ }
94
+ const lng = prop?.Coordinates__Longitude__s;
95
+ if (lng != null && typeof lng === "object" && "value" in lng) {
96
+ fields["Property__r.Coordinates__Longitude__s"] = nodeToFieldValue(
97
+ lng as { value?: unknown; displayValue?: string | null },
98
+ );
99
+ }
86
100
  return {
87
101
  id: node.Id,
88
102
  apiName: typeof node.ApiName === "string" ? node.ApiName : OBJECT_API_NAME,
@@ -134,6 +148,14 @@ const PROPERTY_LISTINGS_QUERY = gql`
134
148
  value
135
149
  displayValue
136
150
  }
151
+ Coordinates__Latitude__s @optional {
152
+ value
153
+ displayValue
154
+ }
155
+ Coordinates__Longitude__s @optional {
156
+ value
157
+ displayValue
158
+ }
137
159
  Bedrooms__c @optional {
138
160
  value
139
161
  displayValue
@@ -2,7 +2,6 @@ import { createBrowserRouter, RouterProvider } from "react-router";
2
2
  import { routes } from "@/routes";
3
3
  import { StrictMode, Component, type ReactNode } from "react";
4
4
  import { createRoot } from "react-dom/client";
5
- import { AuthProvider } from "./features/authentication/context/AuthContext";
6
5
  import "./styles/global.css";
7
6
 
8
7
  class ErrorBoundary extends Component<
@@ -37,9 +36,7 @@ if (rootEl) {
37
36
  createRoot(rootEl).render(
38
37
  <StrictMode>
39
38
  <ErrorBoundary>
40
- <AuthProvider>
41
- <RouterProvider router={router} />
42
- </AuthProvider>
39
+ <RouterProvider router={router} />
43
40
  </ErrorBoundary>
44
41
  </StrictMode>,
45
42
  );
@@ -19,6 +19,26 @@ function getListingName(record: {
19
19
  return "Property";
20
20
  }
21
21
 
22
+ function toFiniteNumber(value: unknown): number | null {
23
+ if (typeof value === "number" && Number.isFinite(value)) return value;
24
+ if (typeof value === "string" && value.trim() !== "") {
25
+ const n = Number(value);
26
+ return Number.isFinite(n) ? n : null;
27
+ }
28
+ return null;
29
+ }
30
+
31
+ function getCoordinatesFromRecord(record: {
32
+ fields?: Record<string, { value?: unknown }>;
33
+ }): { lat: number; lng: number } | null {
34
+ const latRaw = record.fields?.["Property__r.Coordinates__Latitude__s"]?.value;
35
+ const lngRaw = record.fields?.["Property__r.Coordinates__Longitude__s"]?.value;
36
+ const lat = toFiniteNumber(latRaw);
37
+ const lng = toFiniteNumber(lngRaw);
38
+ if (lat == null || lng == null) return null;
39
+ return { lat, lng };
40
+ }
41
+
22
42
  /** Round to 5 decimals (~1 m) so near-duplicate coords group together */
23
43
  function key(lat: number, lng: number): string {
24
44
  return `${lat.toFixed(5)},${lng.toFixed(5)}`;
@@ -84,14 +104,39 @@ export function usePropertyMapMarkers(results: SearchResultRecord[]): {
84
104
  let cancelled = false;
85
105
  setLoading(true);
86
106
  const uniqIds = [...new Set(propertyIds)];
87
- fetchPropertyAddresses(uniqIds)
107
+ const directMarkers: MapMarker[] = [];
108
+ const missingIds: string[] = [];
109
+ for (const r of results) {
110
+ if (!r?.record) continue;
111
+ const id = getPropertyIdFromRecord(r.record);
112
+ if (!id || !uniqIds.includes(id)) continue;
113
+ const coords = getCoordinatesFromRecord(r.record);
114
+ if (coords) {
115
+ directMarkers.push({
116
+ lat: coords.lat,
117
+ lng: coords.lng,
118
+ label: propertyIdToLabel.get(id) ?? "Property",
119
+ propertyId: id,
120
+ });
121
+ }
122
+ }
123
+ for (const id of uniqIds) {
124
+ const hasDirect = directMarkers.some((m) => m.propertyId === id);
125
+ if (!hasDirect) missingIds.push(id);
126
+ }
127
+ if (missingIds.length === 0) {
128
+ setMarkers(spreadDuplicateMarkers(directMarkers));
129
+ setLoading(false);
130
+ return;
131
+ }
132
+ fetchPropertyAddresses(missingIds)
88
133
  .then((idToAddress) => {
89
134
  if (cancelled) return;
90
135
  const toGeocode = Object.entries(idToAddress).filter(
91
136
  ([, addr]) => addr != null && addr.trim() !== "",
92
137
  );
93
138
  if (toGeocode.length === 0) {
94
- setMarkers([]);
139
+ setMarkers(spreadDuplicateMarkers(directMarkers));
95
140
  setLoading(false);
96
141
  return;
97
142
  }
@@ -111,7 +156,7 @@ export function usePropertyMapMarkers(results: SearchResultRecord[]): {
111
156
  )
112
157
  .then((resolved) => {
113
158
  if (cancelled) return;
114
- const raw: MapMarker[] = resolved
159
+ const geocoded: MapMarker[] = resolved
115
160
  .filter((r): r is { id: string; coords: { lat: number; lng: number } } => r != null)
116
161
  .map(({ id, coords }) => ({
117
162
  lat: coords.lat,
@@ -119,7 +164,7 @@ export function usePropertyMapMarkers(results: SearchResultRecord[]): {
119
164
  label: propertyIdToLabel.get(id) ?? "Property",
120
165
  propertyId: id,
121
166
  }));
122
- setMarkers(spreadDuplicateMarkers(raw));
167
+ setMarkers(spreadDuplicateMarkers([...directMarkers, ...geocoded]));
123
168
  })
124
169
  .catch(() => {
125
170
  if (!cancelled) setMarkers([]);
@@ -5,6 +5,7 @@ import { Skeleton } from "@/components/ui/skeleton";
5
5
  import PropertyMap from "@/components/properties/PropertyMap";
6
6
  import { usePropertyDetail } from "@/hooks/usePropertyDetail";
7
7
  import { useGeocode } from "@/hooks/useGeocode";
8
+ import type { GeocodeResult } from "@/utils/geocode";
8
9
 
9
10
  function formatCurrency(val: number | string | null): string {
10
11
  if (val == null) return "—";
@@ -108,7 +109,12 @@ export default function PropertyDetails() {
108
109
  const { id } = useParams<{ id: string }>();
109
110
  const { listing, property, images, costs, features, loading, error } = usePropertyDetail(id);
110
111
  const addressForGeocode = property?.address?.replace(/\n/g, ", ") ?? null;
111
- const { coords: addressCoords } = useGeocode(addressForGeocode);
112
+ // Always call hook in the same order; disable geocoding when coordinates already exist.
113
+ const { coords: geocodedCoords } = useGeocode(property?.coordinates ? null : addressForGeocode);
114
+ const addressCoords: GeocodeResult | null =
115
+ property?.coordinates?.lat != null && property?.coordinates?.lng != null
116
+ ? { lat: property.coordinates.lat, lng: property.coordinates.lng }
117
+ : geocodedCoords;
112
118
 
113
119
  if (loading) {
114
120
  return <PropertyDetailsSkeleton />;
@@ -3,6 +3,7 @@ import Home from '@/pages/Home';
3
3
  import NotFound from '@/pages/NotFound';
4
4
  import AccountSearch from "./features/object-search/__examples__/pages/AccountSearch";
5
5
  import AccountObjectDetail from "./features/object-search/__examples__/pages/AccountObjectDetailPage";
6
+ import AuthAppLayout from "./features/authentication/layouts/AuthAppLayout";
6
7
  import Login from "./features/authentication/pages/Login";
7
8
  import Register from "./features/authentication/pages/Register";
8
9
  import ForgotPassword from "./features/authentication/pages/ForgotPassword";
@@ -12,7 +13,6 @@ import ChangePassword from "./features/authentication/pages/ChangePassword";
12
13
  import AuthenticationRoute from "./features/authentication/layouts/authenticationRouteLayout";
13
14
  import PrivateRoute from "./features/authentication/layouts/privateRouteLayout";
14
15
  import { ROUTES } from "./features/authentication/authenticationConfig";
15
- import AppLayout from "@/appLayout";
16
16
  import Dashboard from "@/pages/Dashboard";
17
17
  import Maintenance from "@/pages/Maintenance";
18
18
  import PropertySearch from "@/pages/PropertySearch";
@@ -23,7 +23,7 @@ import Contact from "@/pages/Contact";
23
23
  export const routes: RouteObject[] = [
24
24
  {
25
25
  path: "/",
26
- element: <AppLayout />,
26
+ element: <AuthAppLayout />,
27
27
  children: [
28
28
  {
29
29
  index: true,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-base-sfdx-project-experimental",
3
- "version": "1.116.4",
3
+ "version": "1.116.6",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@salesforce/webapp-template-base-sfdx-project-experimental",
9
- "version": "1.116.4",
9
+ "version": "1.116.6",
10
10
  "license": "SEE LICENSE IN LICENSE.txt",
11
11
  "devDependencies": {
12
12
  "@lwc/eslint-plugin-lwc": "^3.3.0",
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-base-sfdx-project-experimental",
3
- "version": "1.116.4",
3
+ "version": "1.116.6",
4
4
  "description": "Base SFDX project template",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "publishConfig": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-app-react-sample-b2x-experimental",
3
- "version": "1.116.4",
3
+ "version": "1.116.6",
4
4
  "description": "Salesforce sample property rental React app",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "author": "",