@snapdragonsnursery/react-components 1.17.3 → 1.17.5
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
|
@@ -47,7 +47,7 @@ const EmployeeSearchModal = ({
|
|
|
47
47
|
onClose,
|
|
48
48
|
onSelect,
|
|
49
49
|
title = "Select Employee",
|
|
50
|
-
searchPlaceholder = "Search by name,
|
|
50
|
+
searchPlaceholder = "Search by name, ID, or email address...",
|
|
51
51
|
siteId = null,
|
|
52
52
|
siteIds = null,
|
|
53
53
|
sites = null,
|
|
@@ -984,7 +984,7 @@ const EmployeeSearchModal = ({
|
|
|
984
984
|
>
|
|
985
985
|
{debouncedSearchTerm
|
|
986
986
|
? "No employees found matching your search."
|
|
987
|
-
: "
|
|
987
|
+
: "Type a name, ID, or email to find an employee"}
|
|
988
988
|
</TableCell>
|
|
989
989
|
</TableRow>
|
|
990
990
|
)}
|
|
@@ -995,7 +995,7 @@ const EmployeeSearchModal = ({
|
|
|
995
995
|
<div className="p-6 text-center text-gray-500 dark:text-gray-400">
|
|
996
996
|
{debouncedSearchTerm
|
|
997
997
|
? "No employees found matching your search."
|
|
998
|
-
: "
|
|
998
|
+
: "Type a name, ID, or email to find an employee"}
|
|
999
999
|
</div>
|
|
1000
1000
|
)}
|
|
1001
1001
|
|
|
@@ -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>}
|