@salesforce/webapp-template-app-react-sample-b2x-experimental 1.112.7 → 1.112.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.
- package/dist/CHANGELOG.md +19 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/package.json +6 -5
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/graphql-operations-types.ts +12058 -214
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/graphqlClient.ts +18 -15
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/{propertyListingGraphQL.ts → properties/propertyListingGraphQL.ts} +1 -1
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/appLayout.tsx +4 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/{TopBar.tsx → layout/TopBar.tsx} +2 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/{MaintenanceRequestList.tsx → maintenanceRequests/MaintenanceRequestList.tsx} +4 -4
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/{MaintenanceRequestListItem.tsx → maintenanceRequests/MaintenanceRequestListItem.tsx} +3 -3
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/maintenanceRequests/MaintenanceSummaryDetailsModal.tsx +87 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/{PropertyListingCard.tsx → properties/PropertyListingCard.tsx} +1 -1
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/{features/global-search/components/search/SearchPagination.tsx → components/properties/PropertyListingSearchPagination.tsx} +20 -28
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/constants/propertyListing.ts +4 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/__examples__/api/accountSearchService.ts +46 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/__examples__/api/query/distinctAccountIndustries.graphql +19 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/__examples__/api/query/distinctAccountTypes.graphql +19 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/__examples__/api/query/getAccountDetail.graphql +121 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/__examples__/api/query/searchAccounts.graphql +51 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/__examples__/pages/AccountObjectDetailPage.tsx +357 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/__examples__/pages/AccountSearch.tsx +303 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/__examples__/pages/Home.tsx +34 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/api/objectSearchService.ts +84 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/ActiveFilters.tsx +89 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/FilterContext.tsx +73 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/ObjectBreadcrumb.tsx +66 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/PaginationControls.tsx +109 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/SearchBar.tsx +41 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/SortControl.tsx +143 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/filters/BooleanFilter.tsx +74 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/filters/DateFilter.tsx +121 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/filters/DateRangeFilter.tsx +69 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/filters/MultiSelectFilter.tsx +98 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/filters/NumericRangeFilter.tsx +85 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/filters/SearchFilter.tsx +37 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/filters/SelectFilter.tsx +93 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/components/filters/TextFilter.tsx +74 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/hooks/useAsyncData.ts +54 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/hooks/useCachedAsyncData.ts +184 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/hooks/useObjectSearchParams.ts +247 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/utils/debounce.ts +22 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/utils/fieldUtils.ts +29 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/utils/filterUtils.ts +372 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/object-search/utils/sortUtils.ts +38 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useMaintenanceRequests.ts +1 -1
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyAddresses.ts +2 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyDetail.ts +1 -1
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyListingAmenities.ts +2 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyListingSearch.ts +2 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyMapMarkers.ts +3 -3
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyPrimaryImages.ts +2 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Application.tsx +3 -3
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Dashboard.tsx +2 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Home.tsx +4 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Maintenance.tsx +2 -2
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyDetails.tsx +1 -1
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertySearch.tsx +12 -10
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/routes.tsx +6 -18
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/propertyListingPaginationUtils.ts +18 -0
- package/dist/package-lock.json +2 -2
- package/dist/package.json +1 -1
- package/dist/scripts/graphql-search.sh +69 -17
- package/package.json +1 -1
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/MaintenanceDetailsModal.tsx +0 -128
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/api/objectDetailService.ts +0 -102
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/api/objectInfoGraphQLService.ts +0 -137
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/api/objectInfoService.ts +0 -95
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/api/recordListGraphQLService.ts +0 -364
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/DetailFields.tsx +0 -55
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/DetailForm.tsx +0 -146
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/DetailHeader.tsx +0 -34
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/DetailLayoutSections.tsx +0 -80
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/Section.tsx +0 -108
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/SectionRow.tsx +0 -20
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/UiApiDetailForm.tsx +0 -140
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/formatted/FieldValueDisplay.tsx +0 -73
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/formatted/FormattedAddress.tsx +0 -29
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/formatted/FormattedEmail.tsx +0 -17
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/formatted/FormattedPhone.tsx +0 -24
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/formatted/FormattedText.tsx +0 -11
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/detail/formatted/FormattedUrl.tsx +0 -29
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/filters/FilterField.tsx +0 -54
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/filters/FilterInput.tsx +0 -55
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/filters/FilterSelect.tsx +0 -72
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/filters/FiltersPanel.tsx +0 -380
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/forms/filters-form.tsx +0 -114
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/forms/submit-button.tsx +0 -47
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/search/GlobalSearchInput.tsx +0 -114
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/search/ResultCardFields.tsx +0 -71
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/search/SearchHeader.tsx +0 -31
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/search/SearchResultCard.tsx +0 -138
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/search/SearchResultsPanel.tsx +0 -197
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/components/shared/LoadingFallback.tsx +0 -61
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/constants.ts +0 -39
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/filters/FilterInput.tsx +0 -55
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/filters/FilterSelect.tsx +0 -72
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/hooks/form.tsx +0 -209
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/hooks/useObjectInfoBatch.ts +0 -72
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/hooks/useObjectSearchData.ts +0 -174
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/hooks/useRecordDetailLayout.ts +0 -137
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/hooks/useRecordListGraphQL.ts +0 -135
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/pages/DetailPage.tsx +0 -109
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/pages/GlobalSearch.tsx +0 -235
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/types/filters/filters.ts +0 -121
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/types/filters/picklist.ts +0 -6
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/types/objectInfo/objectInfo.ts +0 -49
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/types/recordDetail/recordDetail.ts +0 -61
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/types/schema.d.ts +0 -200
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/apiUtils.ts +0 -59
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/cacheUtils.ts +0 -76
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/debounce.ts +0 -90
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/fieldUtils.ts +0 -354
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/fieldValueExtractor.ts +0 -67
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/filterUtils.ts +0 -32
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/formDataTransformUtils.ts +0 -260
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/formUtils.ts +0 -142
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/graphQLNodeFieldUtils.ts +0 -186
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/graphQLObjectInfoAdapter.ts +0 -77
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/graphQLRecordAdapter.ts +0 -90
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/layoutTransformUtils.ts +0 -236
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/linkUtils.ts +0 -14
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/paginationUtils.ts +0 -49
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/recordUtils.ts +0 -159
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/utils/sanitizationUtils.ts +0 -50
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyListingPriceRange.ts +0 -64
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/index.ts +0 -120
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/About.tsx +0 -8
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/HelpCenter.tsx +0 -29
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyListings.tsx +0 -100
- /package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/{applicationApi.ts → applications/applicationApi.ts} +0 -0
- /package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/{maintenanceRequestApi.ts → maintenanceRequests/maintenanceRequestApi.ts} +0 -0
- /package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/{propertyDetailGraphQL.ts → properties/propertyDetailGraphQL.ts} +0 -0
- /package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/{WeatherWidget.tsx → dashboard/WeatherWidget.tsx} +0 -0
- /package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/{NavMenu.tsx → layout/VerticalNav.tsx} +0 -0
- /package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/{MaintenanceRequestIcon.tsx → maintenanceRequests/MaintenanceRequestIcon.tsx} +0 -0
- /package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/{StatusBadge.tsx → maintenanceRequests/StatusBadge.tsx} +0 -0
- /package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/{PropertyMap.tsx → properties/PropertyMap.tsx} +0 -0
- /package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/{PropertySearchFilters.tsx → properties/PropertySearchFilters.tsx} +0 -0
- /package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/{features/global-search/types/search → types}/searchResults.ts +0 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* filterUtils.ts
|
|
3
|
+
*
|
|
4
|
+
* Centralizes all filter-related transformations for the object search feature.
|
|
5
|
+
* This module handles two distinct concerns:
|
|
6
|
+
*
|
|
7
|
+
* 1. **URL serialization** — Converting filter/sort state to and from
|
|
8
|
+
* URLSearchParams so that search criteria can be bookmarked, shared, and
|
|
9
|
+
* restored on page load.
|
|
10
|
+
*
|
|
11
|
+
* 2. **GraphQL query building** — Converting the same filter state into the
|
|
12
|
+
* `where` clause shape expected by the GraphQL API.
|
|
13
|
+
*
|
|
14
|
+
* Both concerns operate on the shared {@link ActiveFilterValue} type, which
|
|
15
|
+
* represents a single active filter with a field name, filter type, and one or
|
|
16
|
+
* more values (value, min, max).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { SortState } from "./sortUtils";
|
|
20
|
+
|
|
21
|
+
export type FilterFieldType =
|
|
22
|
+
| "text"
|
|
23
|
+
| "picklist"
|
|
24
|
+
| "numeric"
|
|
25
|
+
| "boolean"
|
|
26
|
+
| "date"
|
|
27
|
+
| "daterange"
|
|
28
|
+
| "multipicklist"
|
|
29
|
+
| "search";
|
|
30
|
+
|
|
31
|
+
export type FilterFieldConfig<TFieldName extends string = string> = {
|
|
32
|
+
field: TFieldName;
|
|
33
|
+
label: string;
|
|
34
|
+
type: FilterFieldType;
|
|
35
|
+
placeholder?: string;
|
|
36
|
+
/** Required for picklist type. */
|
|
37
|
+
options?: Array<{ value: string; label: string }>;
|
|
38
|
+
helpText?: string;
|
|
39
|
+
/** Required for search type — the fields to match against with `or`. */
|
|
40
|
+
searchFields?: string[];
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type ActiveFilterValue<TFieldName extends string = string> = {
|
|
44
|
+
field: TFieldName;
|
|
45
|
+
label: string;
|
|
46
|
+
type: FilterFieldType;
|
|
47
|
+
value?: string;
|
|
48
|
+
min?: string;
|
|
49
|
+
max?: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// URL Serialization
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Prefix applied to all filter-related URL search params.
|
|
58
|
+
* This namespaces filter params so they don't collide with other query params
|
|
59
|
+
* (e.g. pagination, feature flags).
|
|
60
|
+
*
|
|
61
|
+
* @example "f.Industry=Technology" or "f.AnnualRevenue.min=1000000"
|
|
62
|
+
*/
|
|
63
|
+
const FILTER_PREFIX = "f.";
|
|
64
|
+
|
|
65
|
+
/** URL param key for the multi-field search term. */
|
|
66
|
+
const SEARCH_KEY = "q";
|
|
67
|
+
|
|
68
|
+
/** URL param key for the currently sorted field name. */
|
|
69
|
+
const SORT_KEY = "sort";
|
|
70
|
+
|
|
71
|
+
/** URL param key for the sort direction (ASC or DESC). */
|
|
72
|
+
const DIR_KEY = "dir";
|
|
73
|
+
|
|
74
|
+
/** URL param key for the page size preference. */
|
|
75
|
+
const PAGE_SIZE_KEY = "ps";
|
|
76
|
+
const PAGE_KEY = "page";
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Serializes filter and sort state into URLSearchParams.
|
|
80
|
+
*
|
|
81
|
+
* Encoding scheme:
|
|
82
|
+
* - Simple values (text, picklist, boolean, multipicklist):
|
|
83
|
+
* `f.<field>=<value>`
|
|
84
|
+
* - Range values (numeric, date, daterange):
|
|
85
|
+
* `f.<field>.min=<min>` and/or `f.<field>.max=<max>`
|
|
86
|
+
* - Sort: `sort=<field>&dir=ASC|DESC`
|
|
87
|
+
*
|
|
88
|
+
* @param filters - The currently active filters to serialize.
|
|
89
|
+
* @param sort - The current sort state, or null if no sort is applied.
|
|
90
|
+
* @returns A URLSearchParams instance representing the full search state.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* const params = filtersToSearchParams(
|
|
95
|
+
* [{ field: "Industry", type: "picklist", value: "Technology" }],
|
|
96
|
+
* { field: "Name", direction: "ASC" },
|
|
97
|
+
* );
|
|
98
|
+
* // params.toString() => "f.Industry=Technology&sort=Name&dir=ASC"
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export function filtersToSearchParams(
|
|
102
|
+
filters: ActiveFilterValue[],
|
|
103
|
+
sort: SortState | null,
|
|
104
|
+
pageSize?: number,
|
|
105
|
+
pageIndex?: number,
|
|
106
|
+
): URLSearchParams {
|
|
107
|
+
const params = new URLSearchParams();
|
|
108
|
+
|
|
109
|
+
for (const filter of filters) {
|
|
110
|
+
if (filter.type === "search") {
|
|
111
|
+
if (filter.value) params.set(SEARCH_KEY, filter.value);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (filter.value !== undefined && filter.value !== "") {
|
|
115
|
+
params.set(`${FILTER_PREFIX}${filter.field}`, filter.value);
|
|
116
|
+
}
|
|
117
|
+
if (filter.min !== undefined && filter.min !== "") {
|
|
118
|
+
params.set(`${FILTER_PREFIX}${filter.field}.min`, filter.min);
|
|
119
|
+
}
|
|
120
|
+
if (filter.max !== undefined && filter.max !== "") {
|
|
121
|
+
params.set(`${FILTER_PREFIX}${filter.field}.max`, filter.max);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (sort) {
|
|
126
|
+
params.set(SORT_KEY, sort.field);
|
|
127
|
+
params.set(DIR_KEY, sort.direction);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (pageSize !== undefined) {
|
|
131
|
+
params.set(PAGE_SIZE_KEY, String(pageSize));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (pageIndex !== undefined && pageIndex > 0) {
|
|
135
|
+
params.set(PAGE_KEY, String(pageIndex + 1));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return params;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Deserializes URLSearchParams back into filter and sort state.
|
|
143
|
+
*
|
|
144
|
+
* Requires the full list of filter configs so it knows which URL params to look
|
|
145
|
+
* for and what type each filter is. Params that don't match a known config are
|
|
146
|
+
* silently ignored, making this safe against stale or hand-edited URLs.
|
|
147
|
+
*
|
|
148
|
+
* @param params - The URLSearchParams to parse (typically from the browser URL).
|
|
149
|
+
* @param configs - The filter field configurations defining available filters.
|
|
150
|
+
* @returns An object containing the deserialized `filters` array and `sort` state.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* const url = new URLSearchParams("f.Industry=Technology&sort=Name&dir=ASC");
|
|
155
|
+
* const { filters, sort } = searchParamsToFilters(url, filterConfigs);
|
|
156
|
+
* // filters => [{ field: "Industry", type: "picklist", value: "Technology" }]
|
|
157
|
+
* // sort => { field: "Name", direction: "ASC" }
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export function searchParamsToFilters(
|
|
161
|
+
params: URLSearchParams,
|
|
162
|
+
configs: FilterFieldConfig[],
|
|
163
|
+
): {
|
|
164
|
+
filters: ActiveFilterValue[];
|
|
165
|
+
sort: SortState | null;
|
|
166
|
+
pageSize: number | undefined;
|
|
167
|
+
pageIndex: number;
|
|
168
|
+
} {
|
|
169
|
+
const filters: ActiveFilterValue[] = [];
|
|
170
|
+
|
|
171
|
+
for (const config of configs) {
|
|
172
|
+
const { field, label, type } = config;
|
|
173
|
+
|
|
174
|
+
if (type === "search") {
|
|
175
|
+
const q = params.get(SEARCH_KEY);
|
|
176
|
+
if (q) {
|
|
177
|
+
filters.push({ field, label, type: "search", value: q });
|
|
178
|
+
}
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const value = params.get(`${FILTER_PREFIX}${field}`) ?? undefined;
|
|
183
|
+
const min = params.get(`${FILTER_PREFIX}${field}.min`) ?? undefined;
|
|
184
|
+
const max = params.get(`${FILTER_PREFIX}${field}.max`) ?? undefined;
|
|
185
|
+
|
|
186
|
+
const hasValue = value !== undefined && value !== "";
|
|
187
|
+
const hasRange = (min !== undefined && min !== "") || (max !== undefined && max !== "");
|
|
188
|
+
|
|
189
|
+
if (hasValue || hasRange) {
|
|
190
|
+
filters.push({ field, label, type, value, min, max });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let sort: SortState | null = null;
|
|
195
|
+
const sortField = params.get(SORT_KEY);
|
|
196
|
+
const sortDir = params.get(DIR_KEY);
|
|
197
|
+
if (sortField) {
|
|
198
|
+
sort = {
|
|
199
|
+
field: sortField,
|
|
200
|
+
direction: sortDir === "DESC" ? "DESC" : "ASC",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const pageSizeRaw = params.get(PAGE_SIZE_KEY);
|
|
205
|
+
const pageSize = pageSizeRaw ? parseInt(pageSizeRaw, 10) : undefined;
|
|
206
|
+
|
|
207
|
+
const pageRaw = params.get(PAGE_KEY);
|
|
208
|
+
const page = pageRaw ? parseInt(pageRaw, 10) : 1;
|
|
209
|
+
const pageIndex = !isNaN(page) && page > 1 ? page - 1 : 0;
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
filters,
|
|
213
|
+
sort,
|
|
214
|
+
pageSize: pageSize && !isNaN(pageSize) ? pageSize : undefined,
|
|
215
|
+
pageIndex,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
// GraphQL Filter Building
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Converts an array of active filter values into a GraphQL `where` clause.
|
|
225
|
+
*
|
|
226
|
+
* Each filter is individually converted to a clause via {@link buildSingleFilter},
|
|
227
|
+
* then multiple clauses are combined with a top-level `and` operator. This ensures
|
|
228
|
+
* all active filters are applied simultaneously (intersection semantics).
|
|
229
|
+
*
|
|
230
|
+
* @typeParam TFilter - The GraphQL filter input type (e.g. `AccountFilterInput`).
|
|
231
|
+
* @param filters - The active filters to convert.
|
|
232
|
+
* @returns A filter object for the GraphQL `where` variable, or `undefined` if
|
|
233
|
+
* no filters are active (which tells the API to return unfiltered results).
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```ts
|
|
237
|
+
* const where = buildFilter<AccountFilterInput>([
|
|
238
|
+
* { field: "Industry", type: "picklist", value: "Technology" },
|
|
239
|
+
* { field: "AnnualRevenue", type: "numeric", min: "1000000" },
|
|
240
|
+
* ]);
|
|
241
|
+
* // where => { and: [
|
|
242
|
+
* // { Industry: { eq: "Technology" } },
|
|
243
|
+
* // { AnnualRevenue: { gte: 1000000 } },
|
|
244
|
+
* // ]}
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
export function buildFilter<TFilter>(
|
|
248
|
+
filters: ActiveFilterValue[],
|
|
249
|
+
configs: FilterFieldConfig[],
|
|
250
|
+
): TFilter | undefined {
|
|
251
|
+
const configMap = new Map(configs.map((c) => [c.field, c]));
|
|
252
|
+
const clauses: TFilter[] = [];
|
|
253
|
+
|
|
254
|
+
for (const filter of filters) {
|
|
255
|
+
const clause = buildSingleFilter<TFilter>(filter, configMap.get(filter.field));
|
|
256
|
+
if (clause) clauses.push(clause);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (clauses.length === 0) return undefined;
|
|
260
|
+
if (clauses.length === 1) return clauses[0];
|
|
261
|
+
return { and: clauses } as TFilter;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Converts a YYYY-MM-DD date string to a full ISO-8601 datetime at midnight UTC.
|
|
266
|
+
* Used as the inclusive lower bound for date range queries.
|
|
267
|
+
*/
|
|
268
|
+
function toStartOfDay(dateStr: string): string {
|
|
269
|
+
return `${dateStr}T00:00:00.000Z`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Converts a YYYY-MM-DD date string to a full ISO-8601 datetime at the last
|
|
274
|
+
* millisecond of the day in UTC. Used as the inclusive upper bound for date
|
|
275
|
+
* range queries.
|
|
276
|
+
*/
|
|
277
|
+
function toEndOfDay(dateStr: string): string {
|
|
278
|
+
return `${dateStr}T23:59:59.999Z`;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Converts a single active filter value into a GraphQL filter clause.
|
|
283
|
+
*
|
|
284
|
+
* Supported filter types and their GraphQL mappings:
|
|
285
|
+
*
|
|
286
|
+
* | Type | GraphQL operator(s) | Example output |
|
|
287
|
+
* |-----------------|---------------------|----------------------------------------------------------|
|
|
288
|
+
* | `text` | `like` | `{ Name: { like: "%Acme%" } }` |
|
|
289
|
+
* | `picklist` | `eq` | `{ Industry: { eq: "Technology" } }` |
|
|
290
|
+
* | `multipicklist` | `eq` or `in` | `{ Type: { in: ["A", "B"] } }` |
|
|
291
|
+
* | `numeric` | `gte` / `lte` | `{ Revenue: { gte: 1000, lte: 5000 } }` |
|
|
292
|
+
* | `boolean` | `eq` | `{ IsActive: { eq: true } }` |
|
|
293
|
+
* | `date` | dynamic operator | `{ CreatedDate: { gte: { value: "..." } } }` |
|
|
294
|
+
* | `daterange` | `gte` + `lte` | Combined with `and` if both bounds set |
|
|
295
|
+
* | `search` | `like` + `or` | `{ or: [{ Name: { like: "%x%" } }, { Phone: { like: "%x%" } }] }` |
|
|
296
|
+
*
|
|
297
|
+
* The `search` type uses `config.searchFields` to build an `or` clause that
|
|
298
|
+
* matches the search term across multiple fields simultaneously (union semantics).
|
|
299
|
+
*
|
|
300
|
+
* @param filter - The active filter value to convert.
|
|
301
|
+
* @param config - The corresponding field config. Required for `search` type
|
|
302
|
+
* (provides `searchFields`); optional for all other types.
|
|
303
|
+
* @returns A single filter clause, or `null` if the filter has no meaningful value.
|
|
304
|
+
*/
|
|
305
|
+
function buildSingleFilter<TFilter>(
|
|
306
|
+
filter: ActiveFilterValue,
|
|
307
|
+
config?: FilterFieldConfig,
|
|
308
|
+
): TFilter | null {
|
|
309
|
+
const { field, type, value, min, max } = filter;
|
|
310
|
+
|
|
311
|
+
switch (type) {
|
|
312
|
+
case "text": {
|
|
313
|
+
if (!value) return null;
|
|
314
|
+
return { [field]: { like: `%${value}%` } } as TFilter;
|
|
315
|
+
}
|
|
316
|
+
case "picklist": {
|
|
317
|
+
if (!value) return null;
|
|
318
|
+
return { [field]: { eq: value } } as TFilter;
|
|
319
|
+
}
|
|
320
|
+
case "numeric": {
|
|
321
|
+
if (!min && !max) return null;
|
|
322
|
+
const ops: Record<string, number> = {};
|
|
323
|
+
if (min) ops.gte = Number(min);
|
|
324
|
+
if (max) ops.lte = Number(max);
|
|
325
|
+
return { [field]: ops } as TFilter;
|
|
326
|
+
}
|
|
327
|
+
case "boolean": {
|
|
328
|
+
if (value === undefined || value === "") return null;
|
|
329
|
+
return { [field]: { eq: value === "true" } } as TFilter;
|
|
330
|
+
}
|
|
331
|
+
case "multipicklist": {
|
|
332
|
+
if (!value) return null;
|
|
333
|
+
const values = value.split(",");
|
|
334
|
+
if (values.length === 1) {
|
|
335
|
+
return { [field]: { eq: values[0] } } as TFilter;
|
|
336
|
+
}
|
|
337
|
+
return { [field]: { in: values } } as TFilter;
|
|
338
|
+
}
|
|
339
|
+
case "date": {
|
|
340
|
+
if (!min && !max) return null;
|
|
341
|
+
const op = value ?? (min ? "gte" : "lte");
|
|
342
|
+
const dateStr = min ?? max;
|
|
343
|
+
const isoStr = op === "gte" || op === "gt" ? toStartOfDay(dateStr!) : toEndOfDay(dateStr!);
|
|
344
|
+
return { [field]: { [op]: { value: isoStr } } } as TFilter;
|
|
345
|
+
}
|
|
346
|
+
case "daterange": {
|
|
347
|
+
if (!min && !max) return null;
|
|
348
|
+
const clauses: TFilter[] = [];
|
|
349
|
+
if (min) {
|
|
350
|
+
clauses.push({
|
|
351
|
+
[field]: { gte: { value: toStartOfDay(min) } },
|
|
352
|
+
} as TFilter);
|
|
353
|
+
}
|
|
354
|
+
if (max) {
|
|
355
|
+
clauses.push({
|
|
356
|
+
[field]: { lte: { value: toEndOfDay(max) } },
|
|
357
|
+
} as TFilter);
|
|
358
|
+
}
|
|
359
|
+
return clauses.length === 1 ? clauses[0] : ({ and: clauses } as TFilter);
|
|
360
|
+
}
|
|
361
|
+
case "search": {
|
|
362
|
+
if (!value) return null;
|
|
363
|
+
const searchFields = config?.searchFields ?? [];
|
|
364
|
+
if (searchFields.length === 0) return null;
|
|
365
|
+
const clauses = searchFields.map((f) => ({ [f]: { like: `%${value}%` } }) as TFilter);
|
|
366
|
+
if (clauses.length === 1) return clauses[0];
|
|
367
|
+
return { or: clauses } as TFilter;
|
|
368
|
+
}
|
|
369
|
+
default:
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ResultOrder, NullOrder } from "../../../api/graphql-operations-types";
|
|
2
|
+
|
|
3
|
+
export type SortFieldConfig<TFieldName extends string = string> = {
|
|
4
|
+
field: TFieldName;
|
|
5
|
+
label: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type SortState<TFieldName extends string = string> = {
|
|
9
|
+
field: TFieldName;
|
|
10
|
+
direction: "ASC" | "DESC";
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Converts a {@link SortState} into a GraphQL order-by object.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam TOrderBy - The GraphQL order-by input type (e.g. `AccountOrderByInput`).
|
|
17
|
+
* @param sort - The current sort state from the UI, or `null` if no sort is applied.
|
|
18
|
+
* @returns An order-by object for the GraphQL query's `orderBy` variable, or
|
|
19
|
+
* `undefined` if no sort is active (which uses the API's default ordering).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const orderBy = buildOrderBy<AccountOrderByInput>({
|
|
24
|
+
* field: "Name",
|
|
25
|
+
* direction: "ASC",
|
|
26
|
+
* });
|
|
27
|
+
* // orderBy => { Name: { order: ResultOrder.Asc, nulls: NullOrder.Last } }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function buildOrderBy<TOrderBy>(sort: SortState | null): TOrderBy | undefined {
|
|
31
|
+
if (!sort) return undefined;
|
|
32
|
+
return {
|
|
33
|
+
[sort.field]: {
|
|
34
|
+
order: sort.direction === "ASC" ? ResultOrder.Asc : ResultOrder.Desc,
|
|
35
|
+
nulls: NullOrder.Last,
|
|
36
|
+
},
|
|
37
|
+
} as TOrderBy;
|
|
38
|
+
}
|
|
@@ -5,7 +5,7 @@ import { useState, useEffect, useCallback } from "react";
|
|
|
5
5
|
import {
|
|
6
6
|
queryMaintenanceRequests,
|
|
7
7
|
type MaintenanceRequestSummary,
|
|
8
|
-
} from "@/api/maintenanceRequestApi";
|
|
8
|
+
} from "@/api/maintenanceRequests/maintenanceRequestApi";
|
|
9
9
|
|
|
10
10
|
export function useMaintenanceRequests(): {
|
|
11
11
|
requests: MaintenanceRequestSummary[];
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Returns propertyId -> address for display on listing cards.
|
|
4
4
|
*/
|
|
5
5
|
import { useState, useEffect } from "react";
|
|
6
|
-
import { fetchPropertyAddresses } from "@/api/propertyDetailGraphQL";
|
|
6
|
+
import { fetchPropertyAddresses } from "@/api/properties/propertyDetailGraphQL";
|
|
7
7
|
import { getPropertyIdFromRecord } from "@/hooks/usePropertyPrimaryImages";
|
|
8
|
-
import type { SearchResultRecord } from "@/
|
|
8
|
+
import type { SearchResultRecord } from "@/types/searchResults.js";
|
|
9
9
|
|
|
10
10
|
export function usePropertyAddresses(
|
|
11
11
|
results: SearchResultRecord[],
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyDetail.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type PropertyImageRecord,
|
|
14
14
|
type PropertyCostRecord,
|
|
15
15
|
type PropertyFeatureRecord,
|
|
16
|
-
} from "@/api/propertyDetailGraphQL";
|
|
16
|
+
} from "@/api/properties/propertyDetailGraphQL";
|
|
17
17
|
|
|
18
18
|
export interface PropertyDetailState {
|
|
19
19
|
listing: ListingDetail | null;
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Returns a map of propertyId -> "Amenity 1 | Amenity 2 | ..." for use in listing cards.
|
|
4
4
|
*/
|
|
5
5
|
import { useState, useEffect } from "react";
|
|
6
|
-
import { fetchFeaturesByPropertyId } from "@/api/propertyDetailGraphQL";
|
|
6
|
+
import { fetchFeaturesByPropertyId } from "@/api/properties/propertyDetailGraphQL";
|
|
7
7
|
import { getPropertyIdFromRecord } from "@/hooks/usePropertyPrimaryImages";
|
|
8
|
-
import type { SearchResultRecord } from "@/
|
|
8
|
+
import type { SearchResultRecord } from "@/types/searchResults.js";
|
|
9
9
|
|
|
10
10
|
const AMENITIES_SEPARATOR = " | ";
|
|
11
11
|
|
|
@@ -5,8 +5,8 @@ import { useState, useEffect, useRef, useCallback } from "react";
|
|
|
5
5
|
import {
|
|
6
6
|
queryPropertyListingsGraphQL,
|
|
7
7
|
type PropertyListingFilters,
|
|
8
|
-
} from "@/api/propertyListingGraphQL";
|
|
9
|
-
import type { SearchResultRecord } from "@/
|
|
8
|
+
} from "@/api/properties/propertyListingGraphQL";
|
|
9
|
+
import type { SearchResultRecord } from "@/types/searchResults.js";
|
|
10
10
|
|
|
11
11
|
export function usePropertyListingSearch(
|
|
12
12
|
searchQuery: string,
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* and returns map markers (one pin per property in the current window).
|
|
4
4
|
*/
|
|
5
5
|
import { useState, useEffect } from "react";
|
|
6
|
-
import { fetchPropertyAddresses } from "@/api/propertyDetailGraphQL";
|
|
6
|
+
import { fetchPropertyAddresses } from "@/api/properties/propertyDetailGraphQL";
|
|
7
7
|
import { geocodeAddress, getStateZipFromAddress } from "@/utils/geocode";
|
|
8
8
|
import { getPropertyIdFromRecord } from "@/hooks/usePropertyPrimaryImages";
|
|
9
|
-
import type { SearchResultRecord } from "@/
|
|
10
|
-
import type { MapMarker } from "@/components/PropertyMap";
|
|
9
|
+
import type { SearchResultRecord } from "@/types/searchResults.js";
|
|
10
|
+
import type { MapMarker } from "@/components/properties/PropertyMap";
|
|
11
11
|
|
|
12
12
|
function getListingName(record: {
|
|
13
13
|
fields?: Record<string, { value?: unknown; displayValue?: string | null }>;
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Returns a map of propertyId -> imageUrl for use in listing cards.
|
|
4
4
|
*/
|
|
5
5
|
import { useState, useEffect } from "react";
|
|
6
|
-
import { fetchPrimaryImagesByPropertyIds } from "@/api/propertyDetailGraphQL";
|
|
7
|
-
import type { SearchResultRecord } from "@/
|
|
6
|
+
import { fetchPrimaryImagesByPropertyIds } from "@/api/properties/propertyDetailGraphQL";
|
|
7
|
+
import type { SearchResultRecord } from "@/types/searchResults.js";
|
|
8
8
|
|
|
9
9
|
export function getPropertyIdFromRecord(record: {
|
|
10
10
|
fields?: Record<string, { value?: unknown }>;
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Application.tsx
CHANGED
|
@@ -10,9 +10,9 @@ import {
|
|
|
10
10
|
fetchListingById,
|
|
11
11
|
fetchPropertyById,
|
|
12
12
|
fetchPrimaryImagesByPropertyIds,
|
|
13
|
-
} from "@/api/propertyDetailGraphQL";
|
|
14
|
-
import { createApplicationRecord } from "@/api/applicationApi";
|
|
15
|
-
import { useAuth } from "
|
|
13
|
+
} from "@/api/properties/propertyDetailGraphQL";
|
|
14
|
+
import { createApplicationRecord } from "@/api/applications/applicationApi";
|
|
15
|
+
import { useAuth } from "@/features/authentication/context/AuthContext";
|
|
16
16
|
import { fetchUserContact } from "../features/authentication/api/userProfileApi";
|
|
17
17
|
|
|
18
18
|
function ApplicationSkeleton() {
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Dashboard.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Card, CardContent } from "@/components/ui/card";
|
|
2
2
|
import { Link } from "react-router";
|
|
3
|
-
import MaintenanceRequestList from "@/components/MaintenanceRequestList";
|
|
4
|
-
import { WeatherWidget } from "@/components/WeatherWidget";
|
|
3
|
+
import MaintenanceRequestList from "@/components/maintenanceRequests/MaintenanceRequestList";
|
|
4
|
+
import { WeatherWidget } from "@/components/dashboard/WeatherWidget";
|
|
5
5
|
import { useMaintenanceRequests } from "@/hooks/useMaintenanceRequests";
|
|
6
6
|
|
|
7
7
|
export default function Dashboard() {
|
|
@@ -10,8 +10,10 @@ import {
|
|
|
10
10
|
} from "@/hooks/usePropertyPrimaryImages";
|
|
11
11
|
import { usePropertyAddresses } from "@/hooks/usePropertyAddresses";
|
|
12
12
|
import { usePropertyListingAmenities } from "@/hooks/usePropertyListingAmenities";
|
|
13
|
-
import PropertyListingCard, {
|
|
14
|
-
|
|
13
|
+
import PropertyListingCard, {
|
|
14
|
+
PropertyListingCardSkeleton,
|
|
15
|
+
} from "@/components/properties/PropertyListingCard";
|
|
16
|
+
import type { SearchResultRecord } from "@/types/searchResults.js";
|
|
15
17
|
import { createNewsletterLead } from "@/api/leadApi";
|
|
16
18
|
import {
|
|
17
19
|
Phone,
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Maintenance.tsx
CHANGED
|
@@ -5,10 +5,10 @@ import { Label } from "@/components/ui/label";
|
|
|
5
5
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
|
6
6
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
7
7
|
import { Calendar, ArrowRight } from "lucide-react";
|
|
8
|
-
import MaintenanceRequestList from "@/components/MaintenanceRequestList";
|
|
8
|
+
import MaintenanceRequestList from "@/components/maintenanceRequests/MaintenanceRequestList";
|
|
9
9
|
import { SkeletonListRows, SkeletonField } from "@/components/SkeletonPrimitives";
|
|
10
10
|
import { useMaintenanceRequests } from "@/hooks/useMaintenanceRequests";
|
|
11
|
-
import { createMaintenanceRequest } from "@/api/maintenanceRequestApi";
|
|
11
|
+
import { createMaintenanceRequest } from "@/api/maintenanceRequests/maintenanceRequestApi";
|
|
12
12
|
import { useAuth } from "@/features/authentication/context/AuthContext";
|
|
13
13
|
|
|
14
14
|
const TYPE_OPTIONS = [
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyDetails.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { useParams, Link } from "react-router";
|
|
|
2
2
|
import { Button } from "@/components/ui/button";
|
|
3
3
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
|
4
4
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
5
|
-
import PropertyMap from "@/components/PropertyMap";
|
|
5
|
+
import PropertyMap from "@/components/properties/PropertyMap";
|
|
6
6
|
import { usePropertyDetail } from "@/hooks/usePropertyDetail";
|
|
7
7
|
import { useGeocode } from "@/hooks/useGeocode";
|
|
8
8
|
|
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertySearch.tsx
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { useState, useCallback, useMemo, useEffect } from "react";
|
|
6
6
|
import { useSearchParams } from "react-router";
|
|
7
|
-
import { DEFAULT_PAGE_SIZE } from "@/
|
|
7
|
+
import { DEFAULT_PAGE_SIZE } from "@/constants/propertyListing";
|
|
8
8
|
import { usePropertyListingSearch } from "@/hooks/usePropertyListingSearch";
|
|
9
9
|
import {
|
|
10
10
|
usePropertyPrimaryImages,
|
|
@@ -13,17 +13,19 @@ import {
|
|
|
13
13
|
import { usePropertyAddresses } from "@/hooks/usePropertyAddresses";
|
|
14
14
|
import { usePropertyListingAmenities } from "@/hooks/usePropertyListingAmenities";
|
|
15
15
|
import { usePropertyMapMarkers } from "@/hooks/usePropertyMapMarkers";
|
|
16
|
-
import
|
|
17
|
-
import PropertyListingCard, {
|
|
16
|
+
import PropertyListingSearchPagination from "@/components/properties/PropertyListingSearchPagination";
|
|
17
|
+
import PropertyListingCard, {
|
|
18
|
+
PropertyListingCardSkeleton,
|
|
19
|
+
} from "@/components/properties/PropertyListingCard";
|
|
18
20
|
import PropertySearchFilters, {
|
|
19
21
|
type BedroomFilter,
|
|
20
22
|
type SortBy,
|
|
21
|
-
} from "@/components/PropertySearchFilters";
|
|
22
|
-
import PropertyMap from "@/components/PropertyMap";
|
|
23
|
-
import type { MapMarker, MapBounds } from "@/components/PropertyMap";
|
|
23
|
+
} from "@/components/properties/PropertySearchFilters";
|
|
24
|
+
import PropertyMap from "@/components/properties/PropertyMap";
|
|
25
|
+
import type { MapMarker, MapBounds } from "@/components/properties/PropertyMap";
|
|
24
26
|
import PropertySearchPlaceholder from "@/pages/PropertySearchPlaceholder";
|
|
25
27
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
26
|
-
import type { SearchResultRecord } from "@/
|
|
28
|
+
import type { SearchResultRecord } from "@/types/searchResults.js";
|
|
27
29
|
|
|
28
30
|
/** Fallback map center when there are no geocoded markers yet. Zoom 7 ≈ 100-mile radius view. */
|
|
29
31
|
const MAP_CENTER_FALLBACK: [number, number] = [37.7897484, -122.3998086];
|
|
@@ -267,7 +269,7 @@ export default function PropertySearch() {
|
|
|
267
269
|
{searchQuery.trim() ? ` matching "${searchQuery.trim()}"` : ""}
|
|
268
270
|
</h2>
|
|
269
271
|
<div className="flex flex-wrap items-center gap-2">
|
|
270
|
-
<
|
|
272
|
+
<div className="text-sm text-muted-foreground">
|
|
271
273
|
{apiUnavailable ? (
|
|
272
274
|
"Placeholder (API unavailable)"
|
|
273
275
|
) : resultsLoading ? (
|
|
@@ -277,7 +279,7 @@ export default function PropertySearch() {
|
|
|
277
279
|
) : (
|
|
278
280
|
`${sortedResults.length} result(s)`
|
|
279
281
|
)}
|
|
280
|
-
</
|
|
282
|
+
</div>
|
|
281
283
|
{mapBounds != null && sortedResults.length > 0 && !resultsLoading && (
|
|
282
284
|
<button
|
|
283
285
|
type="button"
|
|
@@ -345,7 +347,7 @@ export default function PropertySearch() {
|
|
|
345
347
|
})}
|
|
346
348
|
</ul>
|
|
347
349
|
<div className="mt-4">
|
|
348
|
-
<
|
|
350
|
+
<PropertyListingSearchPagination
|
|
349
351
|
currentPageToken={currentPageToken}
|
|
350
352
|
nextPageToken={nextPageToken}
|
|
351
353
|
previousPageToken={previousPageToken}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { RouteObject } from 'react-router';
|
|
2
2
|
import Home from '@/pages/Home';
|
|
3
3
|
import NotFound from '@/pages/NotFound';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { Suspense } from "react";
|
|
7
|
-
import LoadingFallback from "./features/global-search/components/shared/LoadingFallback";
|
|
4
|
+
import AccountSearch from "./features/object-search/__examples__/pages/AccountSearch";
|
|
5
|
+
import AccountObjectDetail from "./features/object-search/__examples__/pages/AccountObjectDetailPage";
|
|
8
6
|
import Login from "./features/authentication/pages/Login";
|
|
9
7
|
import Register from "./features/authentication/pages/Register";
|
|
10
8
|
import ForgotPassword from "./features/authentication/pages/ForgotPassword";
|
|
@@ -37,22 +35,12 @@ export const routes: RouteObject[] = [
|
|
|
37
35
|
element: <NotFound />
|
|
38
36
|
},
|
|
39
37
|
{
|
|
40
|
-
path: "
|
|
41
|
-
element:
|
|
42
|
-
<Suspense fallback={<LoadingFallback />}>
|
|
43
|
-
<GlobalSearch />
|
|
44
|
-
</Suspense>
|
|
45
|
-
),
|
|
46
|
-
handle: { showInNavigation: false }
|
|
38
|
+
path: "accounts/:recordId",
|
|
39
|
+
element: <AccountObjectDetail />
|
|
47
40
|
},
|
|
48
41
|
{
|
|
49
|
-
path: "
|
|
50
|
-
element:
|
|
51
|
-
<Suspense fallback={<LoadingFallback />}>
|
|
52
|
-
<DetailPage />
|
|
53
|
-
</Suspense>
|
|
54
|
-
),
|
|
55
|
-
handle: { showInNavigation: false }
|
|
42
|
+
path: "accounts",
|
|
43
|
+
element: <AccountSearch />
|
|
56
44
|
},
|
|
57
45
|
{
|
|
58
46
|
element: <AuthenticationRoute />,
|