@snapdragonsnursery/react-components 1.13.0 → 1.16.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/README.md CHANGED
@@ -188,16 +188,16 @@ The `ChildSearchFilters` component now includes an "Apply Filters" button that p
188
188
 
189
189
  ## Environment Variables
190
190
 
191
- Set these environment variables in your application:
191
+ Set this environment variable in your application:
192
192
 
193
193
  ```env
194
- VITE_COMMON_API_BASE_URL=https://snaps-common-api.azurewebsites.net
194
+ VITE_APIM_SCOPE=api://your-apim-app-id/.default
195
195
  ```
196
196
 
197
197
  ## Documentation
198
198
 
199
- - [ChildSearchModal Documentation](./CHILD_SEARCH_MODAL_DOCUMENTATION.md)
200
- - [ChildSearchModal README](./CHILD_SEARCH_README.md)
199
+ - [ChildSearchModal Documentation](docs/CHILD_SEARCH_MODAL_DOCUMENTATION.md)
200
+ - [ChildSearchModal README](docs/CHILD_SEARCH_README.md)
201
201
  - [Release Guide](./RELEASE.md)
202
202
  - [SoftWarningAlert](./SOFT_WARNING_ALERT.md)
203
203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapdragonsnursery/react-components",
3
- "version": "1.13.0",
3
+ "version": "1.16.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -26,6 +26,12 @@ const ChildSearchModal = ({
26
26
  multiSelect = false, // Enable multiple child selection
27
27
  maxSelections = null, // Maximum number of children that can be selected (null = unlimited)
28
28
  selectedChildren = [], // Array of already selected children (for multi-select mode)
29
+ // Auth options: provide token directly or a function to fetch it
30
+ authToken = null,
31
+ getAccessToken = null,
32
+ // Layout controls
33
+ zIndex = 60,
34
+ maxHeightVh = 90,
29
35
  }) => {
30
36
  const [searchTerm, setSearchTerm] = useState("");
31
37
  const [children, setChildren] = useState([]);
@@ -137,8 +143,35 @@ const ChildSearchModal = ({
137
143
  setError(null);
138
144
 
139
145
  try {
140
- // Get function key for authentication
141
- const functionKey = import.meta.env.VITE_COMMON_API_FUNCTION_KEY || "";
146
+ // Resolve access token: prefer prop function, then prop token, then MSAL using APIM scope
147
+ const apimScope = import.meta.env.VITE_APIM_SCOPE;
148
+
149
+ let accessToken = null;
150
+ if (typeof getAccessToken === "function") {
151
+ accessToken = await getAccessToken();
152
+ } else if (authToken) {
153
+ accessToken = authToken;
154
+ } else if (apimScope) {
155
+ try {
156
+ const response = await instance.acquireTokenSilent({
157
+ account: accounts[0],
158
+ scopes: [apimScope],
159
+ });
160
+ accessToken = response.accessToken;
161
+ } catch (silentErr) {
162
+ // Fallback to popup if silent acquisition fails
163
+ const response = await instance.acquireTokenPopup({
164
+ scopes: [apimScope],
165
+ });
166
+ accessToken = response.accessToken;
167
+ }
168
+ }
169
+
170
+ if (!accessToken) {
171
+ throw new Error(
172
+ "Missing access token. Provide authToken/getAccessToken or set VITE_APIM_SCOPE."
173
+ );
174
+ }
142
175
 
143
176
  // Build query parameters
144
177
  const params = new URLSearchParams({
@@ -186,16 +219,13 @@ const ChildSearchModal = ({
186
219
  params.append("sort_by", advancedFilters.sortBy);
187
220
  params.append("sort_order", advancedFilters.sortOrder);
188
221
 
189
- // Make API call
222
+ // Make API call via APIM with Bearer token
190
223
  const apiResponse = await fetch(
191
- `${
192
- import.meta.env.VITE_COMMON_API_BASE_URL ||
193
- "https://snaps-common-api.azurewebsites.net"
194
- }/api/search-children?${params}`,
224
+ `https://snapdragons.azure-api.net/api/children/search-children?${params}`,
195
225
  {
196
226
  headers: {
197
227
  "Content-Type": "application/json",
198
- "x-functions-key": functionKey,
228
+ Authorization: `Bearer ${accessToken}`,
199
229
  },
200
230
  }
201
231
  );
@@ -351,13 +381,13 @@ const ChildSearchModal = ({
351
381
  if (!isOpen) return null;
352
382
 
353
383
  return (
354
- <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4 overflow-hidden">
384
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 overflow-hidden" style={{ zIndex }}>
355
385
  <div
356
386
  ref={modalRef}
357
387
  className={`bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-4xl w-full flex flex-col overflow-hidden ${className}`}
358
388
  style={{
359
- maxHeight: '90vh',
360
- height: '90vh',
389
+ maxHeight: `${maxHeightVh}vh`,
390
+ height: `${maxHeightVh}vh`,
361
391
  display: 'flex',
362
392
  flexDirection: 'column',
363
393
  position: 'relative'
@@ -61,6 +61,9 @@ const ChildSearchPage = ({
61
61
  multiSelect = false,
62
62
  maxSelections = null,
63
63
  selectedChildren = [],
64
+ // Auth options: provide token directly or a function to fetch it
65
+ authToken = null,
66
+ getAccessToken = null,
64
67
  }) => {
65
68
  const [searchTerm, setSearchTerm] = useState("");
66
69
  const [children, setChildren] = useState([]);
@@ -286,7 +289,34 @@ const ChildSearchPage = ({
286
289
  setError(null);
287
290
 
288
291
  try {
289
- const functionKey = import.meta.env.VITE_COMMON_API_FUNCTION_KEY || "";
292
+ // Resolve access token for APIM
293
+ const apimScope = import.meta.env.VITE_APIM_SCOPE;
294
+
295
+ let accessToken = null;
296
+ if (typeof getAccessToken === "function") {
297
+ accessToken = await getAccessToken();
298
+ } else if (authToken) {
299
+ accessToken = authToken;
300
+ } else if (apimScope) {
301
+ try {
302
+ const response = await instance.acquireTokenSilent({
303
+ account: accounts[0],
304
+ scopes: [apimScope],
305
+ });
306
+ accessToken = response.accessToken;
307
+ } catch (silentErr) {
308
+ const response = await instance.acquireTokenPopup({
309
+ scopes: [apimScope],
310
+ });
311
+ accessToken = response.accessToken;
312
+ }
313
+ }
314
+
315
+ if (!accessToken) {
316
+ throw new Error(
317
+ "Missing access token. Provide authToken/getAccessToken or set VITE_APIM_SCOPE."
318
+ );
319
+ }
290
320
 
291
321
  const params = new URLSearchParams({
292
322
  entra_id: accounts[0].localAccountId,
@@ -334,14 +364,11 @@ const ChildSearchPage = ({
334
364
  params.append("sort_order", debouncedAdvancedFilters.sortOrder);
335
365
 
336
366
  const apiResponse = await fetch(
337
- `${
338
- import.meta.env.VITE_COMMON_API_BASE_URL ||
339
- "https://snaps-common-api.azurewebsites.net"
340
- }/api/search-children?${params}`,
367
+ `https://snapdragons.azure-api.net/api/children/search-children?${params}`,
341
368
  {
342
369
  headers: {
343
370
  "Content-Type": "application/json",
344
- "x-functions-key": functionKey,
371
+ Authorization: `Bearer ${accessToken}`,
345
372
  },
346
373
  }
347
374
  );
@@ -174,6 +174,42 @@ const EmployeeSearchDemo = () => {
174
174
  showWorkingHoursFilter={true}
175
175
  />
176
176
  </div>
177
+
178
+ {/* Custom Columns (Page) */}
179
+ <div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
180
+ <h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
181
+ Custom Columns (Page)
182
+ </h3>
183
+ <p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
184
+ Demonstrates visibleColumns, columnLabels, and columnRenderers.
185
+ </p>
186
+ <EmployeeSearchPage
187
+ title="Custom Column Set"
188
+ onSelect={handleEmployeeSelect}
189
+ sites={mockSites}
190
+ roles={mockRoles}
191
+ managers={mockManagers}
192
+ visibleColumns={[
193
+ 'full_name',
194
+ 'site_name',
195
+ 'role_name',
196
+ 'email',
197
+ 'employee_status',
198
+ ]}
199
+ columnLabels={{
200
+ full_name: 'Employee',
201
+ site_name: 'Location',
202
+ role_name: 'Position',
203
+ }}
204
+ columnRenderers={{
205
+ email: (row) => (
206
+ <a href={`mailto:${row.email}`} className="text-blue-600">
207
+ {row.email}
208
+ </a>
209
+ ),
210
+ }}
211
+ />
212
+ </div>
177
213
  </div>
178
214
 
179
215
  {/* Site-Specific Search */}
@@ -272,4 +308,4 @@ const EmployeeSearchDemo = () => {
272
308
  );
273
309
  };
274
310
 
275
- export default EmployeeSearchDemo;
311
+ export default EmployeeSearchDemo;