@snapdragonsnursery/react-components 1.0.11 → 1.1.1

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
@@ -1,528 +1,167 @@
1
- # react-components
1
+ # @snapdragonsnursery/react-components
2
2
 
3
- Reusable React components for Snapdragons projects.
3
+ A collection of reusable React components for Snapdragons Nursery applications.
4
4
 
5
5
  ## Components
6
6
 
7
- - **AuthButtons**: Profile button with authentication, user image, dropdown menu (sign out, dark mode), and login/logout handling for MSAL (Azure AD).
8
- - **ThemeToggle**: Simple theme (light/dark/system) toggle button.
9
- - **LandingPage**: Professional landing page component for Microsoft authentication flows with customizable branding.
10
- - **ChildSearchModal**: Modal component for searching and selecting children with advanced filtering and selection capabilities.
7
+ - **ChildSearchModal**: Advanced child search and selection component with filtering, pagination, and multi-select capabilities
8
+ - **AuthButtons**: Authentication buttons for MSAL integration
9
+ - **ThemeToggle**: Dark/light theme toggle component
10
+ - **LandingPage**: Landing page component with authentication
11
11
 
12
12
  ## Installation
13
13
 
14
- Install via npm (local path or published package):
15
-
16
- ```
14
+ ```bash
17
15
  npm install @snapdragonsnursery/react-components
18
16
  ```
19
17
 
20
- ## Required Dependencies
21
-
22
- Your project must have the following installed:
23
-
24
- ### Core Dependencies
25
-
26
- ```bash
27
- npm install react react-dom
28
- npm install @azure/msal-browser @azure/msal-react
29
- npm install @headlessui/react @heroicons/react
30
- ```
18
+ ## Quick Start
31
19
 
32
- ### CSS Framework (Required for AuthButtons styling)
20
+ ```jsx
21
+ import { ChildSearchModal } from '@snapdragonsnursery/react-components';
33
22
 
34
- ```bash
35
- npm install -D tailwindcss postcss autoprefixer
36
- npx tailwindcss init -p
23
+ function MyComponent() {
24
+ const [isModalOpen, setIsModalOpen] = useState(false);
25
+
26
+ return (
27
+ <ChildSearchModal
28
+ isOpen={isModalOpen}
29
+ onClose={() => setIsModalOpen(false)}
30
+ onSelect={(child) => console.log('Selected:', child)}
31
+ title="Search for a Child"
32
+ />
33
+ );
34
+ }
37
35
  ```
38
36
 
39
- ## Complete Setup Guide for New Projects
37
+ ## Environment Variables
40
38
 
41
- ### 1. Install Dependencies
42
-
43
- ```bash
44
- # Core authentication dependencies
45
- npm install @azure/msal-browser @azure/msal-react
39
+ Set these environment variables in your application:
46
40
 
47
- # UI dependencies for AuthButtons
48
- npm install @headlessui/react @heroicons/react
49
-
50
- # Tailwind CSS (required for styling)
51
- npm install -D tailwindcss postcss autoprefixer
52
- npx tailwindcss init -p
53
-
54
- # Install this component library
55
- npm install @snapdragonsnursery/react-components
41
+ ```env
42
+ VITE_COMMON_API_BASE_URL=https://snaps-common-api.azurewebsites.net
56
43
  ```
57
44
 
58
- ### 2. Configure Tailwind CSS
59
-
60
- Update `tailwind.config.js`:
45
+ ## Documentation
61
46
 
62
- ```javascript
63
- /** @type {import('tailwindcss').Config} */
64
- export default {
65
- content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
66
- darkMode: "class",
67
- theme: {
68
- extend: {},
69
- },
70
- plugins: [],
71
- };
72
- ```
47
+ - [ChildSearchModal Documentation](./CHILD_SEARCH_MODAL_DOCUMENTATION.md)
48
+ - [ChildSearchModal README](./CHILD_SEARCH_README.md)
49
+ - [Release Guide](./RELEASE.md)
73
50
 
74
- Update `src/index.css`:
51
+ ---
75
52
 
76
- ```css
77
- @tailwind base;
78
- @tailwind components;
79
- @tailwind utilities;
53
+ ## 🚀 Publishing New Versions
80
54
 
81
- /* Your custom styles */
82
- body {
83
- margin: 0;
84
- font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
85
- "Oxygen", "Ubuntu", "Cantarell", "Open Sans", "Helvetica Neue", sans-serif;
86
- -webkit-font-smoothing: antialiased;
87
- -moz-osx-font-smoothing: grayscale;
88
- }
55
+ ### Quick Release (Recommended)
89
56
 
90
- #root {
91
- min-height: 100vh;
92
- }
93
- ```
57
+ ```bash
58
+ # Patch release (bug fixes) - 1.1.0 → 1.1.1
59
+ npm run release
94
60
 
95
- ### 3. Create Authentication Configuration
96
-
97
- Create `src/utils/authConfig.js`:
98
-
99
- ```javascript
100
- import { LogLevel } from "@azure/msal-browser";
101
-
102
- export const msalConfig = {
103
- auth: {
104
- clientId: "2e2d9456-a0b7-4651-a8f8-c979ed1486b0", // Snapdragons Azure AD App
105
- authority:
106
- "https://login.microsoftonline.com/d098ee48-9874-4959-8229-da24c99c36fb",
107
- redirectUri: window.location.origin,
108
- postLogoutRedirectUri: window.location.origin,
109
- navigateToLoginRequestUrl: false,
110
- },
111
- cache: {
112
- cacheLocation: "sessionStorage",
113
- storeAuthStateInCookie: false,
114
- },
115
- system: {
116
- loggerOptions: {
117
- loggerCallback: (level, message, containsPii) => {
118
- if (containsPii) return;
119
- switch (level) {
120
- case LogLevel.Error:
121
- console.error(message);
122
- return;
123
- case LogLevel.Warning:
124
- console.warn(message);
125
- return;
126
- default:
127
- return;
128
- }
129
- },
130
- logLevel: LogLevel.Warning,
131
- },
132
- },
133
- };
134
-
135
- export const loginRequest = {
136
- scopes: ["User.Read", "email", "profile", "Directory.Read.All"],
137
- };
138
-
139
- export const adminConfig = {
140
- adminEmailDomains: [],
141
- adminEmails: [
142
- "james@snapdragonsnursery.com",
143
- "accounts@snapdragonsnursery.com",
144
- ],
145
- adminGroupIds: [],
146
- adminRoles: ["FundingAdmin", "Admin"],
147
- };
148
-
149
- export const isUserAdmin = (account, idTokenClaims = null) => {
150
- if (!account) return false;
151
- const claims = idTokenClaims || account.idTokenClaims;
152
- const email = account.username || claims?.email || claims?.preferred_username;
153
-
154
- if (
155
- email &&
156
- adminConfig.adminEmailDomains.some((domain) =>
157
- email.toLowerCase().endsWith(`@${domain.toLowerCase()}`)
158
- )
159
- ) {
160
- return true;
161
- }
162
-
163
- if (
164
- email &&
165
- adminConfig.adminEmails.some(
166
- (adminEmail) => email.toLowerCase() === adminEmail.toLowerCase()
167
- )
168
- ) {
169
- return true;
170
- }
171
-
172
- if (claims?.groups && adminConfig.adminGroupIds.length > 0) {
173
- const userGroups = Array.isArray(claims.groups)
174
- ? claims.groups
175
- : [claims.groups];
176
- if (
177
- userGroups.some((groupId) => adminConfig.adminGroupIds.includes(groupId))
178
- ) {
179
- return true;
180
- }
181
- }
182
-
183
- if (claims?.roles && adminConfig.adminRoles.length > 0) {
184
- const userRoles = Array.isArray(claims.roles)
185
- ? claims.roles
186
- : [claims.roles];
187
- if (userRoles.some((role) => adminConfig.adminRoles.includes(role))) {
188
- return true;
189
- }
190
- }
191
-
192
- return false;
193
- };
194
- ```
61
+ # Minor release (new features) - 1.1.0 1.2.0
62
+ npm run release:minor
195
63
 
196
- ### 4. Create Authentication Provider
197
-
198
- Create `src/utils/AuthProvider.jsx`:
199
-
200
- ```javascript
201
- import React, { createContext, useContext, useEffect, useState } from "react";
202
- import { useMsal, useAccount } from "@azure/msal-react";
203
- import { isUserAdmin } from "./authConfig";
204
-
205
- const AuthContext = createContext();
206
-
207
- export const useAuth = () => {
208
- const context = useContext(AuthContext);
209
- if (!context) {
210
- throw new Error("useAuth must be used within an AuthProvider");
211
- }
212
- return context;
213
- };
214
-
215
- export const AuthProvider = ({ children }) => {
216
- const { instance, accounts } = useMsal();
217
- const account = useAccount(accounts[0] || {});
218
- const [isLoading, setIsLoading] = useState(true);
219
- const [userRole, setUserRole] = useState(null);
220
-
221
- useEffect(() => {
222
- const checkUserRole = async () => {
223
- if (account) {
224
- const adminStatus = isUserAdmin(account);
225
- setUserRole(adminStatus ? "admin" : "employee");
226
- } else {
227
- setUserRole(null);
228
- }
229
- setIsLoading(false);
230
- };
231
- checkUserRole();
232
- }, [account]);
233
-
234
- const login = async () => {
235
- try {
236
- await instance.loginPopup();
237
- } catch (error) {
238
- console.error("Login failed:", error);
239
- throw error;
240
- }
241
- };
242
-
243
- const logout = async () => {
244
- try {
245
- await instance.logoutPopup();
246
- } catch (error) {
247
- console.error("Logout failed:", error);
248
- throw error;
249
- }
250
- };
251
-
252
- const isAuthenticated = !!account;
253
- const isAdmin = userRole === "admin";
254
-
255
- const value = {
256
- account,
257
- isAuthenticated,
258
- isAdmin,
259
- userRole,
260
- isLoading,
261
- login,
262
- logout,
263
- instance,
264
- };
265
-
266
- return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
267
- };
64
+ # Major release (breaking changes) - 1.1.0 2.0.0
65
+ npm run release:major
268
66
  ```
269
67
 
270
- ### 5. Create Theme Provider
271
-
272
- Create `src/utils/theme.jsx`:
273
-
274
- ```javascript
275
- import { createContext, useContext, useEffect, useState } from "react";
276
-
277
- const ThemeContext = createContext();
278
-
279
- export function ThemeProvider({ children }) {
280
- const [theme, setTheme] = useState("system");
281
-
282
- useEffect(() => {
283
- const savedTheme = localStorage.getItem("theme");
284
- if (savedTheme && ["light", "dark", "system"].includes(savedTheme)) {
285
- setTheme(savedTheme);
286
- } else {
287
- setTheme("system");
288
- }
289
- }, []);
290
-
291
- useEffect(() => {
292
- const applyTheme = () => {
293
- if (theme === "system") {
294
- const systemPrefersDark = window.matchMedia(
295
- "(prefers-color-scheme: dark)"
296
- ).matches;
297
- if (systemPrefersDark) {
298
- document.documentElement.classList.add("dark");
299
- } else {
300
- document.documentElement.classList.remove("dark");
301
- }
302
- } else if (theme === "dark") {
303
- document.documentElement.classList.add("dark");
304
- } else {
305
- document.documentElement.classList.remove("dark");
306
- }
307
- };
308
-
309
- applyTheme();
310
- localStorage.setItem("theme", theme);
311
- }, [theme]);
312
-
313
- useEffect(() => {
314
- if (theme !== "system") return;
315
-
316
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
317
- const handleChange = () => {
318
- if (theme === "system") {
319
- if (mediaQuery.matches) {
320
- document.documentElement.classList.add("dark");
321
- } else {
322
- document.documentElement.classList.remove("dark");
323
- }
324
- }
325
- };
326
-
327
- mediaQuery.addEventListener("change", handleChange);
328
- return () => mediaQuery.removeEventListener("change", handleChange);
329
- }, [theme]);
330
-
331
- return (
332
- <ThemeContext.Provider value={{ theme, setTheme }}>
333
- {children}
334
- </ThemeContext.Provider>
335
- );
336
- }
337
-
338
- export function useTheme() {
339
- const context = useContext(ThemeContext);
340
- if (context === undefined) {
341
- throw new Error("useTheme must be used within a ThemeProvider");
342
- }
343
- return context;
344
- }
345
- ```
68
+ ### GitHub Actions (Web Interface)
346
69
 
347
- ### 6. Setup Main Entry Point
348
-
349
- Update `src/main.jsx`:
350
-
351
- ```javascript
352
- import React from "react";
353
- import ReactDOM from "react-dom/client";
354
- import App from "./App.jsx";
355
- import "./index.css";
356
- import { MsalProvider } from "@azure/msal-react";
357
- import { PublicClientApplication } from "@azure/msal-browser";
358
- import { msalConfig } from "./utils/authConfig";
359
- import { AuthProvider } from "./utils/AuthProvider";
360
- import { ThemeProvider } from "./utils/theme";
361
-
362
- const msalInstance = new PublicClientApplication(msalConfig);
363
-
364
- ReactDOM.createRoot(document.getElementById("root")).render(
365
- <React.StrictMode>
366
- <MsalProvider instance={msalInstance}>
367
- <AuthProvider>
368
- <ThemeProvider>
369
- <App />
370
- </ThemeProvider>
371
- </AuthProvider>
372
- </MsalProvider>
373
- </React.StrictMode>
374
- );
375
- ```
70
+ 1. Go to [GitHub Actions](https://github.com/Snapdragons-Nursery/react-components/actions)
71
+ 2. Click "Publish Package"
72
+ 3. Click "Run workflow"
73
+ 4. Select version type (patch/minor/major)
74
+ 5. Click "Run workflow"
376
75
 
377
- ### 7. Create Navbar Component
378
-
379
- Create `src/components/Navbar.jsx`:
380
-
381
- ```javascript
382
- import React, { useState, useEffect } from "react";
383
- import { AuthButtons } from "@snapdragonsnursery/react-components";
384
- import { useAuth } from "../utils/AuthProvider";
385
- import { useTheme } from "../utils/theme";
386
-
387
- const Navbar = () => {
388
- const { account, userRole, instance } = useAuth();
389
- const { theme, setTheme } = useTheme();
390
- const [authState, setAuthState] = useState({
391
- isAuthenticated: !!account,
392
- accountsCount: account ? 1 : 0,
393
- account,
394
- instance,
395
- });
396
-
397
- useEffect(() => {
398
- setAuthState({
399
- isAuthenticated: !!account,
400
- accountsCount: account ? 1 : 0,
401
- account,
402
- instance,
403
- });
404
- }, [account, instance]);
76
+ ### Manual Commands
405
77
 
406
- return (
407
- <nav className="bg-white dark:bg-gray-800 shadow-md sticky top-0 z-40 px-4 py-2 flex justify-between items-center">
408
- <div className="flex items-center space-x-4">
409
- <span className="font-bold text-lg text-gray-800 dark:text-gray-100">
410
- Your App Name
411
- </span>
412
- </div>
413
- <div className="flex items-center space-x-4">
414
- <AuthButtons
415
- theme={theme}
416
- setTheme={setTheme}
417
- authState={authState}
418
- setAuthState={setAuthState}
419
- />
420
- </div>
421
- </nav>
422
- );
423
- };
78
+ ```bash
79
+ # Just bump version
80
+ npm run version:patch
81
+ npm run version:minor
82
+ npm run version:major
424
83
 
425
- export default Navbar;
84
+ # Bump and publish
85
+ npm run publish:patch
86
+ npm run publish:minor
87
+ npm run publish:major
426
88
  ```
427
89
 
428
- ### 8. Use in Your App
429
-
430
- Update `src/App.jsx`:
431
-
432
- ```javascript
433
- import Navbar from "./components/Navbar";
90
+ ### Pre-release Versions (Beta)
434
91
 
435
- function App() {
436
- return (
437
- <>
438
- <Navbar />
439
- {/* Your app content */}
440
- </>
441
- );
442
- }
92
+ ```bash
93
+ # Create beta version
94
+ npm run publish:prepatch
95
+ npm run publish:preminor
96
+ npm run publish:premajor
443
97
 
444
- export default App;
98
+ # Install beta in consuming apps
99
+ npm install @snapdragonsnursery/react-components@beta
445
100
  ```
446
101
 
447
- ## Usage
102
+ ## Version Types
448
103
 
449
- Once setup is complete, the AuthButtons component will:
104
+ | Type | Example | Use Case |
105
+ |------|---------|----------|
106
+ | **patch** | 1.1.0 → 1.1.1 | Bug fixes, documentation |
107
+ | **minor** | 1.1.0 → 1.2.0 | New features, backward compatible |
108
+ | **major** | 1.1.0 → 2.0.0 | Breaking changes |
109
+ | **prepatch** | 1.1.0 → 1.1.1-0 | Beta patch release |
110
+ | **preminor** | 1.1.0 → 1.2.0-0 | Beta minor release |
111
+ | **premajor** | 1.1.0 → 2.0.0-0 | Beta major release |
450
112
 
451
- - Show a user icon when not authenticated (clickable to login)
452
- - Show user profile picture or initials when authenticated
453
- - Provide a dropdown menu with theme toggle and logout option
454
- - Handle all authentication flows automatically
113
+ ## Release Checklist
455
114
 
456
- ## LandingPage Component
115
+ Before releasing:
457
116
 
458
- The LandingPage component provides a professional landing page for Microsoft authentication flows.
117
+ - [ ] All changes committed and pushed
118
+ - [ ] Tests pass (if configured)
119
+ - [ ] Documentation updated
120
+ - [ ] Version type selected (patch/minor/major)
121
+ - [ ] NPM token configured (for GitHub Actions)
459
122
 
460
- ### Quick Start
461
-
462
- ```jsx
463
- import { LandingPage } from '@snapdragonsnursery/react-components';
123
+ ## Troubleshooting
464
124
 
465
- function App() {
466
- const handleSignIn = () => {
467
- // Implement your Microsoft authentication logic
468
- console.log('Sign in clicked');
469
- };
125
+ ### Common Issues
470
126
 
471
- return (
472
- <LandingPage
473
- logoUrl="https://your-logo-url.png"
474
- appName="Your Application"
475
- appDescription="Manage your application efficiently"
476
- onSignIn={handleSignIn}
477
- companyName="Your Company"
478
- />
479
- );
480
- }
127
+ **"Working directory not clean"**
128
+ ```bash
129
+ git add .
130
+ git commit -m "Prepare for release"
481
131
  ```
482
132
 
483
- ### Props
484
-
485
- | Prop | Type | Required | Default | Description |
486
- |------|------|----------|---------|-------------|
487
- | `logoUrl` | `string` | ✅ | - | URL to your company logo |
488
- | `appName` | `string` | ✅ | - | Name of your application |
489
- | `appDescription` | `string` | ❌ | "Manage your application efficiently" | App description |
490
- | `onSignIn` | `function` | ❌ | `() => console.log('Sign in clicked')` | Sign-in callback |
491
- | `companyName` | `string` | ❌ | "Your Company" | Company name for footer |
492
- | `backgroundColor` | `string` | ❌ | Blue gradient | CSS background value |
493
- | `cardClassName` | `string` | ❌ | `""` | Additional CSS classes |
494
-
495
- ### Features
133
+ **"Not on main branch"**
134
+ ```bash
135
+ git checkout main
136
+ git pull origin main
137
+ ```
496
138
 
497
- - **Framework Agnostic**: Uses inline styles for maximum compatibility
498
- - **Responsive Design**: Works on all device sizes
499
- - **Microsoft Integration**: Official Microsoft logo and branding
500
- - **Customizable**: Configurable logo, colors, and branding
501
- - **Accessible**: Proper semantic HTML and keyboard navigation
139
+ **"NPM authentication failed"**
140
+ - Check GitHub Secrets NPM_TOKEN
141
+ - Verify token has publish permissions
502
142
 
503
- For detailed documentation, see [LANDING_PAGE_DOCUMENTATION.md](./LANDING_PAGE_DOCUMENTATION.md).
143
+ **"Version already exists"**
144
+ ```bash
145
+ npm run release:minor # Use different version type
146
+ ```
504
147
 
505
- ## Troubleshooting
148
+ ## Support
506
149
 
507
- ### Common Issues
150
+ For issues with components or release process:
151
+ 1. Check component documentation
152
+ 2. Review [Release Guide](./RELEASE.md)
153
+ 3. Check GitHub Actions logs
154
+ 4. Contact the development team
508
155
 
509
- 1. **Blank icon**: Ensure Tailwind CSS is properly configured and included in your build
510
- 2. **Authentication errors**: Check that all MSAL dependencies are installed and providers are set up correctly
511
- 3. **Theme not working**: Ensure ThemeProvider is wrapping your app and `darkMode: 'class'` is set in Tailwind config
156
+ ## Contributing
512
157
 
513
- ### Required File Structure
158
+ 1. Make changes in feature branch
159
+ 2. Test locally
160
+ 3. Create pull request
161
+ 4. Merge to main
162
+ 5. Release using one of the methods above
514
163
 
515
- ```
516
- src/
517
- ├── utils/
518
- │ ├── authConfig.js
519
- │ ├── AuthProvider.jsx
520
- │ └── theme.jsx
521
- ├── components/
522
- │ └── Navbar.jsx
523
- ├── main.jsx
524
- ├── App.jsx
525
- └── index.css
526
- ```
164
+ ---
527
165
 
528
- For questions or contributions, contact the Snapdragons development team.
166
+ **Current Version**: 1.1.0
167
+ **Last Updated**: January 2024
package/package.json CHANGED
@@ -1,10 +1,28 @@
1
1
  {
2
2
  "name": "@snapdragonsnursery/react-components",
3
- "version": "1.0.11",
3
+ "version": "1.1.1",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
7
+ "test": "echo \"No tests specified - skipping\"",
8
+ "version:patch": "npm version patch",
9
+ "version:minor": "npm version minor",
10
+ "version:major": "npm version major",
11
+ "version:prepatch": "npm version prepatch",
12
+ "version:preminor": "npm version preminor",
13
+ "version:premajor": "npm version premajor",
14
+ "version:prerelease": "npm version prerelease",
15
+ "publish:patch": "npm run version:patch && npm publish",
16
+ "publish:minor": "npm run version:minor && npm publish",
17
+ "publish:major": "npm run version:major && npm publish",
18
+ "publish:prepatch": "npm run version:prepatch && npm publish --tag beta",
19
+ "publish:preminor": "npm run version:preminor && npm publish --tag beta",
20
+ "publish:premajor": "npm run version:premajor && npm publish --tag beta",
21
+ "publish:prerelease": "npm run version:prerelease && npm publish --tag beta",
22
+ "release": "node scripts/release.js",
23
+ "release:patch": "node scripts/release.js patch",
24
+ "release:minor": "node scripts/release.js minor",
25
+ "release:major": "node scripts/release.js major"
8
26
  },
9
27
  "repository": {
10
28
  "type": "git",
@@ -105,8 +105,11 @@ const ChildSearchModal = ({
105
105
 
106
106
  try {
107
107
  // Get access token
108
+ const apiBaseUrl = process.env.VITE_COMMON_API_BASE_URL || "https://snaps-common-api.azurewebsites.net";
109
+ const apiScope = apiBaseUrl.replace(/^https?:\/\//, "api://") + "/.default";
110
+
108
111
  const response = await instance.acquireTokenSilent({
109
- scopes: ["api://your-api-scope/.default"], // Update with your actual API scope
112
+ scopes: [apiScope],
110
113
  account: accounts[0],
111
114
  });
112
115
 
@@ -159,8 +162,8 @@ const ChildSearchModal = ({
159
162
  // Make API call
160
163
  const apiResponse = await fetch(
161
164
  `${
162
- process.env.REACT_APP_API_BASE_URL ||
163
- "https://your-function-app.azurewebsites.net"
165
+ process.env.VITE_COMMON_API_BASE_URL ||
166
+ "https://snaps-common-api.azurewebsites.net"
164
167
  }/api/search-children?${params}`,
165
168
  {
166
169
  headers: {