@snapdragonsnursery/react-components 1.23.0 → 1.26.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.
package/package.json
CHANGED
package/src/ChildSearchModal.jsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, {
|
|
2
2
|
useState,
|
|
3
3
|
useEffect,
|
|
4
|
+
useLayoutEffect,
|
|
4
5
|
useRef,
|
|
5
6
|
useCallback,
|
|
6
7
|
useMemo,
|
|
@@ -90,6 +91,30 @@ const ChildSearchModal = ({
|
|
|
90
91
|
const { instance, accounts } = useMsal();
|
|
91
92
|
const modalRef = useRef();
|
|
92
93
|
const searchInputRef = useRef();
|
|
94
|
+
const prevIsOpenRef = useRef(false);
|
|
95
|
+
const statusPropRef = useRef(status);
|
|
96
|
+
const activeOnlyPropRef = useRef(activeOnly);
|
|
97
|
+
statusPropRef.current = status;
|
|
98
|
+
activeOnlyPropRef.current = activeOnly;
|
|
99
|
+
|
|
100
|
+
// When the modal opens (closed → open), reset status from props so a previous session
|
|
101
|
+
// does not stick and cause searches with no status param (API defaults to active-only).
|
|
102
|
+
useLayoutEffect(() => {
|
|
103
|
+
if (!isOpen) {
|
|
104
|
+
prevIsOpenRef.current = false;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const wasOpen = prevIsOpenRef.current;
|
|
108
|
+
prevIsOpenRef.current = true;
|
|
109
|
+
if (wasOpen) return;
|
|
110
|
+
setAdvancedFilters((prev) => ({
|
|
111
|
+
...prev,
|
|
112
|
+
// Match useState initialiser: treat "" like missing so we do not omit status on the API.
|
|
113
|
+
status:
|
|
114
|
+
statusPropRef.current ||
|
|
115
|
+
(activeOnlyPropRef.current ? "active" : "all"),
|
|
116
|
+
}));
|
|
117
|
+
}, [isOpen]);
|
|
93
118
|
|
|
94
119
|
// Debounce search term
|
|
95
120
|
useEffect(() => {
|
|
@@ -101,6 +126,42 @@ const ChildSearchModal = ({
|
|
|
101
126
|
return () => clearTimeout(timer);
|
|
102
127
|
}, [searchTerm]);
|
|
103
128
|
|
|
129
|
+
// Value-stable key for siteIds so the sync effect only runs when the set of site IDs actually
|
|
130
|
+
// changes, not on every parent re-render (avoids resetting user's site/room selection and pagination).
|
|
131
|
+
const siteIdsKey = useMemo(
|
|
132
|
+
() =>
|
|
133
|
+
Array.isArray(siteIds) && siteIds.length > 0
|
|
134
|
+
? [...siteIds].sort((a, b) => a - b).join(",")
|
|
135
|
+
: "",
|
|
136
|
+
[siteIds]
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// Keep the internal site filter in sync with externally controlled site changes
|
|
140
|
+
// (for example when the app site is switched from a sidebar while the page stays mounted).
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
const nextSelectedSiteId =
|
|
143
|
+
siteIdsKey !== "" ? "" : siteId || "";
|
|
144
|
+
|
|
145
|
+
setAdvancedFilters((prev) => {
|
|
146
|
+
if (
|
|
147
|
+
prev.selectedSiteId === nextSelectedSiteId &&
|
|
148
|
+
prev.selectedRoomId === ""
|
|
149
|
+
) {
|
|
150
|
+
return prev;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
...prev,
|
|
155
|
+
selectedSiteId: nextSelectedSiteId,
|
|
156
|
+
selectedRoomId: "",
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
setPagination((prev) =>
|
|
161
|
+
prev.page === 1 ? prev : { ...prev, page: 1 }
|
|
162
|
+
);
|
|
163
|
+
}, [siteId, siteIdsKey]);
|
|
164
|
+
|
|
104
165
|
// Close modal when clicking outside
|
|
105
166
|
useEffect(() => {
|
|
106
167
|
const handleClickOutside = (event) => {
|
|
@@ -196,9 +257,8 @@ const ChildSearchModal = ({
|
|
|
196
257
|
);
|
|
197
258
|
}
|
|
198
259
|
|
|
199
|
-
// Build query parameters
|
|
260
|
+
// Build query parameters (caller identity: Bearer token — Common API extracts oid from JWT)
|
|
200
261
|
const params = new URLSearchParams({
|
|
201
|
-
entra_id: accounts[0].localAccountId,
|
|
202
262
|
search_term: debouncedSearchTerm,
|
|
203
263
|
page: pagination.page,
|
|
204
264
|
page_size: pagination.pageSize,
|
|
@@ -215,11 +275,20 @@ const ChildSearchModal = ({
|
|
|
215
275
|
params.append("site_id", siteId.toString());
|
|
216
276
|
}
|
|
217
277
|
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
278
|
+
// Resolve status: prefer non-empty UI state, else props (handles empty/stale state before open sync).
|
|
279
|
+
const resolvedStatus =
|
|
280
|
+
advancedFilters.status != null &&
|
|
281
|
+
String(advancedFilters.status).trim() !== ""
|
|
282
|
+
? String(advancedFilters.status).trim()
|
|
283
|
+
: status || (activeOnly ? "active" : "all");
|
|
284
|
+
|
|
285
|
+
// Backend (search-children) defaults active_only to true when status is omitted, so "all"
|
|
286
|
+
// must be sent explicitly as status=all — otherwise only active children are returned.
|
|
287
|
+
if (resolvedStatus === "all") {
|
|
288
|
+
params.append("status", "all");
|
|
289
|
+
params.append("active_only", "false");
|
|
290
|
+
} else if (resolvedStatus) {
|
|
291
|
+
params.append("status", resolvedStatus);
|
|
223
292
|
}
|
|
224
293
|
|
|
225
294
|
// Add room filter
|
|
@@ -296,6 +365,8 @@ const ChildSearchModal = ({
|
|
|
296
365
|
advancedFilters,
|
|
297
366
|
applicationContext,
|
|
298
367
|
bypassPermissions,
|
|
368
|
+
status,
|
|
369
|
+
activeOnly,
|
|
299
370
|
]);
|
|
300
371
|
|
|
301
372
|
// Search when debounced term changes
|
package/src/ChildSearchPage.jsx
CHANGED
|
@@ -329,8 +329,8 @@ const ChildSearchPage = ({
|
|
|
329
329
|
);
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
// Caller identity: Bearer token — Common API extracts oid from JWT (do not send entra_id in query)
|
|
332
333
|
const params = new URLSearchParams({
|
|
333
|
-
entra_id: accounts[0].localAccountId,
|
|
334
334
|
search_term: debouncedSearchTerm,
|
|
335
335
|
page: pagination.page,
|
|
336
336
|
page_size: pagination.pageSize,
|
|
@@ -347,14 +347,19 @@ const ChildSearchPage = ({
|
|
|
347
347
|
params.append("site_id", siteId.toString());
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
)
|
|
357
|
-
|
|
350
|
+
const resolvedStatus =
|
|
351
|
+
debouncedAdvancedFilters.status != null &&
|
|
352
|
+
String(debouncedAdvancedFilters.status).trim() !== ""
|
|
353
|
+
? String(debouncedAdvancedFilters.status).trim()
|
|
354
|
+
: status || (activeOnly ? "active" : "all");
|
|
355
|
+
|
|
356
|
+
// Backend (search-children) defaults active_only to true when status is omitted, so "all"
|
|
357
|
+
// must be sent explicitly as status=all — otherwise only active children are returned.
|
|
358
|
+
if (resolvedStatus === "all") {
|
|
359
|
+
params.append("status", "all");
|
|
360
|
+
params.append("active_only", "false");
|
|
361
|
+
} else if (resolvedStatus) {
|
|
362
|
+
params.append("status", resolvedStatus);
|
|
358
363
|
}
|
|
359
364
|
|
|
360
365
|
// Add room filter
|
|
@@ -437,6 +442,8 @@ const ChildSearchPage = ({
|
|
|
437
442
|
debouncedAdvancedFilters,
|
|
438
443
|
applicationContext,
|
|
439
444
|
bypassPermissions,
|
|
445
|
+
status,
|
|
446
|
+
activeOnly,
|
|
440
447
|
]);
|
|
441
448
|
|
|
442
449
|
// Search when debounced term changes
|
|
@@ -613,8 +613,8 @@ const EmployeeSearchModal = ({
|
|
|
613
613
|
);
|
|
614
614
|
}
|
|
615
615
|
|
|
616
|
+
// Caller identity: Bearer token — Common API extracts oid from JWT (do not send entra_id in query)
|
|
616
617
|
const params = new URLSearchParams({
|
|
617
|
-
entra_id: accounts[0].localAccountId,
|
|
618
618
|
search_term: debouncedSearchTerm,
|
|
619
619
|
page: pagination.page,
|
|
620
620
|
page_size: pagination.pageSize,
|
|
@@ -426,8 +426,8 @@ const EmployeeSearchPage = ({
|
|
|
426
426
|
);
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
+
// Caller identity: Bearer token — Common API extracts oid from JWT (do not send entra_id in query)
|
|
429
430
|
const params = new URLSearchParams({
|
|
430
|
-
entra_id: accounts[0].localAccountId,
|
|
431
431
|
search_term: debouncedSearchTerm,
|
|
432
432
|
page: loadAllResults ? 1 : pagination.page,
|
|
433
433
|
page_size: loadAllResults ? 10000 : pagination.pageSize,
|