academe-kit 0.1.7 → 0.1.8

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/dist/index.d.ts CHANGED
@@ -13,7 +13,6 @@ declare const Button: React$1.ForwardRefExoticComponent<ButtonProps & React$1.Re
13
13
  type ProtectedAppProps = {
14
14
  children: React.ReactElement;
15
15
  requiredClientRoles?: string[];
16
- unauthorizedMessage?: string;
17
16
  };
18
17
  declare const ProtectedApp: React.FC<ProtectedAppProps>;
19
18
 
@@ -33,32 +32,113 @@ declare const ProtectedRouter: React.FC<ProtectedRouterProps>;
33
32
 
34
33
  declare function Spinner({ className, ...props }: React.ComponentProps<"svg">): react_jsx_runtime.JSX.Element;
35
34
 
35
+ /**
36
+ * User Service - Fetches complete user data from MongoDB
37
+ * This service interacts with the new unified users collection
38
+ */
39
+ interface MongoDBUser {
40
+ id: string;
41
+ keycloak_user_id: string;
42
+ email: string;
43
+ full_name: string;
44
+ phone?: string;
45
+ school_id?: string;
46
+ active: boolean;
47
+ created_at: string;
48
+ updated_at: string;
49
+ last_login?: string;
50
+ registration_number?: string;
51
+ school_year?: string;
52
+ serie_ano?: string;
53
+ turma?: string;
54
+ turno?: string;
55
+ class_id?: string;
56
+ nome_responsavel?: string;
57
+ email_responsavel?: string;
58
+ telefone_responsavel?: string;
59
+ data_nascimento?: string;
60
+ cpf?: string;
61
+ sexo?: string;
62
+ completion_rate: number;
63
+ engagement_rate: number;
64
+ onboarding_completo: boolean;
65
+ acesso_liberado: boolean;
66
+ roles?: string[];
67
+ }
68
+ interface UserServiceConfig {
69
+ apiBaseUrl: string;
70
+ getAccessToken: () => string | null;
71
+ }
72
+ declare class UserService {
73
+ private client;
74
+ private getAccessToken;
75
+ private cachedUser;
76
+ private cacheExpiry;
77
+ constructor(config: UserServiceConfig);
78
+ /**
79
+ * Get current user data from MongoDB
80
+ * Includes caching to reduce API calls
81
+ */
82
+ getCurrentUser(forceRefresh?: boolean): Promise<MongoDBUser | null>;
83
+ /**
84
+ * Update current user data
85
+ */
86
+ updateCurrentUser(data: Partial<MongoDBUser>): Promise<MongoDBUser | null>;
87
+ /**
88
+ * Clear cached user data
89
+ */
90
+ clearCache(): void;
91
+ /**
92
+ * Check if user has a specific role
93
+ */
94
+ hasRole(user: MongoDBUser | null, role: string): boolean;
95
+ /**
96
+ * Check if user has access to a specific school
97
+ */
98
+ hasSchoolAccess(user: MongoDBUser | null, schoolId: string): boolean;
99
+ /**
100
+ * Check if user is a student (has student-specific data)
101
+ */
102
+ isStudent(user: MongoDBUser | null): boolean;
103
+ /**
104
+ * Check if user has completed onboarding
105
+ */
106
+ hasCompletedOnboarding(user: MongoDBUser | null): boolean;
107
+ /**
108
+ * Check if user has access enabled
109
+ */
110
+ hasAccessEnabled(user: MongoDBUser | null): boolean;
111
+ }
112
+ declare const createUserService: (config: UserServiceConfig) => UserService;
113
+
36
114
  type AcademeKeycloakContextProps = {
37
115
  realm: string;
38
116
  hubUrl: string;
39
117
  clientId: string;
40
118
  keycloakUrl: string;
119
+ apiBaseUrl?: string;
41
120
  children: React.ReactElement;
42
121
  };
43
122
  type SecurityProviderProps = {
44
123
  hubUrl: string;
124
+ apiBaseUrl?: string;
45
125
  children: React.ReactElement;
46
126
  };
47
127
  type KeycloakUser = {
48
128
  name: string;
49
129
  lastName: string;
50
130
  email: string;
51
- attributes: {
52
- school_ids: Array<string>;
53
- };
131
+ };
132
+ type AcademeUser = MongoDBUser & {
133
+ keycloakUser?: KeycloakUser;
54
134
  };
55
135
  type SecurityContextType = {
56
136
  isInitialized: boolean;
57
137
  isAuthenticated: () => boolean;
58
138
  signOut: () => void | null;
59
- redirectToHub: (errorMessage: string) => void | null;
60
139
  goToLogin: (options?: KeycloakLoginOptions | undefined) => KeycloakPromise<void, void> | null;
61
- getUser: () => KeycloakUser;
140
+ user: AcademeUser | null;
141
+ refreshUserData: () => Promise<void>;
62
142
  hasRealmRole: (role: string) => boolean;
63
143
  hasSchool: (schoolId: string) => boolean;
64
144
  hasClientRole: (role: string, resource?: string) => boolean;
@@ -74,5 +154,5 @@ declare const useAcademeAuth: () => SecurityContextType;
74
154
 
75
155
  declare function cn(...inputs: ClassValue[]): string;
76
156
 
77
- export { AcademeAuthProvider, Button, ProtectedApp, ProtectedComponent, ProtectedRouter, Spinner, cn, useAcademeAuth };
78
- export type { AcademeKeycloakContextProps, ButtonProps, KeycloakUser, SecurityContextType, SecurityProviderProps };
157
+ export { AcademeAuthProvider, Button, ProtectedApp, ProtectedComponent, ProtectedRouter, Spinner, cn, createUserService, useAcademeAuth };
158
+ export type { AcademeKeycloakContextProps, AcademeUser, ButtonProps, KeycloakUser, MongoDBUser, SecurityContextType, SecurityProviderProps, UserServiceConfig };
package/dist/index.esm.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
- import React__default, { createContext, useContext, useState, useEffect, forwardRef, createElement } from 'react';
3
+ import React__default, { createContext, useContext, useState, useEffect, useCallback, forwardRef, createElement } from 'react';
4
+ import axios from 'axios';
4
5
 
5
6
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
6
7
 
@@ -2230,66 +2231,250 @@ function isObject(input) {
2230
2231
  return typeof input === 'object' && input !== null;
2231
2232
  }
2232
2233
 
2233
- const AcademeAuthProvider = ({ realm, hubUrl, children, clientId, keycloakUrl, }) => {
2234
- return (jsx(ReactKeycloakProvider, { authClient: new Keycloak({ clientId, realm, url: keycloakUrl }), children: jsx(SecurityProvider, { hubUrl: hubUrl, children: children }) }));
2234
+ /**
2235
+ * User Service - Fetches complete user data from MongoDB
2236
+ * This service interacts with the new unified users collection
2237
+ */
2238
+ class UserService {
2239
+ constructor(config) {
2240
+ this.cachedUser = null;
2241
+ this.cacheExpiry = 0;
2242
+ this.getAccessToken = config.getAccessToken;
2243
+ this.client = axios.create({
2244
+ baseURL: config.apiBaseUrl,
2245
+ headers: {
2246
+ 'Content-Type': 'application/json'
2247
+ }
2248
+ });
2249
+ // Add auth interceptor
2250
+ this.client.interceptors.request.use((config) => {
2251
+ const token = this.getAccessToken();
2252
+ if (token) {
2253
+ config.headers.Authorization = `Bearer ${token}`;
2254
+ }
2255
+ return config;
2256
+ });
2257
+ // Add response interceptor for error handling
2258
+ this.client.interceptors.response.use((response) => response, (error) => {
2259
+ if (error.response?.status === 401) {
2260
+ // Clear cache on authentication error
2261
+ this.clearCache();
2262
+ }
2263
+ return Promise.reject(error);
2264
+ });
2265
+ }
2266
+ /**
2267
+ * Get current user data from MongoDB
2268
+ * Includes caching to reduce API calls
2269
+ */
2270
+ async getCurrentUser(forceRefresh = false) {
2271
+ try {
2272
+ // Check cache
2273
+ if (!forceRefresh && this.cachedUser && Date.now() < this.cacheExpiry) {
2274
+ return this.cachedUser;
2275
+ }
2276
+ const response = await this.client.get('/api/users/me');
2277
+ if (response.data) {
2278
+ // Cache for 5 minutes
2279
+ this.cachedUser = response.data;
2280
+ this.cacheExpiry = Date.now() + (5 * 60 * 1000);
2281
+ return response.data;
2282
+ }
2283
+ return null;
2284
+ }
2285
+ catch (error) {
2286
+ console.error('Error fetching current user:', error);
2287
+ // If it's a 404, user doesn't exist in MongoDB yet
2288
+ if (axios.isAxiosError(error) && error.response?.status === 404) {
2289
+ // User will be auto-created on next API call
2290
+ return null;
2291
+ }
2292
+ throw error;
2293
+ }
2294
+ }
2295
+ /**
2296
+ * Update current user data
2297
+ */
2298
+ async updateCurrentUser(data) {
2299
+ try {
2300
+ // First get current user to get the ID
2301
+ const currentUser = await this.getCurrentUser();
2302
+ if (!currentUser) {
2303
+ throw new Error('User not found');
2304
+ }
2305
+ const response = await this.client.put(`/api/users/${currentUser.id}`, data);
2306
+ if (response.data) {
2307
+ // Update cache
2308
+ this.cachedUser = response.data;
2309
+ this.cacheExpiry = Date.now() + (5 * 60 * 1000);
2310
+ return response.data;
2311
+ }
2312
+ return null;
2313
+ }
2314
+ catch (error) {
2315
+ console.error('Error updating user:', error);
2316
+ throw error;
2317
+ }
2318
+ }
2319
+ /**
2320
+ * Clear cached user data
2321
+ */
2322
+ clearCache() {
2323
+ this.cachedUser = null;
2324
+ this.cacheExpiry = 0;
2325
+ }
2326
+ /**
2327
+ * Check if user has a specific role
2328
+ */
2329
+ hasRole(user, role) {
2330
+ if (!user || !user.roles)
2331
+ return false;
2332
+ return user.roles.includes(role);
2333
+ }
2334
+ /**
2335
+ * Check if user has access to a specific school
2336
+ */
2337
+ hasSchoolAccess(user, schoolId) {
2338
+ if (!user)
2339
+ return false;
2340
+ // Admin has access to all schools
2341
+ if (this.hasRole(user, 'admin_academe'))
2342
+ return true;
2343
+ // Check if user's school matches
2344
+ return user.school_id === schoolId;
2345
+ }
2346
+ /**
2347
+ * Check if user is a student (has student-specific data)
2348
+ */
2349
+ isStudent(user) {
2350
+ if (!user)
2351
+ return false;
2352
+ return !!user.registration_number || this.hasRole(user, 'student');
2353
+ }
2354
+ /**
2355
+ * Check if user has completed onboarding
2356
+ */
2357
+ hasCompletedOnboarding(user) {
2358
+ if (!user)
2359
+ return false;
2360
+ return user.onboarding_completo;
2361
+ }
2362
+ /**
2363
+ * Check if user has access enabled
2364
+ */
2365
+ hasAccessEnabled(user) {
2366
+ if (!user)
2367
+ return false;
2368
+ return user.acesso_liberado && user.active;
2369
+ }
2370
+ }
2371
+ // Export singleton instance creator
2372
+ const createUserService = (config) => {
2373
+ return new UserService(config);
2374
+ };
2375
+
2376
+ const AcademeAuthProvider = ({ realm, hubUrl, children, clientId, keycloakUrl, apiBaseUrl, }) => {
2377
+ return (jsx(ReactKeycloakProvider, { authClient: new Keycloak({ clientId, realm, url: keycloakUrl }), children: jsx(SecurityProvider, { hubUrl: hubUrl, apiBaseUrl: apiBaseUrl, children: children }) }));
2235
2378
  };
2236
2379
  const SecurityContext = createContext({
2237
2380
  isInitialized: false,
2238
- getUser: () => ({
2239
- email: "",
2240
- name: "",
2241
- lastName: "",
2242
- attributes: {
2243
- school_ids: [],
2244
- },
2245
- }),
2381
+ user: null,
2382
+ refreshUserData: async () => { },
2246
2383
  signOut: () => null,
2247
2384
  goToLogin: () => null,
2248
2385
  hasSchool: () => false,
2249
2386
  hasRealmRole: () => false,
2250
- redirectToHub: () => null,
2251
2387
  hasClientRole: () => false,
2252
2388
  isAuthenticated: () => false,
2253
2389
  });
2254
- const SecurityProvider = ({ hubUrl, children, }) => {
2390
+ const SecurityProvider = ({ apiBaseUrl = "https://api.academe.com.br", children, }) => {
2255
2391
  const [isInitialized, setIsInitialized] = useState(false);
2392
+ const [currentUser, setCurrentUser] = useState(null);
2256
2393
  const { initialized, keycloak } = useKeycloak();
2394
+ const userService = createUserService({
2395
+ apiBaseUrl,
2396
+ getAccessToken: () => keycloak?.token || null,
2397
+ });
2257
2398
  useEffect(() => {
2258
2399
  setIsInitialized(initialized);
2259
2400
  }, [initialized]);
2260
2401
  useEffect(() => {
2261
2402
  window.accessToken = keycloak.token;
2262
2403
  }, [keycloak.token]);
2263
- const getUser = () => {
2404
+ // Fetch user data from MongoDB when authenticated
2405
+ useEffect(() => {
2406
+ const fetchUserData = async () => {
2407
+ if (initialized && keycloak?.authenticated && keycloak?.token) {
2408
+ try {
2409
+ const mongoUser = await userService.getCurrentUser();
2410
+ if (mongoUser) {
2411
+ const academeUser = {
2412
+ ...mongoUser,
2413
+ keycloakUser: getKeycloakUser(),
2414
+ };
2415
+ setCurrentUser(academeUser);
2416
+ }
2417
+ }
2418
+ catch (error) {
2419
+ console.error("Error fetching user data:", error);
2420
+ }
2421
+ }
2422
+ else if (!keycloak?.authenticated) {
2423
+ // Clear user data when not authenticated
2424
+ setCurrentUser(null);
2425
+ userService.clearCache();
2426
+ }
2427
+ };
2428
+ fetchUserData();
2429
+ }, [initialized, keycloak?.authenticated, keycloak?.token]);
2430
+ const getKeycloakUser = () => {
2264
2431
  const idTokenParsed = keycloak?.idTokenParsed || {};
2265
2432
  return {
2266
- email: keycloak?.idTokenParsed?.email || "",
2267
- name: keycloak?.idTokenParsed?.given_name || "",
2268
- lastName: keycloak?.idTokenParsed?.family_name || "",
2269
- attributes: {
2270
- school_ids: (idTokenParsed.school_ids || []),
2271
- },
2433
+ email: idTokenParsed?.email || "",
2434
+ name: idTokenParsed?.given_name || "",
2435
+ lastName: idTokenParsed?.family_name || "",
2272
2436
  };
2273
2437
  };
2274
- const redirectToHub = (errorMessage) => {
2275
- window.location.replace(`${hubUrl}?errorMessage=${errorMessage}`);
2276
- };
2438
+ const refreshUserData = useCallback(async () => {
2439
+ if (keycloak?.authenticated) {
2440
+ try {
2441
+ const mongoUser = await userService.getCurrentUser(true); // Force refresh
2442
+ if (mongoUser) {
2443
+ const academeUser = {
2444
+ ...mongoUser,
2445
+ keycloakUser: getKeycloakUser(),
2446
+ };
2447
+ setCurrentUser(academeUser);
2448
+ }
2449
+ }
2450
+ catch (error) {
2451
+ console.error("Error refreshing user data:", error);
2452
+ }
2453
+ }
2454
+ }, [keycloak?.authenticated]);
2277
2455
  const signOut = () => {
2456
+ userService.clearCache();
2457
+ setCurrentUser(null);
2278
2458
  keycloak?.logout();
2279
2459
  };
2280
2460
  const isAuthenticated = () => {
2281
2461
  return (!!keycloak?.idTokenParsed?.email?.length && !!keycloak?.authenticated);
2282
2462
  };
2283
2463
  const hasSchool = (schoolId) => {
2284
- return getUser().attributes.school_ids.includes(schoolId);
2464
+ if (currentUser?.school_id) {
2465
+ return (currentUser.school_id === schoolId ||
2466
+ userService.hasRole(currentUser, "admin_academe"));
2467
+ }
2468
+ // Fallback to Keycloak data
2469
+ return false; // No school_id in Keycloak data
2285
2470
  };
2286
2471
  return (jsx(SecurityContext.Provider, { value: {
2287
2472
  isInitialized,
2288
- getUser,
2473
+ user: currentUser,
2474
+ refreshUserData,
2289
2475
  signOut,
2290
- redirectToHub,
2291
2476
  isAuthenticated,
2292
- hasSchool: hasSchool,
2477
+ hasSchool,
2293
2478
  goToLogin: keycloak.login,
2294
2479
  hasRealmRole: keycloak?.hasRealmRole,
2295
2480
  hasClientRole: keycloak.hasResourceRole,
@@ -5380,8 +5565,8 @@ function Spinner({ className, ...props }) {
5380
5565
  return (jsx(LoaderCircle, { role: "status", "aria-label": "Loading", className: cn("size-10 animate-spin text-violet-500", className), ...props }));
5381
5566
  }
5382
5567
 
5383
- const ProtectedApp = ({ children, unauthorizedMessage, requiredClientRoles, }) => {
5384
- const { isInitialized, goToLogin, hasClientRole, redirectToHub, isAuthenticated, } = useAcademeAuth();
5568
+ const ProtectedApp = ({ children, requiredClientRoles, }) => {
5569
+ const { isInitialized, goToLogin, hasClientRole, isAuthenticated } = useAcademeAuth();
5385
5570
  if (!isInitialized) {
5386
5571
  return (jsx("div", { className: "flex w-screen h-screen items-center justify-center", children: jsx(Spinner, { className: "size-10 text-violet-500" }) }));
5387
5572
  }
@@ -5391,7 +5576,6 @@ const ProtectedApp = ({ children, unauthorizedMessage, requiredClientRoles, }) =
5391
5576
  }
5392
5577
  if (requiredClientRoles &&
5393
5578
  !requiredClientRoles?.some((role) => hasClientRole(role))) {
5394
- redirectToHub(unauthorizedMessage || "Unauthorized");
5395
5579
  return jsx(Fragment, {});
5396
5580
  }
5397
5581
  return children;
@@ -5452,5 +5636,5 @@ styleInject(css_248z$1,{"insertAt":"top"});
5452
5636
  var css_248z = "*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.18 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:\"\"}:host,html{-webkit-text-size-adjust:100%;font-feature-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-feature-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}.flex{display:flex}.size-10{height:2.5rem;width:2.5rem}.h-screen{height:100vh}.w-screen{width:100vw}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.items-center{align-items:center}.justify-center{justify-content:center}.rounded-lg{border-radius:var(--radius)}.border-2{border-width:2px}.border-blue-600{--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.bg-transparent{background-color:transparent}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1\\.5{padding-bottom:.375rem;padding-top:.375rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.font-semibold{font-weight:600}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-violet-500{--tw-text-opacity:1;color:rgb(139 92 246/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.outline{outline-style:solid}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0) scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1)) rotate(var(--tw-enter-rotate,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0) scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1)) rotate(var(--tw-exit-rotate,0))}}.duration-200{animation-duration:.2s}.hover\\:bg-blue-50:hover{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.hover\\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.focus\\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity,1))}.focus\\:ring-gray-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(107 114 128/var(--tw-ring-opacity,1))}.focus\\:ring-offset-2:focus{--tw-ring-offset-width:2px}";
5453
5637
  styleInject(css_248z,{"insertAt":"top"});
5454
5638
 
5455
- export { AcademeAuthProvider, Button, ProtectedApp, ProtectedComponent, ProtectedRouter, Spinner, cn, useAcademeAuth };
5639
+ export { AcademeAuthProvider, Button, ProtectedApp, ProtectedComponent, ProtectedRouter, Spinner, cn, createUserService, useAcademeAuth };
5456
5640
  //# sourceMappingURL=index.esm.js.map