@salesforce/webapp-template-app-react-sample-b2x-experimental 1.68.0 → 1.69.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/Lease__c.json +13 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/package.json +13 -8
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/applicationApi.ts +78 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/graphqlClient.ts +17 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/index.ts +19 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/leadApi.ts +69 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/maintenanceRequestApi.ts +177 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/objectDetailService.ts +125 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/objectInfoGraphQLService.ts +194 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/objectInfoService.ts +199 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/propertyDetailGraphQL.ts +497 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/propertyListingGraphQL.ts +190 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/recordListGraphQLService.ts +365 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/appLayout.tsx +20 -30
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/FiltersPanel.tsx +375 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/LoadingFallback.tsx +61 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/PropertyListingCard.tsx +164 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/PropertyMap.tsx +113 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/SearchResultCard.tsx +131 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/alerts/status-alert.tsx +45 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailFields.tsx +55 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailForm.tsx +146 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailHeader.tsx +34 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailLayoutSections.tsx +80 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/Section.tsx +108 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/SectionRow.tsx +20 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/UiApiDetailForm.tsx +140 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FieldValueDisplay.tsx +73 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedAddress.tsx +29 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedEmail.tsx +17 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedPhone.tsx +24 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedText.tsx +11 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedUrl.tsx +29 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/index.ts +6 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/filters/FilterField.tsx +54 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/filters/FilterInput.tsx +55 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/filters/FilterSelect.tsx +72 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/forms/filters-form.tsx +114 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/forms/submit-button.tsx +47 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/layout/card-layout.tsx +19 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/ResultCardFields.tsx +71 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/SearchHeader.tsx +31 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/SearchPagination.tsx +144 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/SearchResultsPanel.tsx +197 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/shared/GlobalSearchInput.tsx +114 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/constants.ts +39 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/index.ts +33 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/form.tsx +204 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/index.ts +22 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useGeocode.ts +35 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useMaintenanceRequests.ts +39 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useObjectInfoBatch.ts +65 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useObjectSearchData.ts +395 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyAddresses.ts +36 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyDetail.ts +99 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyListingSearch.ts +75 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyMapMarkers.ts +100 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyPrimaryImages.ts +51 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useRecordDetailLayout.ts +156 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useRecordListGraphQL.ts +135 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useWeather.ts +173 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Application.tsx +263 -76
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Contact.tsx +158 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Dashboard.tsx +137 -65
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/DetailPage.tsx +109 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/GlobalSearch.tsx +229 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Home.tsx +469 -21
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Maintenance.tsx +244 -95
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyDetails.tsx +211 -39
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyListings.tsx +26 -10
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertySearch.tsx +165 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertySearchPlaceholder.tsx +49 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-01.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-02.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-03.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-04.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-05.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-06.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-07.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-08.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-09.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-10.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-11.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-12.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-13.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-14.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-15.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-16.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-17.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-18.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-19.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-20.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-21.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-22.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-23.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-24.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-25.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/routes.tsx +32 -6
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/styles/global.css +23 -63
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/filters/filters.ts +120 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/filters/picklist.ts +32 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/index.ts +4 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/leaflet.d.ts +17 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/objectInfo/objectInfo.ts +166 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/recordDetail/recordDetail.ts +61 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/search/searchResults.ts +229 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/apiUtils.ts +125 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/cacheUtils.ts +76 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/debounce.ts +89 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/fieldUtils.ts +354 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/fieldValueExtractor.ts +67 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/filterUtils.ts +32 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/formDataTransformUtils.ts +260 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/formUtils.ts +142 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/geocode.ts +65 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/graphQLNodeFieldUtils.ts +186 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/graphQLObjectInfoAdapter.ts +319 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/graphQLRecordAdapter.ts +90 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/index.ts +59 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/layoutTransformUtils.ts +236 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/linkUtils.ts +14 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/paginationUtils.ts +49 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/recordUtils.ts +159 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/sanitizationUtils.ts +49 -0
- package/dist/package.json +1 -1
- package/package.json +2 -2
- package/dist/force-app/main/default/classes/MaintenanceRequestListAction.cls +0 -111
- package/dist/force-app/main/default/classes/MaintenanceRequestListAction.cls-meta.xml +0 -6
- package/dist/force-app/main/default/classes/MaintenanceRequestUpdatePriorityAction.cls +0 -93
- package/dist/force-app/main/default/classes/MaintenanceRequestUpdatePriorityAction.cls-meta.xml +0 -6
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyListings.tsx
CHANGED
|
@@ -31,11 +31,23 @@ export default function PropertyListings() {
|
|
|
31
31
|
<Input
|
|
32
32
|
type="text"
|
|
33
33
|
defaultValue="San Francisco, CA"
|
|
34
|
-
className="min-w-[200px] flex-1 flex-[1_1_200px]"
|
|
34
|
+
className="min-w-[200px] flex-1 flex-[1_1_200px] rounded-xl"
|
|
35
35
|
/>
|
|
36
|
-
<Button
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
<Button
|
|
37
|
+
variant="outline"
|
|
38
|
+
className="cursor-pointer rounded-xl transition-colors duration-200"
|
|
39
|
+
>
|
|
40
|
+
Price
|
|
41
|
+
</Button>
|
|
42
|
+
<Button
|
|
43
|
+
variant="outline"
|
|
44
|
+
className="cursor-pointer rounded-xl transition-colors duration-200"
|
|
45
|
+
>
|
|
46
|
+
Beds/Bath
|
|
47
|
+
</Button>
|
|
48
|
+
<Button className="cursor-pointer rounded-xl transition-colors duration-200">
|
|
49
|
+
All Filters
|
|
50
|
+
</Button>
|
|
39
51
|
</div>
|
|
40
52
|
<h2 className="mb-1 text-lg font-semibold text-foreground">
|
|
41
53
|
2 Bedroom Apartments for Rent in San Francisco CA
|
|
@@ -43,13 +55,13 @@ export default function PropertyListings() {
|
|
|
43
55
|
<p className="mb-4 text-sm text-muted-foreground">1,181 Rentals Available</p>
|
|
44
56
|
<div className="space-y-4">
|
|
45
57
|
{listings.map((p) => (
|
|
46
|
-
<Card key={p.name}>
|
|
58
|
+
<Card key={p.name} className="rounded-2xl shadow-md">
|
|
47
59
|
<CardContent className="flex gap-4 p-4">
|
|
48
60
|
<Link
|
|
49
61
|
to={`/property/${p.id}`}
|
|
50
|
-
className="relative block size-[200px] shrink-0 rounded-xl bg-muted"
|
|
62
|
+
className="relative block size-[200px] shrink-0 cursor-pointer rounded-xl bg-muted transition-opacity duration-200 hover:opacity-95"
|
|
51
63
|
>
|
|
52
|
-
<span className="absolute left-2 top-2 rounded-
|
|
64
|
+
<span className="absolute left-2 top-2 rounded-full bg-violet-600 px-2 py-0.5 text-xs font-medium text-white">
|
|
53
65
|
Virtual Tours
|
|
54
66
|
</span>
|
|
55
67
|
</Link>
|
|
@@ -57,7 +69,7 @@ export default function PropertyListings() {
|
|
|
57
69
|
<h3 className="mb-1 text-base font-semibold">
|
|
58
70
|
<Link
|
|
59
71
|
to={`/property/${p.id}`}
|
|
60
|
-
className="text-primary no-underline hover:underline"
|
|
72
|
+
className="cursor-pointer text-primary no-underline transition-colors duration-200 hover:underline"
|
|
61
73
|
>
|
|
62
74
|
{p.name}
|
|
63
75
|
</Link>
|
|
@@ -70,8 +82,12 @@ export default function PropertyListings() {
|
|
|
70
82
|
In Unit Washer & Dryer, Pets Allowed, Fitness Center
|
|
71
83
|
</p>
|
|
72
84
|
<p className="mb-2 text-sm text-primary">{p.phone}</p>
|
|
73
|
-
<Button
|
|
74
|
-
|
|
85
|
+
<Button
|
|
86
|
+
asChild
|
|
87
|
+
size="sm"
|
|
88
|
+
className="cursor-pointer rounded-xl transition-colors duration-200"
|
|
89
|
+
>
|
|
90
|
+
<Link to={`/application?listingId=${encodeURIComponent(p.id)}`}>Apply</Link>
|
|
75
91
|
</Button>
|
|
76
92
|
</div>
|
|
77
93
|
</CardContent>
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertySearch.tsx
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Property Search page – ZENLEASE-style layout.
|
|
3
|
+
* Map ~2/3 left, scrollable listings ~1/3 right; search/filter bar above.
|
|
4
|
+
*/
|
|
5
|
+
import { useState, useCallback, useMemo, useEffect } from "react";
|
|
6
|
+
import { useSearchParams } from "react-router";
|
|
7
|
+
import { DEFAULT_PAGE_SIZE } from "@/constants";
|
|
8
|
+
import { usePropertyListingSearch } from "@/hooks/usePropertyListingSearch";
|
|
9
|
+
import {
|
|
10
|
+
usePropertyPrimaryImages,
|
|
11
|
+
getPropertyIdFromRecord,
|
|
12
|
+
} from "@/hooks/usePropertyPrimaryImages";
|
|
13
|
+
import { usePropertyAddresses } from "@/hooks/usePropertyAddresses";
|
|
14
|
+
import { usePropertyMapMarkers } from "@/hooks/usePropertyMapMarkers";
|
|
15
|
+
import { Input } from "@/components/ui/input";
|
|
16
|
+
import SearchPagination from "@/components/search/SearchPagination";
|
|
17
|
+
import PropertyListingCard from "@/components/PropertyListingCard";
|
|
18
|
+
import PropertyMap from "@/components/PropertyMap";
|
|
19
|
+
import PropertySearchPlaceholder from "./PropertySearchPlaceholder";
|
|
20
|
+
|
|
21
|
+
const MAP_CENTER_SF: [number, number] = [37.7749, -122.4194];
|
|
22
|
+
|
|
23
|
+
export default function PropertySearch() {
|
|
24
|
+
const [searchParams] = useSearchParams();
|
|
25
|
+
const initialSearch = searchParams.get("search") ?? "";
|
|
26
|
+
const [searchQuery, setSearchQuery] = useState(initialSearch);
|
|
27
|
+
const [searchPageSize, setSearchPageSize] = useState(DEFAULT_PAGE_SIZE);
|
|
28
|
+
const [searchPageToken, setSearchPageToken] = useState("0");
|
|
29
|
+
|
|
30
|
+
// Sync from URL when navigating with ?search=... (e.g. from Home "Find Home")
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const q = searchParams.get("search") ?? "";
|
|
33
|
+
setSearchQuery(q);
|
|
34
|
+
setSearchPageToken("0");
|
|
35
|
+
}, [searchParams]);
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
results,
|
|
39
|
+
nextPageToken,
|
|
40
|
+
previousPageToken,
|
|
41
|
+
currentPageToken,
|
|
42
|
+
resultsLoading,
|
|
43
|
+
resultsError,
|
|
44
|
+
} = usePropertyListingSearch(searchQuery, searchPageSize, searchPageToken);
|
|
45
|
+
|
|
46
|
+
const primaryImagesMap = usePropertyPrimaryImages(results);
|
|
47
|
+
const propertyAddressMap = usePropertyAddresses(results);
|
|
48
|
+
const { markers: mapMarkers } = usePropertyMapMarkers(results);
|
|
49
|
+
const apiUnavailable = Boolean(resultsError);
|
|
50
|
+
|
|
51
|
+
const validResults = useMemo(() => results.filter((r) => r?.record?.id), [results]);
|
|
52
|
+
|
|
53
|
+
const handlePageChange = useCallback((newPageToken: string) => {
|
|
54
|
+
setSearchPageToken(newPageToken);
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
const handlePageSizeChange = useCallback((newPageSize: number) => {
|
|
58
|
+
setSearchPageSize(newPageSize);
|
|
59
|
+
setSearchPageToken("0");
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
const handleSearchSubmit = useCallback(() => {
|
|
63
|
+
setSearchPageToken("0");
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div className="flex h-[calc(100vh-4rem)] min-h-[500px] flex-col">
|
|
68
|
+
{/* Search bar */}
|
|
69
|
+
<div className="flex shrink-0 flex-wrap items-center gap-2 border-b bg-background px-4 py-3">
|
|
70
|
+
<Input
|
|
71
|
+
type="text"
|
|
72
|
+
value={searchQuery}
|
|
73
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
74
|
+
onKeyDown={(e) => e.key === "Enter" && handleSearchSubmit()}
|
|
75
|
+
placeholder="Add area or search"
|
|
76
|
+
className="h-9 w-48 max-w-[200px]"
|
|
77
|
+
aria-label="Search property listings"
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
{/* Main: map left (2/3), list right (1/3) */}
|
|
82
|
+
<div className="flex min-h-0 flex-1 flex-col lg:flex-row">
|
|
83
|
+
{/* Map – takes ~2/3 on desktop */}
|
|
84
|
+
<div className="h-64 shrink-0 lg:h-full lg:min-h-0 lg:w-2/3" aria-label="Map">
|
|
85
|
+
<PropertyMap
|
|
86
|
+
center={MAP_CENTER_SF}
|
|
87
|
+
zoom={mapMarkers.length > 0 ? 12 : 11}
|
|
88
|
+
markers={mapMarkers}
|
|
89
|
+
className="h-full w-full"
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
{/* Listings – scrollable, ~1/3 */}
|
|
94
|
+
<aside className="flex w-full flex-col border-t border-border bg-background lg:w-1/3 lg:border-l lg:border-t-0">
|
|
95
|
+
<div className="shrink-0 border-b bg-muted/30 px-4 py-3">
|
|
96
|
+
<h2 className="text-base font-semibold text-foreground">
|
|
97
|
+
Property Listings
|
|
98
|
+
{searchQuery.trim() ? ` matching "${searchQuery.trim()}"` : ""}
|
|
99
|
+
</h2>
|
|
100
|
+
<p className="text-sm text-muted-foreground">
|
|
101
|
+
{apiUnavailable
|
|
102
|
+
? "Placeholder (API unavailable)"
|
|
103
|
+
: resultsLoading
|
|
104
|
+
? "Loading…"
|
|
105
|
+
: `${results.length} result(s)`}
|
|
106
|
+
</p>
|
|
107
|
+
</div>
|
|
108
|
+
<div className="flex-1 overflow-y-auto p-4">
|
|
109
|
+
{apiUnavailable ? (
|
|
110
|
+
<PropertySearchPlaceholder
|
|
111
|
+
message={resultsError ?? "Search is temporarily unavailable."}
|
|
112
|
+
/>
|
|
113
|
+
) : resultsLoading ? (
|
|
114
|
+
<div className="space-y-4" role="status">
|
|
115
|
+
{[1, 2, 3].map((i) => (
|
|
116
|
+
<div key={i} className="overflow-hidden rounded-lg border">
|
|
117
|
+
<div className="aspect-[16/10] animate-pulse bg-muted" />
|
|
118
|
+
<div className="space-y-2 p-3">
|
|
119
|
+
<div className="h-4 w-3/4 animate-pulse rounded bg-muted" />
|
|
120
|
+
<div className="h-4 w-1/2 animate-pulse rounded bg-muted" />
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
) : results.length === 0 ? (
|
|
126
|
+
<div className="py-12 text-center">
|
|
127
|
+
<p className="mb-2 font-medium">No results found</p>
|
|
128
|
+
<p className="text-sm text-muted-foreground">Try adjusting search or filters</p>
|
|
129
|
+
</div>
|
|
130
|
+
) : (
|
|
131
|
+
<>
|
|
132
|
+
<ul className="space-y-4" role="list" aria-label="Search results">
|
|
133
|
+
{validResults.map((record, index) => {
|
|
134
|
+
const propertyId = getPropertyIdFromRecord(record.record);
|
|
135
|
+
const imageUrl = propertyId ? (primaryImagesMap[propertyId] ?? null) : null;
|
|
136
|
+
const address = propertyId ? (propertyAddressMap[propertyId] ?? null) : null;
|
|
137
|
+
return (
|
|
138
|
+
<li key={record.record.id ?? index}>
|
|
139
|
+
<PropertyListingCard
|
|
140
|
+
record={record.record}
|
|
141
|
+
imageUrl={imageUrl}
|
|
142
|
+
address={address}
|
|
143
|
+
/>
|
|
144
|
+
</li>
|
|
145
|
+
);
|
|
146
|
+
})}
|
|
147
|
+
</ul>
|
|
148
|
+
<div className="mt-4">
|
|
149
|
+
<SearchPagination
|
|
150
|
+
currentPageToken={currentPageToken}
|
|
151
|
+
nextPageToken={nextPageToken}
|
|
152
|
+
previousPageToken={previousPageToken}
|
|
153
|
+
pageSize={searchPageSize}
|
|
154
|
+
onPageChange={handlePageChange}
|
|
155
|
+
onPageSizeChange={handlePageSizeChange}
|
|
156
|
+
/>
|
|
157
|
+
</div>
|
|
158
|
+
</>
|
|
159
|
+
)}
|
|
160
|
+
</div>
|
|
161
|
+
</aside>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Placeholder content when Property List search API is unavailable.
|
|
3
|
+
* Shows skeleton cards and a friendly message so the layout still feels like the search experience.
|
|
4
|
+
*/
|
|
5
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
6
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
7
|
+
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
|
8
|
+
import { AlertCircle } from "lucide-react";
|
|
9
|
+
|
|
10
|
+
const SKELETON_CARD_COUNT = 3;
|
|
11
|
+
|
|
12
|
+
export default function PropertySearchPlaceholder({
|
|
13
|
+
message = "Search is temporarily unavailable. Please try again later.",
|
|
14
|
+
}: {
|
|
15
|
+
message?: string;
|
|
16
|
+
}) {
|
|
17
|
+
return (
|
|
18
|
+
<div className="space-y-4" role="status" aria-live="polite">
|
|
19
|
+
<Alert
|
|
20
|
+
variant="default"
|
|
21
|
+
className="border-amber-200 bg-amber-50 dark:border-amber-900 dark:bg-amber-950/30"
|
|
22
|
+
>
|
|
23
|
+
<AlertCircle className="h-4 w-4" aria-hidden="true" />
|
|
24
|
+
<AlertTitle>Service temporarily unavailable</AlertTitle>
|
|
25
|
+
<AlertDescription>{message}</AlertDescription>
|
|
26
|
+
</Alert>
|
|
27
|
+
<p className="text-sm text-muted-foreground">Showing placeholder results</p>
|
|
28
|
+
<div className="space-y-4" aria-hidden="true">
|
|
29
|
+
{[...Array(SKELETON_CARD_COUNT)].map((_, i) => (
|
|
30
|
+
<Card key={i}>
|
|
31
|
+
<CardContent className="flex gap-4 p-4">
|
|
32
|
+
<Skeleton className="size-[200px] shrink-0 rounded-xl" />
|
|
33
|
+
<div className="min-w-0 flex-1 space-y-3">
|
|
34
|
+
<Skeleton className="h-5 w-3/4" />
|
|
35
|
+
<Skeleton className="h-4 w-full" />
|
|
36
|
+
<Skeleton className="h-4 w-1/2" />
|
|
37
|
+
<Skeleton className="h-4 w-2/3" />
|
|
38
|
+
<div className="flex gap-2 pt-2">
|
|
39
|
+
<Skeleton className="h-9 w-24" />
|
|
40
|
+
<Skeleton className="h-9 w-28" />
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</CardContent>
|
|
44
|
+
</Card>
|
|
45
|
+
))}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-01.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-02.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-03.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-04.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-05.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-06.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-07.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-08.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-09.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-10.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-11.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-12.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-13.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-14.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-15.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-16.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-17.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-18.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-19.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-20.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-21.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-22.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-23.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-24.jpg
ADDED
|
Binary file
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-25.jpg
ADDED
|
Binary file
|
|
@@ -2,12 +2,16 @@ import type { RouteObject } from 'react-router';
|
|
|
2
2
|
import AppLayout from './appLayout';
|
|
3
3
|
import Home from '@/pages/Home';
|
|
4
4
|
import NotFound from '@/pages/NotFound';
|
|
5
|
+
import GlobalSearch from "./pages/GlobalSearch";
|
|
6
|
+
import DetailPage from "./pages/DetailPage";
|
|
7
|
+
import { Suspense } from "react";
|
|
8
|
+
import LoadingFallback from "./components/LoadingFallback";
|
|
5
9
|
import Dashboard from "@/pages/Dashboard";
|
|
6
10
|
import Maintenance from "@/pages/Maintenance";
|
|
7
|
-
import
|
|
11
|
+
import PropertySearch from "@/pages/PropertySearch";
|
|
8
12
|
import PropertyDetails from "@/pages/PropertyDetails";
|
|
9
13
|
import Application from "@/pages/Application";
|
|
10
|
-
import
|
|
14
|
+
import Contact from "@/pages/Contact";
|
|
11
15
|
|
|
12
16
|
export const routes: RouteObject[] = [
|
|
13
17
|
{
|
|
@@ -23,6 +27,24 @@ export const routes: RouteObject[] = [
|
|
|
23
27
|
path: "*",
|
|
24
28
|
element: <NotFound />
|
|
25
29
|
},
|
|
30
|
+
{
|
|
31
|
+
path: "global-search/:query",
|
|
32
|
+
element: (
|
|
33
|
+
<Suspense fallback={<LoadingFallback />}>
|
|
34
|
+
<GlobalSearch />
|
|
35
|
+
</Suspense>
|
|
36
|
+
),
|
|
37
|
+
handle: { showInNavigation: false }
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
path: "object/:objectApiName/:recordId",
|
|
41
|
+
element: (
|
|
42
|
+
<Suspense fallback={<LoadingFallback />}>
|
|
43
|
+
<DetailPage />
|
|
44
|
+
</Suspense>
|
|
45
|
+
),
|
|
46
|
+
handle: { showInNavigation: false }
|
|
47
|
+
},
|
|
26
48
|
{
|
|
27
49
|
path: "dashboard",
|
|
28
50
|
element: <Dashboard />,
|
|
@@ -30,13 +52,17 @@ export const routes: RouteObject[] = [
|
|
|
30
52
|
},
|
|
31
53
|
{
|
|
32
54
|
path: "properties",
|
|
33
|
-
element: <
|
|
55
|
+
element: <PropertySearch />,
|
|
34
56
|
handle: { showInNavigation: true, label: "Property Search" }
|
|
35
57
|
},
|
|
36
58
|
{
|
|
37
59
|
path: "property/:id",
|
|
38
60
|
element: <PropertyDetails />
|
|
39
61
|
},
|
|
62
|
+
{
|
|
63
|
+
path: "object/Property_Listing__c/:id",
|
|
64
|
+
element: <PropertyDetails />
|
|
65
|
+
},
|
|
40
66
|
{
|
|
41
67
|
path: "maintenance",
|
|
42
68
|
element: <Maintenance />,
|
|
@@ -47,9 +73,9 @@ export const routes: RouteObject[] = [
|
|
|
47
73
|
element: <Application />
|
|
48
74
|
},
|
|
49
75
|
{
|
|
50
|
-
path: "
|
|
51
|
-
element: <
|
|
52
|
-
handle: { showInNavigation: true, label: "
|
|
76
|
+
path: "contact",
|
|
77
|
+
element: <Contact />,
|
|
78
|
+
handle: { showInNavigation: true, label: "Contact" }
|
|
53
79
|
}
|
|
54
80
|
]
|
|
55
81
|
}
|
|
@@ -134,69 +134,29 @@
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
--ring: oklch(0.488 0.09 195.75);
|
|
159
|
-
--radius: 0.625rem;
|
|
160
|
-
--spacing: 0.25rem;
|
|
161
|
-
}
|
|
162
|
-
@media (prefers-reduced-motion: reduce) {
|
|
163
|
-
* {
|
|
164
|
-
animation-duration: 0.01ms !important;
|
|
165
|
-
animation-iteration-count: 1 !important;
|
|
166
|
-
transition-duration: 0.01ms !important;
|
|
167
|
-
scroll-behavior: auto !important;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
* {
|
|
171
|
-
@apply border-[var(--border)];
|
|
172
|
-
}
|
|
173
|
-
body {
|
|
174
|
-
@apply bg-[var(--background)] text-[var(--foreground)] antialiased;
|
|
175
|
-
}
|
|
137
|
+
/* ZENLEASE design language: teal primary, cream background, soft rounded corners */
|
|
138
|
+
|
|
139
|
+
:root {
|
|
140
|
+
/* Primary: deep teal (#20888F / #2c8a8d) */
|
|
141
|
+
--primary: oklch(0.52 0.09 192);
|
|
142
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
143
|
+
/* Background: warm cream (#F8F6F1) */
|
|
144
|
+
--background: oklch(0.97 0.01 85);
|
|
145
|
+
--foreground: oklch(0.21 0.02 260);
|
|
146
|
+
/* Card: white with subtle warmth */
|
|
147
|
+
--card: oklch(1 0 0);
|
|
148
|
+
--card-foreground: oklch(0.21 0.02 260);
|
|
149
|
+
/* Muted: light cream for secondary surfaces */
|
|
150
|
+
--muted: oklch(0.955 0.01 85);
|
|
151
|
+
--muted-foreground: oklch(0.48 0.02 260);
|
|
152
|
+
/* Border / input: soft teal-tinted gray */
|
|
153
|
+
--border: oklch(0.91 0.02 192);
|
|
154
|
+
--input: oklch(0.91 0.02 192);
|
|
155
|
+
--ring: oklch(0.52 0.09 192);
|
|
156
|
+
/* Slightly larger radius for soft, friendly feel (screenshots use 16–24px) */
|
|
157
|
+
--radius: 0.75rem;
|
|
176
158
|
}
|
|
177
159
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
--color-foreground: var(--foreground);
|
|
181
|
-
--color-card: var(--card);
|
|
182
|
-
--color-card-foreground: var(--card-foreground);
|
|
183
|
-
--color-popover: var(--popover);
|
|
184
|
-
--color-popover-foreground: var(--popover-foreground);
|
|
185
|
-
--color-primary: var(--primary);
|
|
186
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
187
|
-
--color-secondary: var(--secondary);
|
|
188
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
189
|
-
--color-muted: var(--muted);
|
|
190
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
191
|
-
--color-accent: var(--accent);
|
|
192
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
193
|
-
--color-destructive: var(--destructive);
|
|
194
|
-
--color-border: var(--border);
|
|
195
|
-
--color-input: var(--input);
|
|
196
|
-
--color-ring: var(--ring);
|
|
197
|
-
--radius-sm: calc(var(--radius) - 4px);
|
|
198
|
-
--radius-md: calc(var(--radius) - 2px);
|
|
199
|
-
--radius-lg: var(--radius);
|
|
200
|
-
--radius-xl: calc(var(--radius) + 4px);
|
|
201
|
-
--spacing: var(--spacing);
|
|
160
|
+
body {
|
|
161
|
+
@apply bg-background text-foreground;
|
|
202
162
|
}
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/filters/filters.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type definitions for filter structures
|
|
5
|
+
* All types are derived from Zod schemas using z.infer for type safety
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Allowed filter operators for Salesforce search queries
|
|
10
|
+
*/
|
|
11
|
+
export const FILTER_OPERATORS = [
|
|
12
|
+
"eq", // Equals
|
|
13
|
+
"ne", // Not equals
|
|
14
|
+
"like", // Pattern matching (contains)
|
|
15
|
+
"gt", // Greater than
|
|
16
|
+
"gte", // Greater than or equal
|
|
17
|
+
"lt", // Less than
|
|
18
|
+
"lte", // Less than or equal
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Filter operator type
|
|
23
|
+
*/
|
|
24
|
+
export type FilterOperator = (typeof FILTER_OPERATORS)[number];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Salesforce field path validation regex
|
|
28
|
+
* Validates field paths like:
|
|
29
|
+
* - Simple fields: "Name", "FieldName__c"
|
|
30
|
+
* - Relationship fields: "Account__r.Name", "Owner__r.FieldName__c"
|
|
31
|
+
* - Nested relationships: "Account__r.Owner__r.Name"
|
|
32
|
+
*
|
|
33
|
+
* Pattern explanation:
|
|
34
|
+
* - ^[A-Za-z][A-Za-z0-9_]* - Starts with letter, followed by letters/numbers/underscores
|
|
35
|
+
* - (__[cr])? - Optional relationship suffix (__r or __c)
|
|
36
|
+
* - (\.[A-Za-z][A-Za-z0-9_]*(__[cr])?)* - Optional relationship traversal (dot notation)
|
|
37
|
+
*/
|
|
38
|
+
const SALESFORCE_FIELD_PATH_REGEX =
|
|
39
|
+
/^[A-Za-z][A-Za-z0-9_]*(__[cr])?(\.[A-Za-z][A-Za-z0-9_]*(__[cr])?)*$/;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validates Salesforce field path format
|
|
43
|
+
* @param fieldPath - The field path to validate
|
|
44
|
+
* @returns true if valid, false otherwise
|
|
45
|
+
*/
|
|
46
|
+
function isValidSalesforceFieldPath(fieldPath: string): boolean {
|
|
47
|
+
if (!fieldPath || fieldPath.trim().length === 0) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return SALESFORCE_FIELD_PATH_REGEX.test(fieldPath);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Zod Schema for Filter Attributes
|
|
54
|
+
const FilterAttributesSchema = z.object({
|
|
55
|
+
affordance: z.string().optional(),
|
|
56
|
+
placeholder: z.string().optional(),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Filter attributes containing input-specific properties
|
|
61
|
+
*/
|
|
62
|
+
export type FilterAttributes = z.infer<typeof FilterAttributesSchema>;
|
|
63
|
+
|
|
64
|
+
// Zod Schema for Filter
|
|
65
|
+
const FilterSchema = z.object({
|
|
66
|
+
affordance: z.string(),
|
|
67
|
+
attributes: FilterAttributesSchema.optional(),
|
|
68
|
+
defaultValues: z.array(z.string()).optional(),
|
|
69
|
+
helpMessage: z.string().nullable().optional(),
|
|
70
|
+
label: z.string(),
|
|
71
|
+
targetFieldPath: z.string().refine((value) => isValidSalesforceFieldPath(value), {
|
|
72
|
+
message:
|
|
73
|
+
"Invalid Salesforce field path format. Field paths must start with a letter and can contain letters, numbers, underscores, and relationship notation (__r or __c). Use dot notation for relationships (e.g., 'Account__r.Name').",
|
|
74
|
+
}),
|
|
75
|
+
type: z.string(),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Single filter definition from getObjectListFilters API
|
|
80
|
+
*/
|
|
81
|
+
export type Filter = z.infer<typeof FilterSchema>;
|
|
82
|
+
|
|
83
|
+
// Export schema for validation
|
|
84
|
+
export const FilterArraySchema = z.array(FilterSchema);
|
|
85
|
+
|
|
86
|
+
// Zod Schema for Filter Criteria with operator and field path validation
|
|
87
|
+
const FilterCriteriaSchema = z.object({
|
|
88
|
+
objectApiName: z.string().min(1, "Object API name is required"),
|
|
89
|
+
fieldPath: z
|
|
90
|
+
.string()
|
|
91
|
+
.min(1, "Field path is required")
|
|
92
|
+
.refine((value) => isValidSalesforceFieldPath(value), {
|
|
93
|
+
message:
|
|
94
|
+
"Invalid Salesforce field path format. Field paths must start with a letter and can contain letters, numbers, underscores, and relationship notation (__r or __c). Use dot notation for relationships (e.g., 'Account__r.Name').",
|
|
95
|
+
}),
|
|
96
|
+
operator: z.enum(FILTER_OPERATORS, {
|
|
97
|
+
message: `Operator must be one of: ${FILTER_OPERATORS.join(", ")}`,
|
|
98
|
+
}),
|
|
99
|
+
values: z.array(z.union([z.string(), z.number()])).min(1, "At least one value is required"),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Filter criteria structure for filtering search results
|
|
104
|
+
*/
|
|
105
|
+
export type FilterCriteria = z.infer<typeof FilterCriteriaSchema>;
|
|
106
|
+
|
|
107
|
+
// Export schema for validation
|
|
108
|
+
export const FilterCriteriaArraySchema = z.array(FilterCriteriaSchema);
|
|
109
|
+
|
|
110
|
+
// Zod Schema for Filters Response
|
|
111
|
+
const FiltersResponseSchema = z.record(z.string(), z.unknown()).and(
|
|
112
|
+
z.object({
|
|
113
|
+
filters: FilterArraySchema.optional(),
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Filters response structure
|
|
119
|
+
*/
|
|
120
|
+
export type FiltersResponse = z.infer<typeof FiltersResponseSchema>;
|