@salesforce/webapp-template-app-react-sample-b2e-experimental 1.116.12 → 1.116.13
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 +8 -0
- package/dist/force-app/main/default/webapplications/propertymanagementapp/package.json +3 -3
- package/dist/force-app/main/default/webapplications/propertymanagementapp/src/features/object-search/components/FilterContext.tsx +44 -3
- package/dist/force-app/main/default/webapplications/propertymanagementapp/src/features/object-search/components/filters/NumericRangeFilter.tsx +48 -1
- package/dist/force-app/main/default/webapplications/propertymanagementapp/src/features/object-search/hooks/useObjectSearchParams.ts +86 -5
- package/dist/force-app/main/default/webapplications/propertymanagementapp/src/lib/filterUtils.ts +11 -0
- package/dist/force-app/main/default/webapplications/propertymanagementapp/src/pages/ApplicationSearch.tsx +15 -5
- package/dist/force-app/main/default/webapplications/propertymanagementapp/src/pages/MaintenanceRequestSearch.tsx +17 -7
- package/dist/force-app/main/default/webapplications/propertymanagementapp/src/pages/MaintenanceWorkerSearch.tsx +23 -5
- package/dist/force-app/main/default/webapplications/propertymanagementapp/src/pages/PropertySearch.tsx +23 -6
- package/dist/package-lock.json +2 -2
- package/dist/package.json +1 -1
- package/package.json +2 -2
package/dist/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.116.13](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.116.12...v1.116.13) (2026-03-27)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
## [1.116.12](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.116.11...v1.116.12) (2026-03-27)
|
|
7
15
|
|
|
8
16
|
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"graphql:schema": "node scripts/get-graphql-schema.mjs"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@salesforce/sdk-data": "^1.116.
|
|
19
|
-
"@salesforce/webapp-experimental": "^1.116.
|
|
18
|
+
"@salesforce/sdk-data": "^1.116.13",
|
|
19
|
+
"@salesforce/webapp-experimental": "^1.116.13",
|
|
20
20
|
"@tailwindcss/vite": "^4.1.17",
|
|
21
21
|
"class-variance-authority": "^0.7.1",
|
|
22
22
|
"clsx": "^2.1.1",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@graphql-eslint/eslint-plugin": "^4.1.0",
|
|
44
44
|
"@graphql-tools/utils": "^11.0.0",
|
|
45
45
|
"@playwright/test": "^1.49.0",
|
|
46
|
-
"@salesforce/vite-plugin-webapp-experimental": "^1.116.
|
|
46
|
+
"@salesforce/vite-plugin-webapp-experimental": "^1.116.13",
|
|
47
47
|
"@testing-library/jest-dom": "^6.6.3",
|
|
48
48
|
"@testing-library/react": "^16.1.0",
|
|
49
49
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -7,6 +7,9 @@ interface FilterContextValue {
|
|
|
7
7
|
onFilterChange: (field: string, value: ActiveFilterValue | undefined) => void;
|
|
8
8
|
onFilterRemove: (field: string) => void;
|
|
9
9
|
onReset: () => void;
|
|
10
|
+
onApply: () => void;
|
|
11
|
+
hasPendingChanges: boolean;
|
|
12
|
+
hasValidationError: boolean;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
const FilterContext = createContext<FilterContextValue | null>(null);
|
|
@@ -16,6 +19,9 @@ interface FilterProviderProps {
|
|
|
16
19
|
onFilterChange: (field: string, value: ActiveFilterValue | undefined) => void;
|
|
17
20
|
onFilterRemove: (field: string) => void;
|
|
18
21
|
onReset: () => void;
|
|
22
|
+
onApply?: () => void;
|
|
23
|
+
hasPendingChanges?: boolean;
|
|
24
|
+
hasValidationError?: boolean;
|
|
19
25
|
children: ReactNode;
|
|
20
26
|
}
|
|
21
27
|
|
|
@@ -24,10 +30,23 @@ export function FilterProvider({
|
|
|
24
30
|
onFilterChange,
|
|
25
31
|
onFilterRemove,
|
|
26
32
|
onReset,
|
|
33
|
+
onApply,
|
|
34
|
+
hasPendingChanges = false,
|
|
35
|
+
hasValidationError = false,
|
|
27
36
|
children,
|
|
28
37
|
}: FilterProviderProps) {
|
|
29
38
|
return (
|
|
30
|
-
<FilterContext.Provider
|
|
39
|
+
<FilterContext.Provider
|
|
40
|
+
value={{
|
|
41
|
+
filters,
|
|
42
|
+
onFilterChange,
|
|
43
|
+
onFilterRemove,
|
|
44
|
+
onReset,
|
|
45
|
+
onApply: onApply ?? (() => {}),
|
|
46
|
+
hasPendingChanges,
|
|
47
|
+
hasValidationError,
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
31
50
|
{children}
|
|
32
51
|
</FilterContext.Provider>
|
|
33
52
|
);
|
|
@@ -56,8 +75,14 @@ export function useFilterField(field: string) {
|
|
|
56
75
|
}
|
|
57
76
|
|
|
58
77
|
export function useFilterPanel() {
|
|
59
|
-
const { filters, onReset } = useFilterContext();
|
|
60
|
-
return {
|
|
78
|
+
const { filters, onReset, onApply, hasPendingChanges, hasValidationError } = useFilterContext();
|
|
79
|
+
return {
|
|
80
|
+
hasActiveFilters: filters.length > 0,
|
|
81
|
+
hasPendingChanges,
|
|
82
|
+
hasValidationError,
|
|
83
|
+
resetAll: onReset,
|
|
84
|
+
apply: onApply,
|
|
85
|
+
};
|
|
61
86
|
}
|
|
62
87
|
|
|
63
88
|
type FilterResetButtonProps = Omit<React.ComponentProps<typeof Button>, "onClick">;
|
|
@@ -71,3 +96,19 @@ export function FilterResetButton({ children, ...props }: FilterResetButtonProps
|
|
|
71
96
|
</Button>
|
|
72
97
|
);
|
|
73
98
|
}
|
|
99
|
+
|
|
100
|
+
type FilterApplyButtonProps = Omit<React.ComponentProps<typeof Button>, "onClick" | "disabled">;
|
|
101
|
+
|
|
102
|
+
export function FilterApplyButton({ children, ...props }: FilterApplyButtonProps) {
|
|
103
|
+
const { apply, hasPendingChanges, hasValidationError } = useFilterPanel();
|
|
104
|
+
return (
|
|
105
|
+
<Button
|
|
106
|
+
onClick={apply}
|
|
107
|
+
disabled={!hasPendingChanges || hasValidationError}
|
|
108
|
+
aria-label="Apply filters"
|
|
109
|
+
{...props}
|
|
110
|
+
>
|
|
111
|
+
{children ?? "Apply"}
|
|
112
|
+
</Button>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Input } from "../../../../components/ui/input";
|
|
2
2
|
import { Label } from "../../../../components/ui/label";
|
|
3
|
+
import { toast } from "sonner";
|
|
3
4
|
import { cn } from "../../../../lib/utils";
|
|
4
5
|
import { useFilterField } from "../FilterContext";
|
|
5
6
|
import type { ActiveFilterValue } from "../../utils/filterUtils";
|
|
@@ -8,6 +9,8 @@ interface NumericRangeFilterProps extends Omit<React.ComponentProps<"div">, "onC
|
|
|
8
9
|
field: string;
|
|
9
10
|
label: string;
|
|
10
11
|
helpText?: string;
|
|
12
|
+
minInputProps?: React.ComponentProps<typeof Input>;
|
|
13
|
+
maxInputProps?: React.ComponentProps<typeof Input>;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export function NumericRangeFilter({
|
|
@@ -15,13 +18,22 @@ export function NumericRangeFilter({
|
|
|
15
18
|
label,
|
|
16
19
|
helpText,
|
|
17
20
|
className,
|
|
21
|
+
minInputProps,
|
|
22
|
+
maxInputProps,
|
|
18
23
|
...props
|
|
19
24
|
}: NumericRangeFilterProps) {
|
|
20
25
|
const { value, onChange } = useFilterField(field);
|
|
21
26
|
return (
|
|
22
27
|
<div className={cn("space-y-1.5", className)} {...props}>
|
|
23
28
|
<Label>{label}</Label>
|
|
24
|
-
<NumericRangeFilterInputs
|
|
29
|
+
<NumericRangeFilterInputs
|
|
30
|
+
field={field}
|
|
31
|
+
label={label}
|
|
32
|
+
value={value}
|
|
33
|
+
onChange={onChange}
|
|
34
|
+
minInputProps={minInputProps}
|
|
35
|
+
maxInputProps={maxInputProps}
|
|
36
|
+
/>
|
|
25
37
|
{helpText && <p className="text-xs text-muted-foreground">{helpText}</p>}
|
|
26
38
|
</div>
|
|
27
39
|
);
|
|
@@ -46,6 +58,24 @@ export function NumericRangeFilterInputs({
|
|
|
46
58
|
maxInputProps,
|
|
47
59
|
...props
|
|
48
60
|
}: NumericRangeFilterInputsProps) {
|
|
61
|
+
const validateNumericRangeFilter = (filter: ActiveFilterValue) => {
|
|
62
|
+
if (filter.type !== "numeric") return null;
|
|
63
|
+
|
|
64
|
+
const min = filter.min?.trim();
|
|
65
|
+
const max = filter.max?.trim();
|
|
66
|
+
const filterLabel = filter.label || filter.field;
|
|
67
|
+
|
|
68
|
+
if (!min || !max) return null;
|
|
69
|
+
|
|
70
|
+
const minValue = Number(min);
|
|
71
|
+
const maxValue = Number(max);
|
|
72
|
+
if (!Number.isNaN(minValue) && !Number.isNaN(maxValue) && minValue >= maxValue) {
|
|
73
|
+
return `${filterLabel}: minimum value must be less than maximum value.`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return null;
|
|
77
|
+
};
|
|
78
|
+
|
|
49
79
|
const handleChange = (bound: "min" | "max", v: string) => {
|
|
50
80
|
const next = {
|
|
51
81
|
field,
|
|
@@ -55,6 +85,7 @@ export function NumericRangeFilterInputs({
|
|
|
55
85
|
max: value?.max ?? "",
|
|
56
86
|
[bound]: v,
|
|
57
87
|
};
|
|
88
|
+
|
|
58
89
|
if (!next.min && !next.max) {
|
|
59
90
|
onChange(undefined);
|
|
60
91
|
} else {
|
|
@@ -62,6 +93,20 @@ export function NumericRangeFilterInputs({
|
|
|
62
93
|
}
|
|
63
94
|
};
|
|
64
95
|
|
|
96
|
+
const handleBlur = (bound: "min" | "max", currentValue: string) => {
|
|
97
|
+
const next = {
|
|
98
|
+
field,
|
|
99
|
+
label,
|
|
100
|
+
type: "numeric" as const,
|
|
101
|
+
min: bound === "min" ? currentValue : (value?.min ?? ""),
|
|
102
|
+
max: bound === "max" ? currentValue : (value?.max ?? ""),
|
|
103
|
+
};
|
|
104
|
+
const validationError = validateNumericRangeFilter(next);
|
|
105
|
+
if (validationError) {
|
|
106
|
+
toast.error("Invalid range filter", { description: validationError });
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
65
110
|
return (
|
|
66
111
|
<div className={cn("flex gap-2", className)} {...props}>
|
|
67
112
|
<Input
|
|
@@ -69,6 +114,7 @@ export function NumericRangeFilterInputs({
|
|
|
69
114
|
placeholder="Min"
|
|
70
115
|
value={value?.min ?? ""}
|
|
71
116
|
onChange={(e) => handleChange("min", e.target.value)}
|
|
117
|
+
onBlur={(e) => handleBlur("min", e.target.value)}
|
|
72
118
|
aria-label={`${label} minimum`}
|
|
73
119
|
{...minInputProps}
|
|
74
120
|
/>
|
|
@@ -77,6 +123,7 @@ export function NumericRangeFilterInputs({
|
|
|
77
123
|
placeholder="Max"
|
|
78
124
|
value={value?.max ?? ""}
|
|
79
125
|
onChange={(e) => handleChange("max", e.target.value)}
|
|
126
|
+
onBlur={(e) => handleBlur("max", e.target.value)}
|
|
80
127
|
aria-label={`${label} maximum`}
|
|
81
128
|
{...maxInputProps}
|
|
82
129
|
/>
|
|
@@ -20,6 +20,11 @@ export interface UseObjectSearchParamsReturn<TFilter, TOrderBy> {
|
|
|
20
20
|
set: (field: string, value: ActiveFilterValue | undefined) => void;
|
|
21
21
|
remove: (field: string) => void;
|
|
22
22
|
};
|
|
23
|
+
filterState: {
|
|
24
|
+
apply: () => void;
|
|
25
|
+
hasPendingChanges: boolean;
|
|
26
|
+
hasValidationError: boolean;
|
|
27
|
+
};
|
|
23
28
|
sort: {
|
|
24
29
|
current: SortState | null;
|
|
25
30
|
set: (sort: SortState | null) => void;
|
|
@@ -36,6 +41,40 @@ export interface UseObjectSearchParamsReturn<TFilter, TOrderBy> {
|
|
|
36
41
|
resetAll: () => void;
|
|
37
42
|
}
|
|
38
43
|
|
|
44
|
+
export interface UseObjectSearchParamsOptions {
|
|
45
|
+
filterSyncMode?: "immediate" | "manual";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function areFiltersEqual(left: ActiveFilterValue[], right: ActiveFilterValue[]) {
|
|
49
|
+
if (left.length !== right.length) return false;
|
|
50
|
+
const normalize = (filters: ActiveFilterValue[]) =>
|
|
51
|
+
[...filters]
|
|
52
|
+
.sort((a, b) => a.field.localeCompare(b.field))
|
|
53
|
+
.map((filter) => ({
|
|
54
|
+
field: filter.field,
|
|
55
|
+
type: filter.type,
|
|
56
|
+
value: filter.value ?? "",
|
|
57
|
+
min: filter.min ?? "",
|
|
58
|
+
max: filter.max ?? "",
|
|
59
|
+
}));
|
|
60
|
+
return JSON.stringify(normalize(left)) === JSON.stringify(normalize(right));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function hasFilterValidationError(filters: ActiveFilterValue[]) {
|
|
64
|
+
for (const filter of filters) {
|
|
65
|
+
if (filter.type !== "numeric") continue;
|
|
66
|
+
const min = filter.min?.trim();
|
|
67
|
+
const max = filter.max?.trim();
|
|
68
|
+
if (!min || !max) continue;
|
|
69
|
+
const minValue = Number(min);
|
|
70
|
+
const maxValue = Number(max);
|
|
71
|
+
if (!Number.isNaN(minValue) && !Number.isNaN(maxValue) && minValue >= maxValue) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
39
78
|
/**
|
|
40
79
|
* Manages filter, sort, and cursor-based pagination state for an object search page.
|
|
41
80
|
*
|
|
@@ -59,7 +98,11 @@ export function useObjectSearchParams<TFilter, TOrderBy>(
|
|
|
59
98
|
filterConfigs: FilterFieldConfig[],
|
|
60
99
|
_sortConfigs?: SortFieldConfig[],
|
|
61
100
|
paginationConfig?: PaginationConfig,
|
|
101
|
+
options?: UseObjectSearchParamsOptions,
|
|
62
102
|
) {
|
|
103
|
+
const filterSyncMode = options?.filterSyncMode ?? "immediate";
|
|
104
|
+
const isManualFilterSync = filterSyncMode === "manual";
|
|
105
|
+
|
|
63
106
|
const defaultPageSize = paginationConfig?.defaultPageSize ?? 10;
|
|
64
107
|
const validPageSizes = useMemo(
|
|
65
108
|
() => paginationConfig?.validPageSizes ?? [defaultPageSize],
|
|
@@ -76,6 +119,7 @@ export function useObjectSearchParams<TFilter, TOrderBy>(
|
|
|
76
119
|
);
|
|
77
120
|
|
|
78
121
|
const [filters, setFilters] = useState<ActiveFilterValue[]>(initial.filters);
|
|
122
|
+
const [appliedFilters, setAppliedFilters] = useState<ActiveFilterValue[]>(initial.filters);
|
|
79
123
|
const [sort, setLocalSort] = useState<SortState | null>(initial.sort);
|
|
80
124
|
|
|
81
125
|
// Pagination — cursor-based with a stack to support "previous page" navigation.
|
|
@@ -126,6 +170,15 @@ export function useObjectSearchParams<TFilter, TOrderBy>(
|
|
|
126
170
|
|
|
127
171
|
const setFilter = useCallback(
|
|
128
172
|
(field: string, value: ActiveFilterValue | undefined) => {
|
|
173
|
+
if (isManualFilterSync) {
|
|
174
|
+
setFilters((prev) => {
|
|
175
|
+
const next = prev.filter((f) => f.field !== field);
|
|
176
|
+
if (value) next.push(value);
|
|
177
|
+
return next;
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
129
182
|
const { sort: s, pageSize: ps } = stateRef.current;
|
|
130
183
|
setFilters((prev) => {
|
|
131
184
|
const next = prev.filter((f) => f.field !== field);
|
|
@@ -135,11 +188,16 @@ export function useObjectSearchParams<TFilter, TOrderBy>(
|
|
|
135
188
|
});
|
|
136
189
|
resetPagination();
|
|
137
190
|
},
|
|
138
|
-
[resetPagination],
|
|
191
|
+
[isManualFilterSync, resetPagination],
|
|
139
192
|
);
|
|
140
193
|
|
|
141
194
|
const removeFilter = useCallback(
|
|
142
195
|
(field: string) => {
|
|
196
|
+
if (isManualFilterSync) {
|
|
197
|
+
setFilters((prev) => prev.filter((f) => f.field !== field));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
143
201
|
const { sort: s, pageSize: ps } = stateRef.current;
|
|
144
202
|
setFilters((prev) => {
|
|
145
203
|
const next = prev.filter((f) => f.field !== field);
|
|
@@ -148,9 +206,17 @@ export function useObjectSearchParams<TFilter, TOrderBy>(
|
|
|
148
206
|
});
|
|
149
207
|
resetPagination();
|
|
150
208
|
},
|
|
151
|
-
[resetPagination],
|
|
209
|
+
[isManualFilterSync, resetPagination],
|
|
152
210
|
);
|
|
153
211
|
|
|
212
|
+
const applyFilters = useCallback(() => {
|
|
213
|
+
if (!isManualFilterSync) return;
|
|
214
|
+
const { filters: nextFilters, sort: s, pageSize: ps } = stateRef.current;
|
|
215
|
+
setAppliedFilters(nextFilters);
|
|
216
|
+
resetPagination();
|
|
217
|
+
syncToUrl(nextFilters, s, ps);
|
|
218
|
+
}, [isManualFilterSync, resetPagination, syncToUrl]);
|
|
219
|
+
|
|
154
220
|
// -- Sort callback ----------------------------------------------------------
|
|
155
221
|
|
|
156
222
|
const setSort = useCallback(
|
|
@@ -167,6 +233,7 @@ export function useObjectSearchParams<TFilter, TOrderBy>(
|
|
|
167
233
|
|
|
168
234
|
const resetAll = useCallback(() => {
|
|
169
235
|
setFilters([]);
|
|
236
|
+
setAppliedFilters([]);
|
|
170
237
|
setLocalSort(null);
|
|
171
238
|
resetPagination();
|
|
172
239
|
syncToUrl([], null, defaultPageSize, 0);
|
|
@@ -216,8 +283,8 @@ export function useObjectSearchParams<TFilter, TOrderBy>(
|
|
|
216
283
|
// Translate local filter/sort state into API-ready `where` and `orderBy`.
|
|
217
284
|
|
|
218
285
|
const where = useMemo(
|
|
219
|
-
() => buildFilter<TFilter>(filters, filterConfigs),
|
|
220
|
-
[filters, filterConfigs],
|
|
286
|
+
() => buildFilter<TFilter>(isManualFilterSync ? appliedFilters : filters, filterConfigs),
|
|
287
|
+
[appliedFilters, filters, filterConfigs, isManualFilterSync],
|
|
221
288
|
);
|
|
222
289
|
|
|
223
290
|
const orderBy = useMemo(() => buildOrderBy<TOrderBy>(sort), [sort]);
|
|
@@ -229,10 +296,23 @@ export function useObjectSearchParams<TFilter, TOrderBy>(
|
|
|
229
296
|
// causing unnecessary re-renders.
|
|
230
297
|
|
|
231
298
|
const filtersGroup = useMemo(
|
|
232
|
-
() => ({
|
|
299
|
+
() => ({
|
|
300
|
+
active: filters,
|
|
301
|
+
set: setFilter,
|
|
302
|
+
remove: removeFilter,
|
|
303
|
+
}),
|
|
233
304
|
[filters, setFilter, removeFilter],
|
|
234
305
|
);
|
|
235
306
|
|
|
307
|
+
const filterState = useMemo(
|
|
308
|
+
() => ({
|
|
309
|
+
apply: applyFilters,
|
|
310
|
+
hasPendingChanges: isManualFilterSync ? !areFiltersEqual(filters, appliedFilters) : false,
|
|
311
|
+
hasValidationError: hasFilterValidationError(filters),
|
|
312
|
+
}),
|
|
313
|
+
[applyFilters, isManualFilterSync, filters, appliedFilters],
|
|
314
|
+
);
|
|
315
|
+
|
|
236
316
|
const sortGroup = useMemo(() => ({ current: sort, set: setSort }), [sort, setSort]);
|
|
237
317
|
|
|
238
318
|
const query = useMemo(() => ({ where, orderBy }), [where, orderBy]);
|
|
@@ -244,6 +324,7 @@ export function useObjectSearchParams<TFilter, TOrderBy>(
|
|
|
244
324
|
|
|
245
325
|
return {
|
|
246
326
|
filters: filtersGroup,
|
|
327
|
+
filterState,
|
|
247
328
|
sort: sortGroup,
|
|
248
329
|
query,
|
|
249
330
|
pagination,
|
package/dist/force-app/main/default/webapplications/propertymanagementapp/src/lib/filterUtils.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
|
|
3
|
+
export const nonNegativeNumberInputProps: ComponentProps<"input"> = {
|
|
4
|
+
min: 0,
|
|
5
|
+
onKeyDown: (event) => {
|
|
6
|
+
if (event.key === "-" || event.key === "Minus") event.preventDefault();
|
|
7
|
+
},
|
|
8
|
+
onPaste: (event) => {
|
|
9
|
+
if (event.clipboardData.getData("text").includes("-")) event.preventDefault();
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -16,12 +16,13 @@ import type { ApplicationSearchNode } from "../api/applications/applicationSearc
|
|
|
16
16
|
import { PageHeader } from "../components/layout/PageHeader";
|
|
17
17
|
import { PageContainer } from "../components/layout/PageContainer";
|
|
18
18
|
import {
|
|
19
|
+
FilterApplyButton,
|
|
19
20
|
FilterProvider,
|
|
20
21
|
FilterResetButton,
|
|
21
22
|
} from "../features/object-search/components/FilterContext";
|
|
22
23
|
import { FilterRow } from "../components/layout/FilterRow";
|
|
23
24
|
import { SearchFilter } from "../features/object-search/components/filters/SearchFilter";
|
|
24
|
-
import {
|
|
25
|
+
import { MultiSelectFilter } from "../features/object-search/components/filters/MultiSelectFilter";
|
|
25
26
|
import { DateFilter } from "../features/object-search/components/filters/DateFilter";
|
|
26
27
|
import { ObjectSearchErrorState } from "../components/shared/ObjectSearchErrorState";
|
|
27
28
|
import PaginationControls from "../features/object-search/components/PaginationControls";
|
|
@@ -75,14 +76,13 @@ export default function ApplicationSearch() {
|
|
|
75
76
|
ttl: 30_000,
|
|
76
77
|
});
|
|
77
78
|
|
|
78
|
-
const { filters, query, pagination, resetAll } = useObjectSearchParams<
|
|
79
|
+
const { filters, filterState, query, pagination, resetAll } = useObjectSearchParams<
|
|
79
80
|
Application__C_Filter,
|
|
80
81
|
Application__C_OrderBy
|
|
81
|
-
>(FILTER_CONFIGS, SORT_CONFIGS, PAGINATION_CONFIG);
|
|
82
|
+
>(FILTER_CONFIGS, SORT_CONFIGS, PAGINATION_CONFIG, { filterSyncMode: "manual" });
|
|
82
83
|
const effectiveOrderBy: Application__C_OrderBy = query.orderBy ?? {
|
|
83
84
|
CreatedDate: { order: ResultOrder.Desc },
|
|
84
85
|
};
|
|
85
|
-
|
|
86
86
|
const searchKey = `applications:${JSON.stringify({ where: query.where, orderBy: effectiveOrderBy, first: pagination.pageSize, after: pagination.afterCursor })}`;
|
|
87
87
|
const { data, loading, error } = useCachedAsyncData(
|
|
88
88
|
() =>
|
|
@@ -138,6 +138,7 @@ export default function ApplicationSearch() {
|
|
|
138
138
|
<PageHeader title="Applications" description="Manage and review rental applications" />
|
|
139
139
|
<ApplicationSearchFilters
|
|
140
140
|
filters={filters}
|
|
141
|
+
filterState={filterState}
|
|
141
142
|
statusOptions={statusOptions ?? []}
|
|
142
143
|
resetAll={resetAll}
|
|
143
144
|
/>
|
|
@@ -192,10 +193,15 @@ export default function ApplicationSearch() {
|
|
|
192
193
|
|
|
193
194
|
function ApplicationSearchFilters({
|
|
194
195
|
filters,
|
|
196
|
+
filterState,
|
|
195
197
|
statusOptions,
|
|
196
198
|
resetAll,
|
|
197
199
|
}: {
|
|
198
200
|
filters: UseObjectSearchParamsReturn<Application__C_Filter, Application__C_OrderBy>["filters"];
|
|
201
|
+
filterState: UseObjectSearchParamsReturn<
|
|
202
|
+
Application__C_Filter,
|
|
203
|
+
Application__C_OrderBy
|
|
204
|
+
>["filterState"];
|
|
199
205
|
statusOptions: Array<{ value: string; label: string }>;
|
|
200
206
|
resetAll: () => void;
|
|
201
207
|
}) {
|
|
@@ -205,6 +211,9 @@ function ApplicationSearchFilters({
|
|
|
205
211
|
onFilterChange={filters.set}
|
|
206
212
|
onFilterRemove={filters.remove}
|
|
207
213
|
onReset={resetAll}
|
|
214
|
+
onApply={filterState.apply}
|
|
215
|
+
hasPendingChanges={filterState.hasPendingChanges}
|
|
216
|
+
hasValidationError={filterState.hasValidationError}
|
|
208
217
|
>
|
|
209
218
|
<FilterRow ariaLabel="Applications filters">
|
|
210
219
|
<SearchFilter
|
|
@@ -213,7 +222,7 @@ function ApplicationSearchFilters({
|
|
|
213
222
|
placeholder="Search by name..."
|
|
214
223
|
className="w-full sm:w-50"
|
|
215
224
|
/>
|
|
216
|
-
<
|
|
225
|
+
<MultiSelectFilter
|
|
217
226
|
field="Status__c"
|
|
218
227
|
label="Status"
|
|
219
228
|
options={statusOptions ?? []}
|
|
@@ -221,6 +230,7 @@ function ApplicationSearchFilters({
|
|
|
221
230
|
/>
|
|
222
231
|
<DateFilter field="Start_Date__c" label="Start Date" className="w-full sm:w-56" />
|
|
223
232
|
<DateFilter field="CreatedDate" label="Created Date" className="w-full sm:w-56" />
|
|
233
|
+
<FilterApplyButton className="h-8 px-3 rounded-lg bg-purple-600 text-white text-sm font-medium hover:bg-purple-700 disabled:bg-purple-300 disabled:cursor-not-allowed transition-colors" />
|
|
224
234
|
<FilterResetButton />
|
|
225
235
|
</FilterRow>
|
|
226
236
|
</FilterProvider>
|
|
@@ -18,12 +18,13 @@ import type { SortFieldConfig } from "../features/object-search/utils/sortUtils"
|
|
|
18
18
|
import { PageHeader } from "../components/layout/PageHeader";
|
|
19
19
|
import { PageContainer } from "../components/layout/PageContainer";
|
|
20
20
|
import {
|
|
21
|
+
FilterApplyButton,
|
|
21
22
|
FilterProvider,
|
|
22
23
|
FilterResetButton,
|
|
23
24
|
} from "../features/object-search/components/FilterContext";
|
|
24
25
|
import { FilterRow } from "../components/layout/FilterRow";
|
|
25
26
|
import { SearchFilter } from "../features/object-search/components/filters/SearchFilter";
|
|
26
|
-
import {
|
|
27
|
+
import { MultiSelectFilter } from "../features/object-search/components/filters/MultiSelectFilter";
|
|
27
28
|
import { DateFilter } from "../features/object-search/components/filters/DateFilter";
|
|
28
29
|
import { ObjectSearchErrorState } from "../components/shared/ObjectSearchErrorState";
|
|
29
30
|
import PaginationControls from "../features/object-search/components/PaginationControls";
|
|
@@ -101,14 +102,13 @@ export default function MaintenanceRequestSearch() {
|
|
|
101
102
|
{ key: "distinctMaintenanceRequestPriority", ttl: 30_000 },
|
|
102
103
|
);
|
|
103
104
|
|
|
104
|
-
const { filters, query, pagination, resetAll } = useObjectSearchParams<
|
|
105
|
+
const { filters, filterState, query, pagination, resetAll } = useObjectSearchParams<
|
|
105
106
|
Maintenance_Request__C_Filter,
|
|
106
107
|
Maintenance_Request__C_OrderBy
|
|
107
|
-
>(FILTER_CONFIGS, SORT_CONFIGS, PAGINATION_CONFIG);
|
|
108
|
+
>(FILTER_CONFIGS, SORT_CONFIGS, PAGINATION_CONFIG, { filterSyncMode: "manual" });
|
|
108
109
|
const effectiveOrderBy: Maintenance_Request__C_OrderBy = query.orderBy ?? {
|
|
109
110
|
CreatedDate: { order: ResultOrder.Desc },
|
|
110
111
|
};
|
|
111
|
-
|
|
112
112
|
const searchKey = `maintenance-requests:${JSON.stringify({ where: query.where, orderBy: effectiveOrderBy, first: pagination.pageSize, after: pagination.afterCursor })}`;
|
|
113
113
|
const { data, loading, error } = useCachedAsyncData(
|
|
114
114
|
() =>
|
|
@@ -167,6 +167,7 @@ export default function MaintenanceRequestSearch() {
|
|
|
167
167
|
/>
|
|
168
168
|
<MaintenanceRequestSearchFilters
|
|
169
169
|
filters={filters}
|
|
170
|
+
filterState={filterState}
|
|
170
171
|
statusOptions={statusOptions ?? []}
|
|
171
172
|
typeOptions={typeOptions ?? []}
|
|
172
173
|
priorityOptions={priorityOptions ?? []}
|
|
@@ -224,6 +225,7 @@ export default function MaintenanceRequestSearch() {
|
|
|
224
225
|
|
|
225
226
|
function MaintenanceRequestSearchFilters({
|
|
226
227
|
filters,
|
|
228
|
+
filterState,
|
|
227
229
|
statusOptions,
|
|
228
230
|
typeOptions,
|
|
229
231
|
priorityOptions,
|
|
@@ -233,6 +235,10 @@ function MaintenanceRequestSearchFilters({
|
|
|
233
235
|
Maintenance_Request__C_Filter,
|
|
234
236
|
Maintenance_Request__C_OrderBy
|
|
235
237
|
>["filters"];
|
|
238
|
+
filterState: UseObjectSearchParamsReturn<
|
|
239
|
+
Maintenance_Request__C_Filter,
|
|
240
|
+
Maintenance_Request__C_OrderBy
|
|
241
|
+
>["filterState"];
|
|
236
242
|
statusOptions: Array<{ value: string; label: string }>;
|
|
237
243
|
typeOptions: Array<{ value: string; label: string }>;
|
|
238
244
|
priorityOptions: Array<{ value: string; label: string }>;
|
|
@@ -244,6 +250,9 @@ function MaintenanceRequestSearchFilters({
|
|
|
244
250
|
onFilterChange={filters.set}
|
|
245
251
|
onFilterRemove={filters.remove}
|
|
246
252
|
onReset={resetAll}
|
|
253
|
+
onApply={filterState.apply}
|
|
254
|
+
hasPendingChanges={filterState.hasPendingChanges}
|
|
255
|
+
hasValidationError={filterState.hasValidationError}
|
|
247
256
|
>
|
|
248
257
|
<FilterRow ariaLabel="Maintenance Requests filters">
|
|
249
258
|
<SearchFilter
|
|
@@ -252,25 +261,26 @@ function MaintenanceRequestSearchFilters({
|
|
|
252
261
|
placeholder="Search by name..."
|
|
253
262
|
className="w-full sm:w-50"
|
|
254
263
|
/>
|
|
255
|
-
<
|
|
264
|
+
<MultiSelectFilter
|
|
256
265
|
field="Status__c"
|
|
257
266
|
label="Status"
|
|
258
267
|
options={statusOptions}
|
|
259
268
|
className="w-full sm:w-36"
|
|
260
269
|
/>
|
|
261
|
-
<
|
|
270
|
+
<MultiSelectFilter
|
|
262
271
|
field="Type__c"
|
|
263
272
|
label="Type"
|
|
264
273
|
options={typeOptions}
|
|
265
274
|
className="w-full sm:w-36"
|
|
266
275
|
/>
|
|
267
|
-
<
|
|
276
|
+
<MultiSelectFilter
|
|
268
277
|
field="Priority__c"
|
|
269
278
|
label="Priority"
|
|
270
279
|
options={priorityOptions}
|
|
271
280
|
className="w-full sm:w-36"
|
|
272
281
|
/>
|
|
273
282
|
<DateFilter field="Scheduled__c" label="Scheduled" className="w-full sm:w-56" />
|
|
283
|
+
<FilterApplyButton className="h-8 px-3 rounded-lg bg-purple-600 text-white text-sm font-medium hover:bg-purple-700 disabled:bg-purple-300 disabled:cursor-not-allowed transition-colors" />
|
|
274
284
|
<FilterResetButton />
|
|
275
285
|
</FilterRow>
|
|
276
286
|
</FilterProvider>
|
|
@@ -15,12 +15,13 @@ import type { SortFieldConfig } from "../features/object-search/utils/sortUtils"
|
|
|
15
15
|
import { PageHeader } from "../components/layout/PageHeader";
|
|
16
16
|
import { PageContainer } from "../components/layout/PageContainer";
|
|
17
17
|
import {
|
|
18
|
+
FilterApplyButton,
|
|
18
19
|
FilterProvider,
|
|
19
20
|
FilterResetButton,
|
|
20
21
|
} from "../features/object-search/components/FilterContext";
|
|
21
22
|
import { FilterRow } from "../components/layout/FilterRow";
|
|
22
23
|
import { SearchFilter } from "../features/object-search/components/filters/SearchFilter";
|
|
23
|
-
import {
|
|
24
|
+
import { MultiSelectFilter } from "../features/object-search/components/filters/MultiSelectFilter";
|
|
24
25
|
import { TextFilter } from "../features/object-search/components/filters/TextFilter";
|
|
25
26
|
import { NumericRangeFilter } from "../features/object-search/components/filters/NumericRangeFilter";
|
|
26
27
|
import { DateFilter } from "../features/object-search/components/filters/DateFilter";
|
|
@@ -41,6 +42,7 @@ import type {
|
|
|
41
42
|
Maintenance_Worker__C_OrderBy,
|
|
42
43
|
} from "../api/graphql-operations-types";
|
|
43
44
|
import { PAGINATION_CONFIG } from "../lib/constants";
|
|
45
|
+
import { nonNegativeNumberInputProps } from "../lib/filterUtils";
|
|
44
46
|
|
|
45
47
|
const FILTER_CONFIGS: FilterFieldConfig[] = [
|
|
46
48
|
{
|
|
@@ -72,10 +74,10 @@ export default function MaintenanceWorkerSearch() {
|
|
|
72
74
|
ttl: 30_000,
|
|
73
75
|
});
|
|
74
76
|
|
|
75
|
-
const { filters, query, pagination, resetAll } = useObjectSearchParams<
|
|
77
|
+
const { filters, filterState, query, pagination, resetAll } = useObjectSearchParams<
|
|
76
78
|
Maintenance_Worker__C_Filter,
|
|
77
79
|
Maintenance_Worker__C_OrderBy
|
|
78
|
-
>(FILTER_CONFIGS, SORT_CONFIGS, PAGINATION_CONFIG);
|
|
80
|
+
>(FILTER_CONFIGS, SORT_CONFIGS, PAGINATION_CONFIG, { filterSyncMode: "manual" });
|
|
79
81
|
|
|
80
82
|
const searchKey = `maintenance-workers:${JSON.stringify({ where: query.where, orderBy: query.orderBy, first: pagination.pageSize, after: pagination.afterCursor })}`;
|
|
81
83
|
const { data, loading, error } = useCachedAsyncData(
|
|
@@ -109,6 +111,7 @@ export default function MaintenanceWorkerSearch() {
|
|
|
109
111
|
<PageHeader title="Maintenance Workers" description="View and filter maintenance workers" />
|
|
110
112
|
<MaintenanceWorkerSearchFilters
|
|
111
113
|
filters={filters}
|
|
114
|
+
filterState={filterState}
|
|
112
115
|
typeOptions={typeOptions ?? []}
|
|
113
116
|
resetAll={resetAll}
|
|
114
117
|
/>
|
|
@@ -163,6 +166,7 @@ export default function MaintenanceWorkerSearch() {
|
|
|
163
166
|
|
|
164
167
|
function MaintenanceWorkerSearchFilters({
|
|
165
168
|
filters,
|
|
169
|
+
filterState,
|
|
166
170
|
typeOptions,
|
|
167
171
|
resetAll,
|
|
168
172
|
}: {
|
|
@@ -170,6 +174,10 @@ function MaintenanceWorkerSearchFilters({
|
|
|
170
174
|
Maintenance_Worker__C_Filter,
|
|
171
175
|
Maintenance_Worker__C_OrderBy
|
|
172
176
|
>["filters"];
|
|
177
|
+
filterState: UseObjectSearchParamsReturn<
|
|
178
|
+
Maintenance_Worker__C_Filter,
|
|
179
|
+
Maintenance_Worker__C_OrderBy
|
|
180
|
+
>["filterState"];
|
|
173
181
|
typeOptions: Array<{ value: string; label: string }>;
|
|
174
182
|
resetAll: () => void;
|
|
175
183
|
}) {
|
|
@@ -179,6 +187,9 @@ function MaintenanceWorkerSearchFilters({
|
|
|
179
187
|
onFilterChange={filters.set}
|
|
180
188
|
onFilterRemove={filters.remove}
|
|
181
189
|
onReset={resetAll}
|
|
190
|
+
onApply={filterState.apply}
|
|
191
|
+
hasPendingChanges={filterState.hasPendingChanges}
|
|
192
|
+
hasValidationError={filterState.hasValidationError}
|
|
182
193
|
>
|
|
183
194
|
<FilterRow ariaLabel="Maintenance Workers filters">
|
|
184
195
|
<SearchFilter
|
|
@@ -187,7 +198,7 @@ function MaintenanceWorkerSearchFilters({
|
|
|
187
198
|
placeholder="By name, or phone..."
|
|
188
199
|
className="w-full sm:w-50"
|
|
189
200
|
/>
|
|
190
|
-
<
|
|
201
|
+
<MultiSelectFilter
|
|
191
202
|
field="Employment_Type__c"
|
|
192
203
|
label="Employment Type"
|
|
193
204
|
options={typeOptions}
|
|
@@ -199,8 +210,15 @@ function MaintenanceWorkerSearchFilters({
|
|
|
199
210
|
placeholder="Location"
|
|
200
211
|
className="w-full sm:w-50"
|
|
201
212
|
/>
|
|
202
|
-
<NumericRangeFilter
|
|
213
|
+
<NumericRangeFilter
|
|
214
|
+
field="Hourly_Rate__c"
|
|
215
|
+
label="Hourly Rate"
|
|
216
|
+
className="w-full sm:w-50"
|
|
217
|
+
minInputProps={nonNegativeNumberInputProps}
|
|
218
|
+
maxInputProps={nonNegativeNumberInputProps}
|
|
219
|
+
/>
|
|
203
220
|
<DateFilter field="CreatedDate" label="Created Date" className="w-full sm:w-56" />
|
|
221
|
+
<FilterApplyButton className="h-8 px-3 rounded-lg bg-purple-600 text-white text-sm font-medium hover:bg-purple-700 disabled:bg-purple-300 disabled:cursor-not-allowed transition-colors" />
|
|
204
222
|
<FilterResetButton />
|
|
205
223
|
</FilterRow>
|
|
206
224
|
</FilterProvider>
|
|
@@ -17,12 +17,13 @@ import type { Property__C_Filter, Property__C_OrderBy } from "../api/graphql-ope
|
|
|
17
17
|
import { PageHeader } from "../components/layout/PageHeader";
|
|
18
18
|
import { PageContainer } from "../components/layout/PageContainer";
|
|
19
19
|
import {
|
|
20
|
+
FilterApplyButton,
|
|
20
21
|
FilterProvider,
|
|
21
22
|
FilterResetButton,
|
|
22
23
|
} from "../features/object-search/components/FilterContext";
|
|
23
24
|
import { FilterRow } from "../components/layout/FilterRow";
|
|
24
25
|
import { SearchFilter } from "../features/object-search/components/filters/SearchFilter";
|
|
25
|
-
import {
|
|
26
|
+
import { MultiSelectFilter } from "../features/object-search/components/filters/MultiSelectFilter";
|
|
26
27
|
import { NumericRangeFilter } from "../features/object-search/components/filters/NumericRangeFilter";
|
|
27
28
|
import { ObjectSearchErrorState } from "../components/shared/ObjectSearchErrorState";
|
|
28
29
|
import PaginationControls from "../features/object-search/components/PaginationControls";
|
|
@@ -30,6 +31,7 @@ import { PropertyCard } from "../components/properties/PropertyCard";
|
|
|
30
31
|
import { PropertyDetailsModal } from "../components/properties/PropertyDetailsModal";
|
|
31
32
|
import { Skeleton } from "../components/ui/skeleton";
|
|
32
33
|
import { PAGINATION_CONFIG } from "../lib/constants";
|
|
34
|
+
import { nonNegativeNumberInputProps } from "../lib/filterUtils";
|
|
33
35
|
|
|
34
36
|
const FILTER_CONFIGS: FilterFieldConfig[] = [
|
|
35
37
|
{
|
|
@@ -65,10 +67,10 @@ export default function PropertySearch() {
|
|
|
65
67
|
ttl: 30_000,
|
|
66
68
|
});
|
|
67
69
|
|
|
68
|
-
const { filters, query, pagination, resetAll } = useObjectSearchParams<
|
|
70
|
+
const { filters, filterState, query, pagination, resetAll } = useObjectSearchParams<
|
|
69
71
|
Property__C_Filter,
|
|
70
72
|
Property__C_OrderBy
|
|
71
|
-
>(FILTER_CONFIGS, PROPERTY_SORT_CONFIGS, PAGINATION_CONFIG);
|
|
73
|
+
>(FILTER_CONFIGS, PROPERTY_SORT_CONFIGS, PAGINATION_CONFIG, { filterSyncMode: "manual" });
|
|
72
74
|
|
|
73
75
|
const searchKey = `properties:${JSON.stringify({ where: query.where, orderBy: query.orderBy, first: pagination.pageSize, after: pagination.afterCursor })}`;
|
|
74
76
|
const { data, loading, error } = useCachedAsyncData(
|
|
@@ -102,6 +104,7 @@ export default function PropertySearch() {
|
|
|
102
104
|
<PageHeader title="Properties" description="Browse and manage available properties" />
|
|
103
105
|
<PropertySearchFilters
|
|
104
106
|
filters={filters}
|
|
107
|
+
filterState={filterState}
|
|
105
108
|
statusOptions={statusOptions ?? []}
|
|
106
109
|
typeOptions={typeOptions ?? []}
|
|
107
110
|
resetAll={resetAll}
|
|
@@ -148,11 +151,13 @@ export default function PropertySearch() {
|
|
|
148
151
|
|
|
149
152
|
function PropertySearchFilters({
|
|
150
153
|
filters,
|
|
154
|
+
filterState,
|
|
151
155
|
statusOptions,
|
|
152
156
|
typeOptions,
|
|
153
157
|
resetAll,
|
|
154
158
|
}: {
|
|
155
159
|
filters: UseObjectSearchParamsReturn<Property__C_Filter, Property__C_OrderBy>["filters"];
|
|
160
|
+
filterState: UseObjectSearchParamsReturn<Property__C_Filter, Property__C_OrderBy>["filterState"];
|
|
156
161
|
statusOptions: Array<{ value: string; label: string }>;
|
|
157
162
|
typeOptions: Array<{ value: string; label: string }>;
|
|
158
163
|
resetAll: () => void;
|
|
@@ -163,6 +168,9 @@ function PropertySearchFilters({
|
|
|
163
168
|
onFilterChange={filters.set}
|
|
164
169
|
onFilterRemove={filters.remove}
|
|
165
170
|
onReset={resetAll}
|
|
171
|
+
onApply={filterState.apply}
|
|
172
|
+
hasPendingChanges={filterState.hasPendingChanges}
|
|
173
|
+
hasValidationError={filterState.hasValidationError}
|
|
166
174
|
>
|
|
167
175
|
<FilterRow ariaLabel="Properties filters">
|
|
168
176
|
<SearchFilter
|
|
@@ -171,13 +179,13 @@ function PropertySearchFilters({
|
|
|
171
179
|
placeholder="Search by name or address..."
|
|
172
180
|
className="w-full sm:w-50"
|
|
173
181
|
/>
|
|
174
|
-
<
|
|
182
|
+
<MultiSelectFilter
|
|
175
183
|
field="Status__c"
|
|
176
184
|
label="Status"
|
|
177
185
|
options={statusOptions}
|
|
178
186
|
className="w-full sm:w-36"
|
|
179
187
|
/>
|
|
180
|
-
<
|
|
188
|
+
<MultiSelectFilter
|
|
181
189
|
field="Type__c"
|
|
182
190
|
label="Type"
|
|
183
191
|
options={typeOptions}
|
|
@@ -187,8 +195,17 @@ function PropertySearchFilters({
|
|
|
187
195
|
field="Monthly_Rent__c"
|
|
188
196
|
label="Monthly Rent"
|
|
189
197
|
className="w-full sm:w-50"
|
|
198
|
+
minInputProps={nonNegativeNumberInputProps}
|
|
199
|
+
maxInputProps={nonNegativeNumberInputProps}
|
|
190
200
|
/>
|
|
191
|
-
<NumericRangeFilter
|
|
201
|
+
<NumericRangeFilter
|
|
202
|
+
field="Bedrooms__c"
|
|
203
|
+
label="Bedrooms"
|
|
204
|
+
className="w-full sm:w-50"
|
|
205
|
+
minInputProps={nonNegativeNumberInputProps}
|
|
206
|
+
maxInputProps={nonNegativeNumberInputProps}
|
|
207
|
+
/>
|
|
208
|
+
<FilterApplyButton className="h-8 px-3 rounded-lg bg-purple-600 text-white text-sm font-medium hover:bg-purple-700 disabled:bg-purple-300 disabled:cursor-not-allowed transition-colors" />
|
|
192
209
|
<FilterResetButton />
|
|
193
210
|
</FilterRow>
|
|
194
211
|
</FilterProvider>
|
package/dist/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/webapp-template-base-sfdx-project-experimental",
|
|
3
|
-
"version": "1.116.
|
|
3
|
+
"version": "1.116.13",
|
|
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.
|
|
9
|
+
"version": "1.116.13",
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/webapp-template-app-react-sample-b2e-experimental",
|
|
3
|
-
"version": "1.116.
|
|
3
|
+
"version": "1.116.13",
|
|
4
4
|
"description": "Salesforce sample property rental React app",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"author": "",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"clean": "rm -rf dist"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@salesforce/webapp-experimental": "^1.116.
|
|
19
|
+
"@salesforce/webapp-experimental": "^1.116.13",
|
|
20
20
|
"sonner": "^1.7.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|