@salesforce/ui-bundle-template-app-react-sample-b2e 11.19.0 → 11.21.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,390 +1,13 @@
1
- import { useMemo, useState } from "react";
2
- import { useNavigate } from "react-router";
3
- import { toast } from "../components/ui/sonner";
4
- import {
5
- searchApplications,
6
- fetchDistinctApplicationStatus,
7
- } from "../api/applications/applicationSearchService";
8
- import { useAsyncData } from "../hooks/useAsyncData";
9
- import {
10
- useObjectSearchParams,
11
- type UseObjectSearchParamsReturn,
12
- } from "../features/object-search/hooks/useObjectSearchParams";
13
- import type { FilterFieldConfig } from "../features/object-search/utils/filterUtils";
14
- import type { SortFieldConfig } from "../features/object-search/utils/sortUtils";
15
- import type { ApplicationSearchNode } from "../api/applications/applicationSearchService";
16
- import { PageHeader } from "../components/layout/PageHeader";
17
- import { PageContainer } from "../components/layout/PageContainer";
18
- import {
19
- FilterProvider,
20
- FilterResetButton,
21
- } from "../features/object-search/components/FilterContext";
22
- import { FilterRow } from "../components/layout/FilterRow";
23
- import { SearchFilter } from "../features/object-search/components/filters/SearchFilter";
24
- import { MultiSelectFilter } from "../features/object-search/components/filters/MultiSelectFilter";
25
- import { DateFilter } from "../features/object-search/components/filters/DateFilter";
26
- import { ObjectSearchErrorState } from "../components/shared/ObjectSearchErrorState";
27
- import PaginationControls from "../features/object-search/components/PaginationControls";
28
- import { ApplicationDetailsModal } from "../components/applications/ApplicationDetailsModal";
29
- import { Skeleton } from "../components/ui/skeleton";
30
- import {
31
- Table,
32
- TableHeader,
33
- TableBody,
34
- TableRow,
35
- TableHead,
36
- TableCell,
37
- } from "../components/ui/table";
38
- import { updateApplicationStatus } from "../api/applications/applications";
39
- import type {
40
- Application__C_Filter,
41
- Application__C_OrderBy,
42
- } from "../api/graphql-operations-types";
43
- import { ResultOrder } from "../api/graphql-operations-types";
44
- import { Badge } from "../components/ui/badge";
45
- import { PAGINATION_CONFIG } from "../lib/constants";
46
- import { User } from "lucide-react";
47
- import { cn } from "../lib/utils";
48
-
49
- const SEARCH_PLACEHOLDER = "Search by application #, applicant, or address...";
50
-
51
- const FILTER_CONFIGS: FilterFieldConfig[] = [
52
- {
53
- field: "search",
54
- label: "Search",
55
- type: "search",
56
- searchFields: ["Name", "User__r.Name", "Property__r.Address__c"],
57
- placeholder: SEARCH_PLACEHOLDER,
58
- },
59
- { field: "Status__c", label: "Status", type: "picklist" },
60
- { field: "Start_Date__c", label: "Start Date", type: "date" },
61
- { field: "CreatedDate", label: "Created Date", type: "datetime" },
62
- ];
63
-
64
- const SORT_CONFIGS: SortFieldConfig<string>[] = [
65
- { field: "Name", label: "Application" },
66
- { field: "Status__c", label: "Status" },
67
- { field: "Start_Date__c", label: "Start Date" },
68
- { field: "CreatedDate", label: "Created Date" },
69
- ];
1
+ import { UnifiedSearch } from "../components/search/UnifiedSearch";
2
+ import { SOURCE_KEYS } from "../lib/searchConfig";
70
3
 
71
4
  export default function ApplicationSearch() {
72
- const [selectedApplication, setSelectedApplication] = useState<ApplicationSearchNode | null>(
73
- null,
74
- );
75
- const [refreshKey, setRefreshKey] = useState(0);
76
-
77
- const { data: statusOptions } = useAsyncData(fetchDistinctApplicationStatus, []);
78
-
79
- const { filters, query, pagination, resetAll } = useObjectSearchParams<
80
- Application__C_Filter,
81
- Application__C_OrderBy
82
- >(FILTER_CONFIGS, SORT_CONFIGS, PAGINATION_CONFIG);
83
- const effectiveOrderBy = useMemo<Application__C_OrderBy>(
84
- () => query.orderBy ?? { CreatedDate: { order: ResultOrder.Desc } },
85
- [query.orderBy],
86
- );
87
- const { data, loading, error } = useAsyncData(
88
- () =>
89
- searchApplications({
90
- where: query.where,
91
- orderBy: effectiveOrderBy,
92
- first: pagination.pageSize,
93
- after: pagination.afterCursor,
94
- }),
95
- [query.where, effectiveOrderBy, pagination.pageSize, pagination.afterCursor, refreshKey],
96
- );
97
-
98
- const validApplicationNodes = useMemo(
99
- () =>
100
- (data?.edges ?? []).reduce<ApplicationSearchNode[]>((acc, edge) => {
101
- if (edge?.node) acc.push(edge.node);
102
- return acc;
103
- }, []),
104
- [data?.edges],
105
- );
106
-
107
- const pageInfo = data?.pageInfo;
108
- const hasNextPage = pageInfo?.hasNextPage ?? false;
109
- const hasPreviousPage = pagination.pageIndex > 0;
110
-
111
- const handleSaveStatus = async (applicationId: string, status: string) => {
112
- try {
113
- const success = await updateApplicationStatus(applicationId, status);
114
- if (success) {
115
- if (selectedApplication?.Id === applicationId) {
116
- setSelectedApplication({ ...selectedApplication, Status__c: { value: status } });
117
- }
118
- setRefreshKey((k) => k + 1);
119
- toast.success("Status updated", {
120
- description: "Application status has been updated successfully.",
121
- });
122
- } else {
123
- toast.error("Update failed", {
124
- description: "Failed to update application status. Please try again.",
125
- });
126
- }
127
- } catch (err) {
128
- console.error(err);
129
- toast.error("Error updating status", {
130
- description: "An error occurred while updating the status. Please try again.",
131
- });
132
- }
133
- };
134
-
135
- return (
136
- <>
137
- <PageContainer>
138
- <PageHeader title="Applications" description="Manage and review rental applications" />
139
- <ApplicationSearchFilters
140
- filters={filters}
141
- statusOptions={statusOptions ?? []}
142
- resetAll={resetAll}
143
- />
144
- <div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
145
- <Table aria-label="Applications">
146
- <ApplicationSearchTableHeader />
147
- <TableBody>
148
- {loading && <ApplicationSearchSkeleton pageSize={pagination.pageSize} />}
149
-
150
- {!loading && error && <ApplicationSearchErrorState />}
151
-
152
- {!loading && !error && validApplicationNodes.length === 0 && (
153
- <ApplicationSearchNoResults />
154
- )}
155
-
156
- {!loading && !error && validApplicationNodes.length > 0 && (
157
- <ApplicationSearchTable
158
- applicationNodes={validApplicationNodes}
159
- setSelectedApplication={setSelectedApplication}
160
- />
161
- )}
162
- </TableBody>
163
- </Table>
164
- </div>
165
-
166
- <PaginationControls
167
- pageIndex={pagination.pageIndex}
168
- hasNextPage={hasNextPage}
169
- hasPreviousPage={hasPreviousPage}
170
- pageSize={pagination.pageSize}
171
- pageSizeOptions={PAGINATION_CONFIG.validPageSizes}
172
- onNextPage={() => {
173
- if (pageInfo?.endCursor) pagination.goToNextPage(pageInfo.endCursor);
174
- }}
175
- onPreviousPage={pagination.goToPreviousPage}
176
- onPageSizeChange={pagination.setPageSize}
177
- disabled={loading || !!error}
178
- />
179
- </PageContainer>
180
-
181
- {!loading && !error && selectedApplication && (
182
- <ApplicationDetailsModal
183
- application={selectedApplication}
184
- isOpen={!!selectedApplication}
185
- onClose={() => setSelectedApplication(null)}
186
- onSave={handleSaveStatus}
187
- />
188
- )}
189
- </>
190
- );
191
- }
192
-
193
- function ApplicationSearchFilters({
194
- filters,
195
- statusOptions,
196
- resetAll,
197
- }: {
198
- filters: UseObjectSearchParamsReturn<Application__C_Filter, Application__C_OrderBy>["filters"];
199
- statusOptions: Array<{ value: string; label: string }>;
200
- resetAll: () => void;
201
- }) {
202
- return (
203
- <FilterProvider
204
- filters={filters.active}
205
- onFilterChange={filters.set}
206
- onFilterRemove={filters.remove}
207
- onReset={resetAll}
208
- >
209
- <FilterRow ariaLabel="Applications filters">
210
- <SearchFilter
211
- field="search"
212
- label="Search"
213
- placeholder={SEARCH_PLACEHOLDER}
214
- className="w-full sm:w-80"
215
- />
216
- <MultiSelectFilter
217
- field="Status__c"
218
- label="Status"
219
- options={statusOptions ?? []}
220
- className="w-full sm:w-36"
221
- />
222
- <DateFilter field="Start_Date__c" label="Start Date" className="w-full sm:w-56" />
223
- <DateFilter
224
- field="CreatedDate"
225
- label="Created Date"
226
- filterType="datetime"
227
- className="w-full sm:w-56"
228
- />
229
- <FilterResetButton />
230
- </FilterRow>
231
- </FilterProvider>
232
- );
233
- }
234
-
235
- function ApplicationSearchTableHeader() {
236
- return (
237
- <TableHeader>
238
- <TableRow>
239
- <TableHead className="w-1/12 px-6 py-4 bg-gray-50 text-sm font-semibold text-purple-700 uppercase tracking-wide">
240
- Application
241
- </TableHead>
242
- <TableHead className="w-5/12 px-6 py-4 bg-gray-50 text-sm font-semibold text-purple-700 uppercase tracking-wide">
243
- Applicant
244
- </TableHead>
245
- <TableHead className="w-3/12 px-6 py-4 bg-gray-50 text-sm font-semibold text-purple-700 uppercase tracking-wide">
246
- Start Date
247
- </TableHead>
248
- <TableHead className="w-3/12 px-6 py-4 bg-gray-50 text-sm font-semibold text-purple-700 uppercase tracking-wide">
249
- Status
250
- </TableHead>
251
- </TableRow>
252
- </TableHeader>
253
- );
254
- }
255
-
256
- function ApplicationSearchNoResults() {
257
- return (
258
- <TableRow>
259
- <TableCell colSpan={4} className="text-center py-12 text-gray-500">
260
- No applications found
261
- </TableCell>
262
- </TableRow>
263
- );
264
- }
265
-
266
- function ApplicationSearchTable({
267
- applicationNodes,
268
- setSelectedApplication,
269
- }: {
270
- applicationNodes: ApplicationSearchNode[];
271
- setSelectedApplication: (application: ApplicationSearchNode) => void;
272
- }) {
273
- return (
274
- <>
275
- {applicationNodes.map((application) => {
276
- const applicationName = application.Name?.value || "";
277
- const applicantName = application.User__r?.Name?.value || "Unknown";
278
- const propertyName = application.Property__r?.Name?.value;
279
- const propertyAddress = application.Property__r?.Address__c?.value || "";
280
- const startDate = application.Start_Date__c?.value || application.CreatedDate?.value || "";
281
- const status = application.Status__c?.value || "Unknown";
282
- return (
283
- <TableRow
284
- key={application.Id}
285
- role="button"
286
- tabIndex={0}
287
- aria-label={applicationName}
288
- onClick={() => setSelectedApplication(application)}
289
- onKeyDown={(e) => {
290
- if (e.key === "Enter" || e.key === " ") {
291
- e.preventDefault();
292
- setSelectedApplication(application);
293
- }
294
- }}
295
- 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"
296
- >
297
- <TableCell className="px-6 py-4">
298
- <div className="text-sm font-medium text-gray-900">{applicationName}</div>
299
- </TableCell>
300
- <TableCell className="px-6 py-4">
301
- <div className="flex items-center">
302
- <div className="w-10 h-10 bg-purple-100 rounded-full flex items-center justify-center flex-shrink-0">
303
- <User className="w-5 h-5 text-purple-700" />
304
- </div>
305
- <div className="ml-4">
306
- <div className="text-sm font-medium text-gray-900">{applicantName}</div>
307
- <div className="text-sm text-gray-500">{propertyName || propertyAddress}</div>
308
- </div>
309
- </div>
310
- </TableCell>
311
- <TableCell className="px-6 py-4">
312
- <div className="text-sm text-gray-900">{formatDate(startDate)}</div>
313
- </TableCell>
314
- <TableCell className="px-6 py-4">
315
- <Badge className={cn("text-sm", getStatusColor(status))}>{status}</Badge>
316
- </TableCell>
317
- </TableRow>
318
- );
319
- })}
320
- </>
321
- );
322
- }
323
-
324
- const getStatusColor = (status: string) => {
325
- const statusLower = status.toLowerCase();
326
- if (statusLower.includes("approved")) return "bg-green-100 text-green-700";
327
- if (statusLower.includes("rejected")) return "bg-red-100 text-red-700";
328
- if (statusLower.includes("background")) return "bg-blue-100 text-blue-700";
329
- if (statusLower.includes("review")) return "bg-yellow-100 text-yellow-800";
330
- return "bg-gray-100 text-gray-700";
331
- };
332
-
333
- const formatDate = (dateString: string) => {
334
- if (!dateString) return "N/A";
335
- try {
336
- const date = new Date(dateString);
337
- return date.toLocaleDateString("en-US", {
338
- year: "numeric",
339
- month: "short",
340
- day: "numeric",
341
- });
342
- } catch {
343
- return dateString;
344
- }
345
- };
346
-
347
- function ApplicationSearchSkeleton({ pageSize }: { pageSize: number }) {
348
- return (
349
- <>
350
- {Array.from({ length: pageSize }, (_, i) => (
351
- <TableRow key={i}>
352
- <TableCell className="px-6 py-4">
353
- <Skeleton className="h-4 w-24" />
354
- </TableCell>
355
- <TableCell className="px-6 py-4">
356
- <div className="flex items-center">
357
- <Skeleton className="h-10 w-10 rounded-full shrink-0" />
358
- <div className="ml-4 space-y-2">
359
- <Skeleton className="h-4 w-32" />
360
- <Skeleton className="h-3 w-48" />
361
- </div>
362
- </div>
363
- </TableCell>
364
- <TableCell className="px-6 py-4">
365
- <Skeleton className="h-4 w-24" />
366
- </TableCell>
367
- <TableCell className="px-6 py-4">
368
- <Skeleton className="h-6 w-20 rounded-full" />
369
- </TableCell>
370
- </TableRow>
371
- ))}
372
- </>
373
- );
374
- }
375
-
376
- function ApplicationSearchErrorState() {
377
- const navigate = useNavigate();
378
-
379
5
  return (
380
- <TableRow>
381
- <TableCell colSpan={4} className="p-0">
382
- <ObjectSearchErrorState
383
- message="There was an error loading the applications. Please try again."
384
- onGoHome={() => navigate("/")}
385
- onRetry={() => navigate(0)}
386
- />
387
- </TableCell>
388
- </TableRow>
6
+ <UnifiedSearch
7
+ title="Applications"
8
+ subtitle="Manage and review rental applications"
9
+ searchPlaceholder="Search by application #, applicant, or address..."
10
+ restrictTo={SOURCE_KEYS.APPLICATIONS}
11
+ />
389
12
  );
390
13
  }
@@ -12,7 +12,7 @@ import {
12
12
  type DashboardMaintenanceNode,
13
13
  } from "../api/dashboard/dashboard";
14
14
  import {
15
- PROPERTY_OBJECT_API_NAME,
15
+ ALL_SEARCH_OBJECT_VALUE,
16
16
  SEARCHABLE_OBJECTS,
17
17
  type SearchableObjectConfig,
18
18
  } from "../lib/constants";
@@ -25,8 +25,10 @@ export default function Home() {
25
25
  const navigate = useNavigate();
26
26
 
27
27
  const [searchQuery, setSearchQuery] = useState("");
28
+ // Default the dashboard search to "All", which routes to the unified
29
+ // `/search` page spanning every object.
28
30
  const [selectedObjectApiName, setSelectedObjectApiName] =
29
- useState<SearchableObjectConfig["objectApiName"]>(PROPERTY_OBJECT_API_NAME);
31
+ useState<SearchableObjectConfig["objectApiName"]>(ALL_SEARCH_OBJECT_VALUE);
30
32
 
31
33
  const selectedConfig = useMemo(
32
34
  () => SEARCHABLE_OBJECTS.find((o) => o.objectApiName === selectedObjectApiName),