@salesforce/ui-bundle-template-app-react-sample-b2e 11.20.0 → 11.22.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.
Files changed (102) hide show
  1. package/dist/CHANGELOG.md +16 -0
  2. package/dist/force-app/main/default/uiBundles/propertymanagementapp/package.json +4 -4
  3. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/applications/applicationSearchService.ts +25 -43
  4. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/maintenanceRequests/maintenanceRequestSearchService.ts +31 -65
  5. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/maintenanceWorkers/maintenanceWorkerSearchService.ts +21 -43
  6. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/properties/propertySearchService.ts +33 -53
  7. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/components/applications/ApplicationCard.tsx +80 -0
  8. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/components/dashboard/GlobalSearchBar.tsx +2 -2
  9. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/components/layout/VerticalNav.tsx +28 -18
  10. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/components/maintenanceRequests/MaintenanceRequestCard.tsx +79 -52
  11. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/components/maintenanceWorkers/WorkerCard.tsx +87 -0
  12. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/components/search/UnifiedSearch.tsx +213 -0
  13. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/api/distinctValuesService.ts +84 -0
  14. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/api/searchService.ts +71 -0
  15. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/MergedSearchResults.tsx +263 -0
  16. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/Search.tsx +185 -0
  17. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/SearchResults.tsx +77 -0
  18. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/SourceSection.tsx +166 -0
  19. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/controls/PaginationControls.tsx +185 -0
  20. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/controls/ScopeSelector.tsx +55 -0
  21. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/controls/SearchBar.tsx +55 -0
  22. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/controls/SortControl.tsx +62 -0
  23. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/filters/ActiveFilters.tsx +61 -0
  24. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/filters/DefaultFilterPanel.tsx +125 -0
  25. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/{object-search/components → search/components/filters}/FilterContext.tsx +7 -20
  26. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/filters/inputs/BooleanFilter.tsx +50 -0
  27. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/filters/inputs/DateRangeFilter.tsx +160 -0
  28. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/filters/inputs/FilterFieldWrapper.tsx +26 -0
  29. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/filters/inputs/MultiSelectFilter.tsx +88 -0
  30. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/filters/inputs/NumericRangeFilter.tsx +78 -0
  31. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/filters/inputs/SelectFilter.tsx +46 -0
  32. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/filters/inputs/TextFilter.tsx +52 -0
  33. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/results/DefaultResultRow.tsx +109 -0
  34. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/config.json +83 -0
  35. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/{object-search → search}/hooks/useAsyncData.ts +3 -10
  36. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/hooks/useDistinctValues.ts +59 -0
  37. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/hooks/useSearch.ts +703 -0
  38. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/index.ts +90 -0
  39. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/loadConfig.ts +14 -0
  40. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/queryBuilder.ts +156 -0
  41. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/types.ts +307 -0
  42. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/utils/debounce.ts +17 -0
  43. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/utils/fieldUtils.ts +14 -0
  44. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/utils/filterUtils.ts +285 -0
  45. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/utils/sortUtils.ts +34 -0
  46. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/lib/constants.ts +16 -29
  47. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/lib/routeConfig.ts +1 -0
  48. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/lib/searchConfig.ts +190 -0
  49. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/pages/ApplicationSearch.tsx +8 -385
  50. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/pages/Home.tsx +4 -2
  51. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/pages/MaintenanceRequestSearch.tsx +8 -458
  52. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/pages/MaintenanceWorkerSearch.tsx +8 -341
  53. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/pages/PropertySearch.tsx +7 -243
  54. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/pages/Search.tsx +16 -0
  55. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/routes.tsx +6 -0
  56. package/dist/force-app/main/default/uiBundles/propertymanagementapp/tsconfig.tsbuildinfo +1 -1
  57. package/dist/package-lock.json +2 -2
  58. package/dist/package.json +1 -1
  59. package/package.json +2 -2
  60. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/applications/query/distinctApplicationStatus.graphql +0 -19
  61. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/applications/query/searchApplications.graphql +0 -65
  62. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/maintenanceRequests/query/distinctMaintenanceRequestPriority.graphql +0 -19
  63. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/maintenanceRequests/query/distinctMaintenanceRequestStatus.graphql +0 -19
  64. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/maintenanceRequests/query/distinctMaintenanceRequestType.graphql +0 -19
  65. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/maintenanceRequests/query/searchMaintenanceRequests.graphql +0 -82
  66. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/maintenanceWorkers/query/distinctMaintenanceWorkerEmploymentType.graphql +0 -19
  67. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/maintenanceWorkers/query/searchMaintenanceWorkers.graphql +0 -64
  68. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/properties/query/distinctPropertyStatus.graphql +0 -19
  69. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/properties/query/distinctPropertyType.graphql +0 -19
  70. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/api/properties/query/searchProperties.graphql +0 -105
  71. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/components/shared/ObjectSearchErrorState.tsx +0 -45
  72. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/components/shared/UserAvatar.tsx +0 -35
  73. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/__examples__/api/accountSearchService.ts +0 -46
  74. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/__examples__/api/query/distinctAccountIndustries.graphql +0 -19
  75. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/__examples__/api/query/distinctAccountTypes.graphql +0 -19
  76. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/__examples__/api/query/getAccountDetail.graphql +0 -121
  77. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/__examples__/api/query/searchAccounts.graphql +0 -51
  78. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/__examples__/pages/AccountObjectDetailPage.tsx +0 -355
  79. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/__examples__/pages/AccountSearch.tsx +0 -304
  80. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/__examples__/pages/Home.tsx +0 -34
  81. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/api/objectSearchService.ts +0 -82
  82. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/ActiveFilters.tsx +0 -89
  83. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/ObjectBreadcrumb.tsx +0 -66
  84. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/PaginationControls.tsx +0 -109
  85. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/SearchBar.tsx +0 -41
  86. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/SortControl.tsx +0 -143
  87. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/filters/BooleanFilter.tsx +0 -78
  88. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/filters/DateFilter.tsx +0 -128
  89. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/filters/DateRangeFilter.tsx +0 -70
  90. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/filters/FilterFieldWrapper.tsx +0 -43
  91. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/filters/MultiSelectFilter.tsx +0 -105
  92. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/filters/NumericRangeFilter.tsx +0 -171
  93. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/filters/SearchFilter.tsx +0 -52
  94. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/filters/SelectFilter.tsx +0 -97
  95. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/components/filters/TextFilter.tsx +0 -93
  96. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/hooks/useDebouncedCallback.ts +0 -34
  97. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/hooks/useObjectSearchParams.ts +0 -254
  98. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/utils/debounce.ts +0 -25
  99. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/utils/fieldUtils.ts +0 -29
  100. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/utils/filterUtils.ts +0 -404
  101. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/object-search/utils/sortUtils.ts +0 -38
  102. package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/lib/filterUtils.ts +0 -11
@@ -1,463 +1,13 @@
1
- import { useMemo, useState } from "react";
2
- import { useNavigate } from "react-router";
3
- import { toast } from "../components/ui/sonner";
4
- import {
5
- searchMaintenanceRequests,
6
- fetchDistinctMaintenanceRequestStatus,
7
- fetchDistinctMaintenanceRequestType,
8
- fetchDistinctMaintenanceRequestPriority,
9
- type MaintenanceRequestSearchNode,
10
- } from "../api/maintenanceRequests/maintenanceRequestSearchService";
11
- import { useAsyncData } from "../hooks/useAsyncData";
12
- import {
13
- useObjectSearchParams,
14
- type UseObjectSearchParamsReturn,
15
- } from "../features/object-search/hooks/useObjectSearchParams";
16
- import type { FilterFieldConfig } from "../features/object-search/utils/filterUtils";
17
- import type { SortFieldConfig } from "../features/object-search/utils/sortUtils";
18
- import { PageHeader } from "../components/layout/PageHeader";
19
- import { PageContainer } from "../components/layout/PageContainer";
20
- import {
21
- FilterProvider,
22
- FilterResetButton,
23
- } from "../features/object-search/components/FilterContext";
24
- import { FilterRow } from "../components/layout/FilterRow";
25
- import { SearchFilter } from "../features/object-search/components/filters/SearchFilter";
26
- import { MultiSelectFilter } from "../features/object-search/components/filters/MultiSelectFilter";
27
- import { DateFilter } from "../features/object-search/components/filters/DateFilter";
28
- import { ObjectSearchErrorState } from "../components/shared/ObjectSearchErrorState";
29
- import PaginationControls from "../features/object-search/components/PaginationControls";
30
- import { UserAvatar } from "../components/shared/UserAvatar";
31
- import { Skeleton } from "../components/ui/skeleton";
32
- import {
33
- Table,
34
- TableHeader,
35
- TableBody,
36
- TableRow,
37
- TableHead,
38
- TableCell,
39
- } from "../components/ui/table";
40
- import { Badge } from "../components/ui/badge";
41
- import { Check } from "lucide-react";
42
- import { MaintenanceDetailsModal } from "../components/maintenanceRequests/MaintenanceDetailsModal";
43
- import { updateMaintenanceStatus } from "../api/maintenanceRequests/maintenanceRequests";
44
- import PlumbingIcon from "../assets/icons/plumbing.svg";
45
- import HVACIcon from "../assets/icons/hvac.svg";
46
- import ElectricalIcon from "../assets/icons/electrical.svg";
47
- import AppliancesIcon from "../assets/icons/appliances.svg";
48
- import PestIcon from "../assets/icons/pest.svg";
49
- import { PAGINATION_CONFIG } from "../lib/constants";
50
- import type {
51
- Maintenance_Request__C_Filter,
52
- Maintenance_Request__C_OrderBy,
53
- } from "../api/graphql-operations-types";
54
- import { ResultOrder } from "../api/graphql-operations-types";
55
- import { cn } from "../lib/utils";
56
-
57
- const issueIcons: Record<string, string> = {
58
- Plumbing: PlumbingIcon,
59
- HVAC: HVACIcon,
60
- Electrical: ElectricalIcon,
61
- Appliance: AppliancesIcon,
62
- Pest: PestIcon,
63
- };
64
-
65
- const FILTER_CONFIGS: FilterFieldConfig[] = [
66
- {
67
- field: "search",
68
- label: "Search",
69
- type: "search",
70
- searchFields: ["Name", "User__r.Name", "Property__r.Address__c", "Assigned_Worker__r.Name"],
71
- placeholder: "Search by request #, tenant, address, or worker...",
72
- },
73
- { field: "Status__c", label: "Status", type: "picklist" },
74
- { field: "Type__c", label: "Type", type: "picklist" },
75
- { field: "Priority__c", label: "Priority", type: "picklist" },
76
- { field: "Scheduled__c", label: "Scheduled", type: "datetime" },
77
- ];
78
-
79
- const SORT_CONFIGS: SortFieldConfig<string>[] = [
80
- { field: "Name", label: "Subject" },
81
- { field: "Status__c", label: "Status" },
82
- { field: "Priority__c", label: "Priority" },
83
- { field: "Scheduled__c", label: "Scheduled" },
84
- { field: "CreatedDate", label: "Created Date" },
85
- ];
1
+ import { UnifiedSearch } from "../components/search/UnifiedSearch";
2
+ import { SOURCE_KEYS } from "../lib/searchConfig";
86
3
 
87
4
  export default function MaintenanceRequestSearch() {
88
- const [selectedRequest, setSelectedRequest] = useState<MaintenanceRequestSearchNode | null>(null);
89
-
90
- const { data: statusOptions } = useAsyncData(fetchDistinctMaintenanceRequestStatus, []);
91
- const { data: typeOptions } = useAsyncData(fetchDistinctMaintenanceRequestType, []);
92
- const { data: priorityOptions } = useAsyncData(fetchDistinctMaintenanceRequestPriority, []);
93
-
94
- const { filters, query, pagination, resetAll } = useObjectSearchParams<
95
- Maintenance_Request__C_Filter,
96
- Maintenance_Request__C_OrderBy
97
- >(FILTER_CONFIGS, SORT_CONFIGS, PAGINATION_CONFIG);
98
- const effectiveOrderBy = useMemo<Maintenance_Request__C_OrderBy>(
99
- () => query.orderBy ?? { CreatedDate: { order: ResultOrder.Desc } },
100
- [query.orderBy],
101
- );
102
- const { data, loading, error } = useAsyncData(
103
- () =>
104
- searchMaintenanceRequests({
105
- where: query.where,
106
- orderBy: effectiveOrderBy,
107
- first: pagination.pageSize,
108
- after: pagination.afterCursor,
109
- }),
110
- [query.where, effectiveOrderBy, pagination.pageSize, pagination.afterCursor],
111
- );
112
-
113
- const validMaintenanceRequestNodes = useMemo(
114
- () =>
115
- (data?.edges ?? []).reduce<MaintenanceRequestSearchNode[]>((acc, edge) => {
116
- if (edge?.node) acc.push(edge.node);
117
- return acc;
118
- }, []),
119
- [data?.edges],
120
- );
121
-
122
- const pageInfo = data?.pageInfo;
123
- const hasNextPage = pageInfo?.hasNextPage ?? false;
124
- const hasPreviousPage = pagination.pageIndex > 0;
125
-
126
- const handleSaveStatus = async (requestId: string, status: string) => {
127
- try {
128
- const success = await updateMaintenanceStatus(requestId, status);
129
- if (success) {
130
- if (selectedRequest?.Id === requestId) {
131
- setSelectedRequest({ ...selectedRequest, Status__c: { value: status } });
132
- }
133
- toast.success("Status updated", {
134
- description: "Maintenance request status has been updated successfully.",
135
- });
136
- } else {
137
- toast.error("Update failed", {
138
- description: "Failed to update maintenance request status. Please try again.",
139
- });
140
- }
141
- } catch (err) {
142
- console.error(err);
143
- toast.error("Error updating status", {
144
- description: "An error occurred while updating the status. Please try again.",
145
- });
146
- }
147
- };
148
-
149
- return (
150
- <>
151
- <PageContainer>
152
- <PageHeader
153
- title="Maintenance Requests"
154
- description="Track and manage maintenance requests"
155
- />
156
- <MaintenanceRequestSearchFilters
157
- filters={filters}
158
- statusOptions={statusOptions ?? []}
159
- typeOptions={typeOptions ?? []}
160
- priorityOptions={priorityOptions ?? []}
161
- resetAll={resetAll}
162
- />
163
-
164
- <div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
165
- <Table aria-label="Maintenance requests">
166
- <MaintenanceRequestSearchTableHeader />
167
- <TableBody>
168
- {loading && <MaintenanceRequestSearchSkeleton pageSize={pagination.pageSize} />}
169
-
170
- {!loading && error && <MaintenanceRequestSearchErrorState />}
171
-
172
- {!loading && !error && validMaintenanceRequestNodes.length === 0 && (
173
- <MaintenanceRequestSearchNoResults />
174
- )}
175
-
176
- {!loading && !error && validMaintenanceRequestNodes.length > 0 && (
177
- <MaintenanceRequestSearchTable
178
- requestNodes={validMaintenanceRequestNodes}
179
- setSelectedRequest={setSelectedRequest}
180
- />
181
- )}
182
- </TableBody>
183
- </Table>
184
- </div>
185
-
186
- <PaginationControls
187
- pageIndex={pagination.pageIndex}
188
- hasNextPage={hasNextPage}
189
- hasPreviousPage={hasPreviousPage}
190
- pageSize={pagination.pageSize}
191
- pageSizeOptions={PAGINATION_CONFIG.validPageSizes}
192
- onNextPage={() => {
193
- if (pageInfo?.endCursor) pagination.goToNextPage(pageInfo.endCursor);
194
- }}
195
- onPreviousPage={pagination.goToPreviousPage}
196
- onPageSizeChange={pagination.setPageSize}
197
- disabled={loading || !!error}
198
- />
199
- </PageContainer>
200
-
201
- {!loading && !error && selectedRequest && (
202
- <MaintenanceDetailsModal
203
- request={selectedRequest}
204
- isOpen={!!selectedRequest}
205
- onClose={() => setSelectedRequest(null)}
206
- onSave={handleSaveStatus}
207
- />
208
- )}
209
- </>
210
- );
211
- }
212
-
213
- function MaintenanceRequestSearchFilters({
214
- filters,
215
- statusOptions,
216
- typeOptions,
217
- priorityOptions,
218
- resetAll,
219
- }: {
220
- filters: UseObjectSearchParamsReturn<
221
- Maintenance_Request__C_Filter,
222
- Maintenance_Request__C_OrderBy
223
- >["filters"];
224
- statusOptions: Array<{ value: string; label: string }>;
225
- typeOptions: Array<{ value: string; label: string }>;
226
- priorityOptions: Array<{ value: string; label: string }>;
227
- resetAll: () => void;
228
- }) {
229
- return (
230
- <FilterProvider
231
- filters={filters.active}
232
- onFilterChange={filters.set}
233
- onFilterRemove={filters.remove}
234
- onReset={resetAll}
235
- >
236
- <FilterRow ariaLabel="Maintenance Requests filters">
237
- <SearchFilter
238
- field="search"
239
- label="Search"
240
- placeholder="Search by request #, tenant, address, or worker..."
241
- className="w-full sm:w-80"
242
- />
243
- <MultiSelectFilter
244
- field="Status__c"
245
- label="Status"
246
- options={statusOptions}
247
- className="w-full sm:w-36"
248
- />
249
- <MultiSelectFilter
250
- field="Type__c"
251
- label="Type"
252
- options={typeOptions}
253
- className="w-full sm:w-36"
254
- />
255
- <MultiSelectFilter
256
- field="Priority__c"
257
- label="Priority"
258
- options={priorityOptions}
259
- className="w-full sm:w-36"
260
- />
261
- <DateFilter
262
- field="Scheduled__c"
263
- label="Scheduled"
264
- filterType="datetime"
265
- className="w-full sm:w-56"
266
- />
267
- <FilterResetButton />
268
- </FilterRow>
269
- </FilterProvider>
270
- );
271
- }
272
-
273
- function MaintenanceRequestSearchTableHeader() {
274
- return (
275
- <TableHeader>
276
- <TableRow>
277
- <TableHead className="w-4/12 max-w-xs px-6 py-4 bg-gray-50 text-sm font-semibold text-purple-700 uppercase tracking-wide">
278
- Maintenance Task
279
- </TableHead>
280
- <TableHead className="w-3/12 px-6 py-4 bg-gray-50 text-sm font-semibold text-purple-700 uppercase tracking-wide">
281
- Tenant Unit
282
- </TableHead>
283
- <TableHead className="w-3/12 px-6 py-4 bg-gray-50 text-sm font-semibold text-purple-700 uppercase tracking-wide">
284
- Assigned Worker
285
- </TableHead>
286
- <TableHead className="w-2/12 px-6 py-4 bg-gray-50 text-sm font-semibold text-purple-700 uppercase tracking-wide">
287
- Status
288
- </TableHead>
289
- </TableRow>
290
- </TableHeader>
291
- );
292
- }
293
-
294
- function MaintenanceRequestSearchNoResults() {
295
- return (
296
- <TableRow>
297
- <TableCell colSpan={4} className="text-center py-12 text-gray-500">
298
- No maintenance requests found
299
- </TableCell>
300
- </TableRow>
301
- );
302
- }
303
-
304
- function MaintenanceRequestSearchTable({
305
- requestNodes,
306
- setSelectedRequest,
307
- }: {
308
- requestNodes: MaintenanceRequestSearchNode[];
309
- setSelectedRequest: (request: MaintenanceRequestSearchNode) => void;
310
- }) {
311
- return (
312
- <>
313
- {requestNodes.map((request) => {
314
- const requestName = request.Name?.value ?? "";
315
- const issueType = request.Type__c?.value ?? "";
316
- const description = request.Description__c?.value ?? "";
317
- const tenantName = request.User__r?.Name?.value || "Unknown";
318
- const tenantUnit =
319
- request.Property__r?.Name?.value || request.Property__r?.Address__c?.value || "";
320
- const assignedWorkerName = request.Assigned_Worker__r?.Name?.value || "Unassigned";
321
- const status = request.Status__c?.value || "New";
322
- const imageVersionId =
323
- request.ContentDocumentLinks?.edges?.[0]?.node?.ContentDocument?.LatestPublishedVersionId
324
- ?.value;
325
- const imageUrl = imageVersionId
326
- ? `/sfc/servlet.shepherd/version/download/${imageVersionId}`
327
- : undefined;
328
- return (
329
- <TableRow
330
- key={request.Id}
331
- role="button"
332
- tabIndex={0}
333
- aria-label={requestName}
334
- onClick={() => setSelectedRequest(request)}
335
- onKeyDown={(e) => {
336
- if (e.key === "Enter" || e.key === " ") {
337
- e.preventDefault();
338
- setSelectedRequest(request);
339
- }
340
- }}
341
- className="hover:bg-gray-50 transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-purple focus-visible:ring-inset"
342
- >
343
- <TableCell className="max-w-xs px-6 py-5">
344
- <div className="flex items-center gap-4">
345
- <div className="w-16 h-16 rounded-lg flex-shrink-0 overflow-hidden flex items-center justify-center bg-purple-100">
346
- {imageUrl ? (
347
- <img src={imageUrl} alt={description} className="w-full h-full object-cover" />
348
- ) : issueIcons[issueType] ? (
349
- <img src={issueIcons[issueType]} alt={issueType} className="w-8 h-8" />
350
- ) : (
351
- <span className="text-2xl">🔧</span>
352
- )}
353
- </div>
354
- <div className="flex-1 min-w-0">
355
- <p className="font-semibold text-gray-900 truncate mb-1">{description}</p>
356
- <p className="text-sm text-gray-500">{requestName}</p>
357
- </div>
358
- </div>
359
- </TableCell>
360
- <TableCell className="px-6 py-5">
361
- <div className="flex items-center gap-3 min-w-0">
362
- <UserAvatar name={tenantName} size="md" />
363
- <div className="min-w-0 flex-1">
364
- <p className="font-medium text-gray-900 truncate">{tenantUnit}</p>
365
- <p className="text-gray-500 truncate">{tenantName}</p>
366
- </div>
367
- </div>
368
- </TableCell>
369
- <TableCell className="px-6 py-5">
370
- <p className="text-gray-900 truncate">{assignedWorkerName}</p>
371
- </TableCell>
372
- <TableCell className="px-6 py-5">
373
- <StatusBadge status={status} />
374
- </TableCell>
375
- </TableRow>
376
- );
377
- })}
378
- </>
379
- );
380
- }
381
-
382
- function StatusBadge({ status }: { status: string }) {
383
- const statusLower = status.toLowerCase();
384
-
385
- const style =
386
- statusLower === "new"
387
- ? "bg-pink-100 text-pink-700"
388
- : statusLower === "in progress"
389
- ? "bg-yellow-100 text-yellow-800"
390
- : statusLower === "resolved"
391
- ? "bg-green-100 text-green-700"
392
- : "bg-gray-100 text-gray-700";
393
-
394
- const label =
395
- statusLower === "new"
396
- ? "Needs Action"
397
- : statusLower === "in progress"
398
- ? "In Progress"
399
- : statusLower === "resolved"
400
- ? "Resolved"
401
- : status;
402
-
403
- return (
404
- <Badge className={cn("text-sm", style)}>
405
- {statusLower === "resolved" && <Check className="w-4 h-4" />}
406
- {(statusLower === "new" || statusLower === "in progress") && (
407
- <span className="w-2 h-2 rounded-full bg-current" />
408
- )}
409
- {label}
410
- </Badge>
411
- );
412
- }
413
-
414
- function MaintenanceRequestSearchSkeleton({ pageSize }: { pageSize: number }) {
415
- return (
416
- <>
417
- {Array.from({ length: pageSize }, (_, i) => (
418
- <TableRow key={i}>
419
- <TableCell className="px-6 py-5">
420
- <div className="flex items-center gap-4">
421
- <Skeleton className="h-16 w-16 rounded-lg shrink-0" />
422
- <div className="space-y-2 flex-1 min-w-0">
423
- <Skeleton className="h-4 w-full" />
424
- <Skeleton className="h-3 w-20" />
425
- </div>
426
- </div>
427
- </TableCell>
428
- <TableCell className="px-6 py-5">
429
- <div className="flex items-center gap-3">
430
- <Skeleton className="h-10 w-10 rounded-full shrink-0" />
431
- <div className="space-y-2 min-w-0">
432
- <Skeleton className="h-4 w-24" />
433
- <Skeleton className="h-3 w-32" />
434
- </div>
435
- </div>
436
- </TableCell>
437
- <TableCell className="px-6 py-5">
438
- <Skeleton className="h-4 w-28" />
439
- </TableCell>
440
- <TableCell className="px-6 py-5">
441
- <Skeleton className="h-6 w-20 rounded-full" />
442
- </TableCell>
443
- </TableRow>
444
- ))}
445
- </>
446
- );
447
- }
448
-
449
- function MaintenanceRequestSearchErrorState() {
450
- const navigate = useNavigate();
451
-
452
5
  return (
453
- <TableRow>
454
- <TableCell colSpan={4} className="p-0">
455
- <ObjectSearchErrorState
456
- message="There was an error loading the maintenance requests. Please try again."
457
- onGoHome={() => navigate("/")}
458
- onRetry={() => navigate(0)}
459
- />
460
- </TableCell>
461
- </TableRow>
6
+ <UnifiedSearch
7
+ title="Maintenance Requests"
8
+ subtitle="Track and update maintenance requests"
9
+ searchPlaceholder="Search by subject, tenant, address, or worker..."
10
+ restrictTo={SOURCE_KEYS.MAINTENANCE}
11
+ />
462
12
  );
463
13
  }