@snapdragonsnursery/react-components 1.17.3 → 1.17.4
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/package.json
CHANGED
|
@@ -61,9 +61,28 @@ export const EmployeeSelect = ({
|
|
|
61
61
|
allowAll = false,
|
|
62
62
|
allLabel = "All employees",
|
|
63
63
|
maxHeight = "160px",
|
|
64
|
+
// New props for server-side search
|
|
65
|
+
enableServerSearch = false,
|
|
66
|
+
onSearchChange = null,
|
|
67
|
+
searchResults = [],
|
|
68
|
+
isSearching = false,
|
|
64
69
|
}) => {
|
|
65
70
|
const [searchTerm, setSearchTerm] = React.useState("");
|
|
66
71
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
72
|
+
const [debouncedSearchTerm, setDebouncedSearchTerm] = React.useState("");
|
|
73
|
+
|
|
74
|
+
// Debounce search term for server-side search
|
|
75
|
+
React.useEffect(() => {
|
|
76
|
+
const timer = setTimeout(() => {
|
|
77
|
+
setDebouncedSearchTerm(searchTerm);
|
|
78
|
+
if (enableServerSearch && onSearchChange) {
|
|
79
|
+
onSearchChange(searchTerm);
|
|
80
|
+
}
|
|
81
|
+
}, 300); // 300ms debounce
|
|
82
|
+
|
|
83
|
+
return () => clearTimeout(timer);
|
|
84
|
+
}, [searchTerm, enableServerSearch, onSearchChange]);
|
|
85
|
+
|
|
67
86
|
// Helper function to generate initials from full name
|
|
68
87
|
const getInitials = (name) => {
|
|
69
88
|
if (!name) return "??";
|
|
@@ -88,7 +107,9 @@ export const EmployeeSelect = ({
|
|
|
88
107
|
};
|
|
89
108
|
|
|
90
109
|
const processedItems = React.useMemo(() => {
|
|
91
|
-
|
|
110
|
+
// Use server search results if enabled, otherwise use items
|
|
111
|
+
const sourceList = enableServerSearch ? searchResults : items;
|
|
112
|
+
const list = Array.isArray(sourceList) ? [...sourceList] : [];
|
|
92
113
|
|
|
93
114
|
// Filter by employee status
|
|
94
115
|
const filtered = list.filter((e) => {
|
|
@@ -110,18 +131,20 @@ export const EmployeeSelect = ({
|
|
|
110
131
|
}))
|
|
111
132
|
.filter((e) => e.entraId); // Remove items without entra_id
|
|
112
133
|
|
|
113
|
-
// Apply search filter
|
|
114
|
-
const searchFiltered =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
// Apply client-side search filter only if not using server search
|
|
135
|
+
const searchFiltered = enableServerSearch
|
|
136
|
+
? mapped // Server search already filtered
|
|
137
|
+
: mapped.filter((e) => {
|
|
138
|
+
if (!searchTerm) return true;
|
|
139
|
+
const searchLower = searchTerm.toLowerCase();
|
|
140
|
+
return (
|
|
141
|
+
e.fullName.toLowerCase().includes(searchLower) ||
|
|
142
|
+
e.siteName.toLowerCase().includes(searchLower) ||
|
|
143
|
+
e.roleName.toLowerCase().includes(searchLower) ||
|
|
144
|
+
e.employeeId.toLowerCase().includes(searchLower) ||
|
|
145
|
+
e.email.toLowerCase().includes(searchLower)
|
|
146
|
+
);
|
|
147
|
+
});
|
|
125
148
|
|
|
126
149
|
// Sort based on grouping
|
|
127
150
|
searchFiltered.sort((a, b) => {
|
|
@@ -139,7 +162,7 @@ export const EmployeeSelect = ({
|
|
|
139
162
|
});
|
|
140
163
|
|
|
141
164
|
return searchFiltered;
|
|
142
|
-
}, [items, filter, groupBy, searchTerm]);
|
|
165
|
+
}, [items, filter, groupBy, searchTerm, enableServerSearch, searchResults]);
|
|
143
166
|
|
|
144
167
|
const selectedEmployee = processedItems.find((e) => e.entraId === value);
|
|
145
168
|
|
|
@@ -217,12 +240,17 @@ export const EmployeeSelect = ({
|
|
|
217
240
|
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
218
241
|
<input
|
|
219
242
|
type="text"
|
|
220
|
-
placeholder="Search employees..."
|
|
243
|
+
placeholder={enableServerSearch ? "Search employees (server-side)..." : "Search employees..."}
|
|
221
244
|
value={searchTerm}
|
|
222
245
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
223
246
|
className="w-full pl-8 pr-3 py-2 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 bg-background"
|
|
224
247
|
onClick={(e) => e.stopPropagation()}
|
|
225
248
|
/>
|
|
249
|
+
{enableServerSearch && isSearching && (
|
|
250
|
+
<div className="absolute right-2 top-1/2 transform -translate-y-1/2">
|
|
251
|
+
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-primary"></div>
|
|
252
|
+
</div>
|
|
253
|
+
)}
|
|
226
254
|
</div>
|
|
227
255
|
</div>
|
|
228
256
|
{allowAll && <SelectItem value="__all__">{allLabel}</SelectItem>}
|