kmod-cli 1.0.10

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.
Files changed (66) hide show
  1. package/README.md +53 -0
  2. package/bin/gen-components.js +68 -0
  3. package/bin/index.js +153 -0
  4. package/component-templates/components/access-denied.tsx +130 -0
  5. package/component-templates/components/breadcumb.tsx +42 -0
  6. package/component-templates/components/count-down.tsx +94 -0
  7. package/component-templates/components/count-input.tsx +221 -0
  8. package/component-templates/components/date-range-calendar/button.tsx +61 -0
  9. package/component-templates/components/date-range-calendar/calendar.tsx +132 -0
  10. package/component-templates/components/date-range-calendar/date-input.tsx +259 -0
  11. package/component-templates/components/date-range-calendar/date-range-picker.tsx +594 -0
  12. package/component-templates/components/date-range-calendar/label.tsx +31 -0
  13. package/component-templates/components/date-range-calendar/popover.tsx +32 -0
  14. package/component-templates/components/date-range-calendar/select.tsx +125 -0
  15. package/component-templates/components/date-range-calendar/switch.tsx +30 -0
  16. package/component-templates/components/datetime-picker/button.tsx +61 -0
  17. package/component-templates/components/datetime-picker/calendar.tsx +156 -0
  18. package/component-templates/components/datetime-picker/datetime-picker.tsx +75 -0
  19. package/component-templates/components/datetime-picker/input.tsx +20 -0
  20. package/component-templates/components/datetime-picker/label.tsx +18 -0
  21. package/component-templates/components/datetime-picker/period-input.tsx +62 -0
  22. package/component-templates/components/datetime-picker/popover.tsx +32 -0
  23. package/component-templates/components/datetime-picker/select.tsx +125 -0
  24. package/component-templates/components/datetime-picker/time-picker-input.tsx +131 -0
  25. package/component-templates/components/datetime-picker/time-picker-utils.tsx +204 -0
  26. package/component-templates/components/datetime-picker/time-picker.tsx +59 -0
  27. package/component-templates/components/gradient-outline.tsx +233 -0
  28. package/component-templates/components/gradient-svg.tsx +157 -0
  29. package/component-templates/components/grid-layout.tsx +69 -0
  30. package/component-templates/components/hydrate-guard.tsx +40 -0
  31. package/component-templates/components/image.tsx +92 -0
  32. package/component-templates/components/loader-slash-gradient.tsx +85 -0
  33. package/component-templates/components/masonry-gallery.tsx +221 -0
  34. package/component-templates/components/modal.tsx +110 -0
  35. package/component-templates/components/multi-select.tsx +447 -0
  36. package/component-templates/components/non-hydration.tsx +27 -0
  37. package/component-templates/components/portal.tsx +34 -0
  38. package/component-templates/components/segments-circle.tsx +235 -0
  39. package/component-templates/components/single-select.tsx +248 -0
  40. package/component-templates/components/stroke-circle.tsx +57 -0
  41. package/component-templates/components/table/column-table.tsx +15 -0
  42. package/component-templates/components/table/data-table.tsx +339 -0
  43. package/component-templates/components/table/readme.tsx +95 -0
  44. package/component-templates/components/table/table.tsx +60 -0
  45. package/component-templates/components/text-hover-effect.tsx +120 -0
  46. package/component-templates/components/timout-loader.tsx +52 -0
  47. package/component-templates/components/toast.tsx +994 -0
  48. package/component-templates/configs/config.ts +33 -0
  49. package/component-templates/configs/feature-config.tsx +432 -0
  50. package/component-templates/configs/keys.ts +7 -0
  51. package/component-templates/core/api-service.ts +202 -0
  52. package/component-templates/core/calculate.ts +18 -0
  53. package/component-templates/core/idb.ts +166 -0
  54. package/component-templates/core/storage.ts +213 -0
  55. package/component-templates/hooks/count-down.ts +38 -0
  56. package/component-templates/hooks/fade-on-scroll.ts +52 -0
  57. package/component-templates/hooks/safe-action.ts +59 -0
  58. package/component-templates/hooks/spam-guard.ts +31 -0
  59. package/component-templates/lib/utils.ts +6 -0
  60. package/component-templates/providers/feature-guard.tsx +432 -0
  61. package/component-templates/queries/query.tsx +775 -0
  62. package/component-templates/utils/colors/color-by-text.ts +307 -0
  63. package/component-templates/utils/colors/stripe-effect.ts +100 -0
  64. package/component-templates/utils/hash/hash-aes.ts +35 -0
  65. package/components.json +348 -0
  66. package/package.json +60 -0
@@ -0,0 +1,775 @@
1
+ // =============================================================================
2
+ // REACT CONTEXTS AND HOOKS
3
+ // =============================================================================
4
+ import React, {
5
+ createContext,
6
+ useContext,
7
+ useEffect,
8
+ useMemo,
9
+ useState,
10
+ } from 'react';
11
+
12
+ import { ApiServices } from '../core/api-service';
13
+ import { Storage } from '../core/storage';
14
+
15
+ // types/index.ts
16
+ export type ResourceConfig = {
17
+ name: string;
18
+ route?: string;
19
+ canAccess?: (options?: AccessOptions) => boolean | Promise<boolean>;
20
+ meta?: Record<string, any>;
21
+ };
22
+
23
+ export type AccessOptions = {
24
+ action?: string;
25
+ params?: any;
26
+ };
27
+
28
+ export type Resources = ResourceConfig[];
29
+
30
+ export type Pagination = {
31
+ current?: number;
32
+ pageSize?: number;
33
+ total?: number;
34
+ };
35
+
36
+ /**
37
+ * @param operator eq | ne | lt | gt | lte | gte | contains | in | nin
38
+ * @description eq: equal, ne: not equal, lt: less than, gt: greater than, lte: less than or equal, gte: greater than or equal, contains: contains, in: in, nin: not in
39
+ */
40
+ export type Filter = {
41
+ field: string;
42
+ operator?: "eq" | "ne" | "lt" | "gt" | "lte" | "gte" | "contains" | "in" | "nin";
43
+ value: any;
44
+ };
45
+
46
+ export type Sorter = {
47
+ field: string;
48
+ order?: "asc" | "desc";
49
+ };
50
+
51
+ export type GetListParams = {
52
+ resource: string;
53
+ pagination?: Pagination;
54
+ filters?: Filter[];
55
+ sorters?: Sorter[];
56
+ meta?: any;
57
+ };
58
+
59
+ export type GetOneParams = {
60
+ resource: string;
61
+ id: string | number;
62
+ meta?: any;
63
+ };
64
+
65
+ export type CreateParams<T = any> = {
66
+ resource: string;
67
+ variables: T;
68
+ meta?: any;
69
+ };
70
+
71
+ export type UpdateParams<T = any> = {
72
+ resource: string;
73
+ id: string | number;
74
+ variables: Partial<T>;
75
+ meta?: any;
76
+ };
77
+
78
+ export type DeleteParams = {
79
+ resource: string;
80
+ id: string | number;
81
+ meta?: any;
82
+ };
83
+
84
+ export type CustomParams<T = any> = {
85
+ url: string;
86
+ method?: "get" | "post" | "put" | "delete" | "patch";
87
+ data?: any;
88
+ config?: any;
89
+ meta?: any;
90
+ };
91
+
92
+ // User and Auth types
93
+ export type User = {
94
+ id?: string | number;
95
+ name?: string;
96
+ email?: string;
97
+ role?: string | string[];
98
+ permissions?: string[];
99
+ [key: string]: any;
100
+ };
101
+
102
+ export type LoginCredentials = Record<string, any>;
103
+
104
+ export type AuthResponse = {
105
+ token?: string;
106
+ user?: User;
107
+ refreshToken?: string;
108
+ expiresIn?: number;
109
+ };
110
+
111
+ // Data Provider Interface
112
+ export interface IDataProvider {
113
+ getList<T = any>(params: GetListParams): Promise<{ data: T[]; total: number }>;
114
+ getOne<T = any>(params: GetOneParams): Promise<T>;
115
+ create<T = any>(params: CreateParams<T>): Promise<T>;
116
+ update<T = any>(params: UpdateParams<T>): Promise<T>;
117
+ deleteOne<T = any>(params: DeleteParams): Promise<T>;
118
+ custom<T = any>(params: CustomParams<T>): Promise<T>;
119
+ }
120
+
121
+ // Auth Provider Interface
122
+ export interface IAuthProvider {
123
+ login(credentials: LoginCredentials): Promise<User | null>;
124
+ logout(): Promise<void>;
125
+ checkAuth(): Promise<boolean>;
126
+ getUser(): User | null;
127
+ getPermissions(): Promise<string[]>;
128
+ refreshToken?(): Promise<boolean>;
129
+ }
130
+
131
+ // Access Control Interface
132
+ export interface IAccessControlProvider {
133
+ can(resource: string, action?: string, params?: any): Promise<{ can: boolean; reason?: string }>;
134
+ getRole(): Promise<string | string[] | null>;
135
+ }
136
+
137
+ // =============================================================================
138
+ // DATA PROVIDER IMPLEMENTATION
139
+ // =============================================================================
140
+
141
+ export class DataProvider implements IDataProvider {
142
+ constructor(private apiService: ApiServices) {}
143
+
144
+ private buildQueryParams(params: GetListParams): Record<string, any> {
145
+ const query: Record<string, any> = {};
146
+
147
+ // Pagination
148
+ if (params.pagination) {
149
+ query.page = params.pagination.current ?? 1;
150
+ query.pageSize = params.pagination.pageSize ?? 10;
151
+ }
152
+
153
+ // Filters
154
+ if (params.filters?.length) {
155
+ params.filters.forEach((filter) => {
156
+ const op = filter.operator ?? "eq";
157
+ query[`filter[${filter.field}][${op}]`] = filter.value;
158
+ });
159
+ }
160
+
161
+ // Sorters
162
+ if (params.sorters?.length) {
163
+ query.sort = params.sorters
164
+ .map((s) => `${s.order === "desc" ? "-" : ""}${s.field}`)
165
+ .join(",");
166
+ }
167
+
168
+ // Meta
169
+ if (params.meta) {
170
+ Object.assign(query, params.meta);
171
+ }
172
+
173
+ return query;
174
+ }
175
+
176
+ private resolveRoute(resource: string): string {
177
+ return resource.startsWith("/") ? resource : `/${resource}`;
178
+ }
179
+
180
+ async getList<T = any>(params: GetListParams): Promise<{ data: T[]; total: number }> {
181
+ const route = this.resolveRoute(params.resource);
182
+ const query = this.buildQueryParams(params);
183
+
184
+ const response = await this.apiService.get<{ data: T[]; total: number }>(route, { params: query });
185
+ return response.data;
186
+ }
187
+
188
+ async getOne<T = any>(params: GetOneParams): Promise<T> {
189
+ const route = this.resolveRoute(params.resource);
190
+ const response = await this.apiService.get<T>(`${route}/${params.id}`);
191
+ return response.data;
192
+ }
193
+
194
+ async create<T = any>(params: CreateParams<T>): Promise<T> {
195
+ const route = this.resolveRoute(params.resource);
196
+ const response = await this.apiService.post<T>(route, params.variables);
197
+ return response.data;
198
+ }
199
+
200
+ async update<T = any>(params: UpdateParams<T>): Promise<T> {
201
+ const route = this.resolveRoute(params.resource);
202
+ const response = await this.apiService.put<T>(`${route}/${params.id}`, params.variables);
203
+ return response.data;
204
+ }
205
+
206
+ async deleteOne<T = any>(params: DeleteParams): Promise<T> {
207
+ const route = this.resolveRoute(params.resource);
208
+ const response = await this.apiService.delete<T>(`${route}/${params.id}`);
209
+ return response.data;
210
+ }
211
+
212
+ async custom<T = any>(params: CustomParams<T>): Promise<T> {
213
+ const method = params.method || "get";
214
+ let response;
215
+ switch (method) {
216
+ case "get":
217
+ response = await this.apiService.get<T>(params.url, params.config);
218
+ break;
219
+ case "post":
220
+ response = await this.apiService.post<T>(params.url, params.data, params.config);
221
+ break;
222
+ case "put":
223
+ response = await this.apiService.put<T>(params.url, params.data, params.config);
224
+ break;
225
+ case "delete":
226
+ response = await this.apiService.delete<T>(params.url, params.config);
227
+ break;
228
+ case "patch":
229
+ response = await this.apiService.patch<T>(params.url, params.data, params.config);
230
+ break;
231
+ default:
232
+ throw new Error(`Unsupported method: ${method}`);
233
+ }
234
+ return response.data;
235
+ }
236
+ }
237
+
238
+ // =============================================================================
239
+ // AUTH PROVIDER IMPLEMENTATION
240
+ // =============================================================================
241
+
242
+ export interface AuthProviderOptions {
243
+ apiService?: any;
244
+ keyOn?: "cookie" | "localstorage" | "sessionstorage";
245
+ tokenKey?: string;
246
+ refreshTokenKey?: string;
247
+ endpoints?: {
248
+ login?: string;
249
+ logout?: string;
250
+ me?: string;
251
+ permissions?: string;
252
+ refresh?: string;
253
+ };
254
+ }
255
+
256
+ export class AuthProvider implements IAuthProvider {
257
+ private user: User | null = null;
258
+ private keyOn: "cookie" | "localstorage" | "sessionstorage";
259
+ private tokenKey: string;
260
+ private refreshTokenKey: string;
261
+ private endpoints: Required<NonNullable<AuthProviderOptions["endpoints"]>>;
262
+
263
+ constructor(
264
+ private apiService: ApiServices,
265
+ private options: AuthProviderOptions = {}
266
+ ) {
267
+ this.keyOn = options.keyOn || "cookie";
268
+ this.tokenKey = options.tokenKey || "token";
269
+ this.refreshTokenKey = options.refreshTokenKey || "refreshToken";
270
+ this.endpoints = {
271
+ login: "/auth/login",
272
+ logout: "/auth/logout",
273
+ me: "/auth/me",
274
+ permissions: "/auth/permissions",
275
+ refresh: "/auth/refresh",
276
+ ...options.endpoints,
277
+ };
278
+ }
279
+
280
+ async login(credentials: LoginCredentials): Promise<User | null> {
281
+ try {
282
+ const response = await this.apiService.post<AuthResponse>(this.endpoints.login, credentials);
283
+ const { token, user, refreshToken } = response.data;
284
+
285
+ if (token) {
286
+ this.setToken(token);
287
+ }
288
+
289
+ if (refreshToken) {
290
+ this.setRefreshToken(refreshToken);
291
+ }
292
+
293
+ if (user) {
294
+ this.user = user;
295
+ return user;
296
+ }
297
+
298
+ // Fallback: try to get user info
299
+ const success = await this.checkAuth();
300
+ return success ? this.user : null;
301
+ } catch (error) {
302
+ throw error;
303
+ }
304
+ }
305
+
306
+ async logout(): Promise<void> {
307
+ try {
308
+ await this.apiService.post(this.endpoints.logout, {}).catch(() => {});
309
+ } finally {
310
+ this.clearTokens();
311
+ this.user = null;
312
+ }
313
+ }
314
+
315
+ async checkAuth(): Promise<boolean> {
316
+ try {
317
+ const response = await this.apiService.get<User>(this.endpoints.me);
318
+ this.user = response.data;
319
+ return true;
320
+ } catch (error) {
321
+ this.user = null;
322
+ return false;
323
+ }
324
+ }
325
+
326
+ getUser(): User | null {
327
+ return this.user;
328
+ }
329
+
330
+ async getPermissions(): Promise<string[]> {
331
+ try {
332
+ const response = await this.apiService.get<string[]>(this.endpoints.permissions);
333
+ if (Array.isArray(response.data)) {
334
+ return response.data;
335
+ }
336
+ } catch (error) {
337
+ // Fallback to user role
338
+ }
339
+
340
+ // Fallback: extract from user
341
+ if (!this.user?.role) return [];
342
+ if (Array.isArray(this.user.role)) return this.user.role.map(String);
343
+ if (typeof this.user.role === "string") return [this.user.role];
344
+ if (this.user.permissions) return this.user.permissions;
345
+
346
+ return [];
347
+ }
348
+
349
+ async refreshToken(): Promise<boolean> {
350
+ try {
351
+ const refreshToken = this.getRefreshToken();
352
+ if (!refreshToken) return false;
353
+
354
+ const response = await this.apiService.post<AuthResponse>(this.endpoints.refresh, {
355
+ refreshToken,
356
+ });
357
+
358
+ const { token, user } = response.data;
359
+ if (token) {
360
+ this.setToken(token);
361
+ }
362
+ if (user) {
363
+ this.user = user;
364
+ }
365
+
366
+ return true;
367
+ } catch (error) {
368
+ this.clearTokens();
369
+ this.user = null;
370
+ return false;
371
+ }
372
+ }
373
+
374
+ private setToken(token: string): void {
375
+ if (typeof window !== "undefined" && this.keyOn === "cookie") {
376
+ Storage.setCookie({ name: this.tokenKey, value: token, days: "infinity" });
377
+ }
378
+ if (typeof window !== "undefined" && this.keyOn === "localstorage") {
379
+ Storage.setItem({ key: this.tokenKey, value: token });
380
+ }
381
+ if (typeof window !== "undefined" && this.keyOn === "sessionstorage") {
382
+ Storage.setSessionItem({ key: this.tokenKey, value: token });
383
+ }
384
+ }
385
+
386
+ private getToken(): string | null {
387
+ if (typeof window !== "undefined" && this.keyOn === "cookie") {
388
+ return Storage.getCookie(this.tokenKey) as any;
389
+ }
390
+ if (typeof window !== "undefined" && this.keyOn === "localstorage") {
391
+ return Storage.getItem({ key: this.tokenKey, expiredCheck: false });
392
+ }
393
+ if (typeof window !== "undefined" && this.keyOn === "sessionstorage") {
394
+ return Storage.getSessionItem({ key: this.tokenKey, expiredCheck: false });
395
+ }
396
+ return null;
397
+ }
398
+
399
+ private setRefreshToken(token: string): void {
400
+ if (typeof window !== "undefined" && this.keyOn === "localstorage") {
401
+ Storage.setItem({ key: this.refreshTokenKey, value: token });
402
+ }
403
+ if (typeof window !== "undefined" && this.keyOn === "sessionstorage") {
404
+ Storage.setSessionItem({ key: this.refreshTokenKey, value: token });
405
+ }
406
+ if(typeof window !== "undefined" && this.keyOn === "cookie") {
407
+ Storage.setCookie({ name: this.refreshTokenKey, value: token, days: "infinity" });
408
+ }
409
+ }
410
+
411
+ private getRefreshToken(): string | null {
412
+ if (typeof window !== "undefined" && this.keyOn === "cookie") {
413
+ return Storage.getCookie(this.refreshTokenKey) as any;
414
+ }
415
+ if (typeof window !== "undefined" && this.keyOn === "localstorage") {
416
+ return Storage.getItem({ key: this.refreshTokenKey, expiredCheck: false });
417
+ }
418
+ if (typeof window !== "undefined" && this.keyOn === "sessionstorage") {
419
+ return Storage.getSessionItem({ key: this.refreshTokenKey, expiredCheck: false });
420
+ }
421
+ return null;
422
+ }
423
+
424
+ private clearTokens(): void {
425
+ if (typeof window !== "undefined" && this.keyOn === "cookie") {
426
+ Storage.removeCookie(this.tokenKey);
427
+ Storage.removeCookie(this.refreshTokenKey);
428
+ }
429
+ if (typeof window !== "undefined" && this.keyOn === "localstorage") {
430
+ Storage.removeItem(this.tokenKey);
431
+ Storage.removeItem(this.refreshTokenKey);
432
+ }
433
+ if (typeof window !== "undefined" && this.keyOn === "sessionstorage") {
434
+ Storage.removeSessionItem(this.tokenKey);
435
+ Storage.removeSessionItem(this.refreshTokenKey);
436
+ }
437
+ }
438
+ }
439
+
440
+ // =============================================================================
441
+ // ACCESS CONTROL PROVIDER
442
+ // =============================================================================
443
+
444
+ export class AccessControlProvider implements IAccessControlProvider {
445
+ constructor(
446
+ private authProvider: IAuthProvider,
447
+ private resources: Resources = []
448
+ ) {}
449
+
450
+ async can(
451
+ resource: string,
452
+ action?: string,
453
+ params?: any
454
+ ): Promise<{ can: boolean; reason?: string }> {
455
+ // 1. Check resource-specific access control
456
+ const resourceConfig = this.resources.find((r) => r.name === resource);
457
+ if (resourceConfig?.canAccess) {
458
+ try {
459
+ const result = await Promise.resolve(
460
+ resourceConfig.canAccess({ action, params })
461
+ );
462
+ return { can: Boolean(result) };
463
+ } catch (error) {
464
+ return { can: false, reason: "Resource access check failed" };
465
+ }
466
+ }
467
+
468
+ // 2. Check global permissions
469
+ try {
470
+ const permissions = await this.authProvider.getPermissions();
471
+
472
+ // Admin has access to everything
473
+ if (permissions.includes("admin") || permissions.includes("*")) {
474
+ return { can: true };
475
+ }
476
+
477
+ // Check specific permission
478
+ const permissionKey = action ? `${resource}:${action}` : resource;
479
+ const hasPermission = permissions.includes(permissionKey);
480
+
481
+ return {
482
+ can: hasPermission,
483
+ reason: hasPermission ? undefined : "Insufficient permissions",
484
+ };
485
+ } catch (error) {
486
+ return { can: false, reason: "Permission check failed" };
487
+ }
488
+ }
489
+
490
+ async getRole(): Promise<string | string[] | null> {
491
+ const user = this.authProvider.getUser();
492
+ return user?.role || null;
493
+ }
494
+ }
495
+
496
+ // Core Context
497
+ type CoreContextType = {
498
+ resources: Resources;
499
+ dataProvider: IDataProvider;
500
+ authProvider: IAuthProvider;
501
+ accessControl: IAccessControlProvider;
502
+ };
503
+
504
+ const CoreContext = createContext<CoreContextType | null>(null);
505
+
506
+ export function useCore() {
507
+ const context = useContext(CoreContext);
508
+ if (!context) {
509
+ throw new Error("useCore must be used within a CoreProvider");
510
+ }
511
+ return context;
512
+ }
513
+
514
+ // Auth Context for React state management
515
+ type AuthContextType = {
516
+ user: User | null;
517
+ loading: boolean;
518
+ login: (credentials: LoginCredentials) => Promise<User | null>;
519
+ logout: () => Promise<void>;
520
+ checkAuth: () => Promise<boolean>;
521
+ getPermissions: () => Promise<string[]>;
522
+ };
523
+
524
+ const AuthContext = createContext<AuthContextType | null>(null);
525
+
526
+ export function useAuth() {
527
+ const context = useContext(AuthContext);
528
+ if (!context) {
529
+ throw new Error("useAuth must be used within an AuthProvider wrapper");
530
+ }
531
+ return context;
532
+ }
533
+
534
+ // =============================================================================
535
+ // PROVIDER COMPONENTS
536
+ // =============================================================================
537
+
538
+ export interface CoreProviderProps {
539
+ children: React.ReactNode;
540
+ resources?: Resources;
541
+ dataProvider?: IDataProvider;
542
+ authProvider?: IAuthProvider;
543
+ accessControl?: IAccessControlProvider;
544
+ apiService?: any;
545
+ }
546
+
547
+ export function CoreProvider({
548
+ children,
549
+ resources = [],
550
+ dataProvider,
551
+ authProvider,
552
+ accessControl,
553
+ apiService,
554
+ }: CoreProviderProps) {
555
+ const providers = useMemo(() => {
556
+ // Create default providers if not provided
557
+ const dp = dataProvider || (apiService ? new DataProvider(apiService) : null);
558
+ const ap = authProvider || (apiService ? new AuthProvider(apiService) : null);
559
+ const ac = accessControl || (ap ? new AccessControlProvider(ap, resources) : null);
560
+
561
+ if (!dp || !ap || !ac) {
562
+ throw new Error("CoreProvider requires either custom providers or apiService");
563
+ }
564
+
565
+ return { dataProvider: dp, authProvider: ap, accessControl: ac };
566
+ }, [dataProvider, authProvider, accessControl, apiService, resources]);
567
+
568
+ const contextValue: CoreContextType = {
569
+ resources,
570
+ dataProvider: providers.dataProvider,
571
+ authProvider: providers.authProvider,
572
+ accessControl: providers.accessControl,
573
+ };
574
+
575
+ return (
576
+ <CoreContext.Provider value={contextValue}>
577
+ <AuthProviderWrapper>{children}</AuthProviderWrapper>
578
+ </CoreContext.Provider>
579
+ );
580
+ }
581
+
582
+ // Internal wrapper for auth state management
583
+ function AuthProviderWrapper({ children }: { children: React.ReactNode }) {
584
+ const { authProvider } = useCore();
585
+ const [user, setUser] = useState<User | null>(null);
586
+ const [loading, setLoading] = useState(true);
587
+
588
+ useEffect(() => {
589
+ const initAuth = async () => {
590
+ try {
591
+ const isAuthenticated = await authProvider.checkAuth();
592
+ if (isAuthenticated) {
593
+ setUser(authProvider.getUser());
594
+ }
595
+ } finally {
596
+ setLoading(false);
597
+ }
598
+ };
599
+
600
+ initAuth();
601
+ }, [authProvider]);
602
+
603
+ const login = async (credentials: LoginCredentials) => {
604
+ const user = await authProvider.login(credentials);
605
+ setUser(user);
606
+ return user;
607
+ };
608
+
609
+ const logout = async () => {
610
+ await authProvider.logout();
611
+ setUser(null);
612
+ };
613
+
614
+ const checkAuth = async () => {
615
+ const result = await authProvider.checkAuth();
616
+ setUser(authProvider.getUser());
617
+ return result;
618
+ };
619
+
620
+ const getPermissions = () => authProvider.getPermissions();
621
+
622
+ const contextValue: AuthContextType = {
623
+ user,
624
+ loading,
625
+ login,
626
+ logout,
627
+ checkAuth,
628
+ getPermissions,
629
+ };
630
+
631
+ return (
632
+ <AuthContext.Provider value={contextValue}>
633
+ {children}
634
+ </AuthContext.Provider>
635
+ );
636
+ }
637
+
638
+ // =============================================================================
639
+ // CUSTOM HOOKS
640
+ // =============================================================================
641
+
642
+ export function useDataProvider() {
643
+ const { dataProvider } = useCore();
644
+ return dataProvider;
645
+ }
646
+
647
+ export function useAccessControl() {
648
+ const { accessControl } = useCore();
649
+ return accessControl;
650
+ }
651
+
652
+ // Resource-specific CRUD hooks
653
+ export function useList<T = any>(resourceName: string) {
654
+ const dataProvider = useDataProvider();
655
+ const { resources } = useCore();
656
+
657
+ return async (options?: {
658
+ pagination?: Pagination;
659
+ filters?: Filter[];
660
+ sorters?: Sorter[];
661
+ meta?: any;
662
+ }) => {
663
+ const resource = resources.find(r => r.name === resourceName);
664
+ const route = resource?.route || resourceName;
665
+
666
+ return dataProvider.getList<T>({
667
+ resource: route,
668
+ ...options,
669
+ });
670
+ };
671
+ }
672
+
673
+ export function useGetOne<T = any>(resourceName: string) {
674
+ const dataProvider = useDataProvider();
675
+ const { resources } = useCore();
676
+
677
+ return async (id: string | number, meta?: any) => {
678
+ const resource = resources.find(r => r.name === resourceName);
679
+ const route = resource?.route || resourceName;
680
+
681
+ return dataProvider.getOne<T>({
682
+ resource: route,
683
+ id,
684
+ meta,
685
+ });
686
+ };
687
+ }
688
+
689
+ export function useCreate<T = any>(resourceName: string) {
690
+ const dataProvider = useDataProvider();
691
+ const { resources } = useCore();
692
+
693
+ return async (variables: T, meta?: any) => {
694
+ const resource = resources.find(r => r.name === resourceName);
695
+ const route = resource?.route || resourceName;
696
+
697
+ return dataProvider.create<T>({
698
+ resource: route,
699
+ variables,
700
+ meta,
701
+ });
702
+ };
703
+ }
704
+
705
+ export function useUpdate<T = any>(resourceName: string) {
706
+ const dataProvider = useDataProvider();
707
+ const { resources } = useCore();
708
+
709
+ return async (id: string | number, variables: Partial<T>, meta?: any) => {
710
+ const resource = resources.find(r => r.name === resourceName);
711
+ const route = resource?.route || resourceName;
712
+
713
+ return dataProvider.update<T>({
714
+ resource: route,
715
+ id,
716
+ variables,
717
+ meta,
718
+ });
719
+ };
720
+ }
721
+
722
+ export function useDelete(resourceName: string) {
723
+ const dataProvider = useDataProvider();
724
+ const { resources } = useCore();
725
+
726
+ return async (id: string | number, meta?: any) => {
727
+ const resource = resources.find(r => r.name === resourceName);
728
+ const route = resource?.route || resourceName;
729
+
730
+ return dataProvider.deleteOne({
731
+ resource: route,
732
+ id,
733
+ meta,
734
+ });
735
+ };
736
+ }
737
+
738
+ export function useCustom() {
739
+ const dataProvider = useDataProvider();
740
+
741
+ return async <T = any>(params: CustomParams<T>) => {
742
+ return dataProvider.custom<T>(params);
743
+ };
744
+ }
745
+
746
+ // Permission hook
747
+ export function useCanAccess() {
748
+ const accessControl = useAccessControl();
749
+ return accessControl.can.bind(accessControl);
750
+ }
751
+
752
+ // =============================================================================
753
+ // FACTORY FUNCTIONS FOR EASY SETUP
754
+ // =============================================================================
755
+
756
+ export interface CreateCoreOptions {
757
+ apiService: any;
758
+ resources?: Resources;
759
+ authOptions?: AuthProviderOptions;
760
+ }
761
+
762
+ export function createCore(options: CreateCoreOptions) {
763
+ const { apiService, resources = [], authOptions = {} } = options;
764
+
765
+ const dataProvider = new DataProvider(apiService);
766
+ const authProvider = new AuthProvider(apiService, authOptions);
767
+ const accessControl = new AccessControlProvider(authProvider, resources);
768
+
769
+ return {
770
+ dataProvider,
771
+ authProvider,
772
+ accessControl,
773
+ resources,
774
+ };
775
+ }