@dashadmin/dash-app-common 1.3.24 → 1.3.26

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 (165) hide show
  1. package/dist/DashApp.js +176 -1
  2. package/dist/DashAppLoader.js +348 -3
  3. package/dist/DashDefaultResources.js +10 -1
  4. package/dist/components/DashDefaultPrivateApp.js +150 -1
  5. package/dist/components/DashDefaultPrivateResources.js +10 -1
  6. package/dist/components/DashDefaultPublicApp.js +20 -1
  7. package/dist/components/DashResourceLoader.js +245 -1
  8. package/dist/defaults/extensions/bootstrap/DashDefaultErrorBoundary.js +78 -1
  9. package/dist/defaults/extensions/bootstrap/dashDefaultInitializeApp.js +59 -1
  10. package/dist/defaults/extensions/bootstrap/index.js +24 -1
  11. package/dist/defaults/extensions/components/DashDefaultLoader.js +43 -2
  12. package/dist/defaults/extensions/components/dashDefaultAutoAdminComponents.js +67 -1
  13. package/dist/defaults/extensions/components/dashDefaultNotification.js +28 -1
  14. package/dist/defaults/extensions/components/dashDefaultStaticComponents.js +15 -1
  15. package/dist/defaults/extensions/components/index.js +13 -1
  16. package/dist/defaults/extensions/electron/dashDefaultIpcListeners.js +38 -1
  17. package/dist/defaults/extensions/electron/index.js +5 -1
  18. package/dist/defaults/extensions/hooks/index.js +12 -1
  19. package/dist/defaults/extensions/hooks/useDashDefaultLazyAutoAdminComponents.js +17 -1
  20. package/dist/defaults/extensions/hooks/useDashDefaultLazyI18nProvider.js +51 -1
  21. package/dist/defaults/extensions/hooks/useDashDefaultLazyResources.js +19 -1
  22. package/dist/defaults/extensions/hooks/useDashDefaultMountTracker.js +22 -1
  23. package/dist/defaults/extensions/hooks/useDashDefaultRoutePath.js +17 -1
  24. package/dist/defaults/extensions/i18n/dashDefaultI18nProvider.js +44 -1
  25. package/dist/defaults/extensions/i18n/dashDefaultTranslations.js +87 -1
  26. package/dist/defaults/extensions/i18n/index.js +24 -1
  27. package/dist/defaults/extensions/index.js +190 -1
  28. package/dist/defaults/extensions/layout/dashDefaultDomainAppLayout.js +58 -1
  29. package/dist/defaults/extensions/layout/index.js +12 -1
  30. package/dist/defaults/extensions/managers/DashDefaultWSMessagesManager.js +84 -1
  31. package/dist/defaults/extensions/managers/index.js +5 -1
  32. package/dist/defaults/extensions/providers/dashDefaultAuthProvider.js +220 -1
  33. package/dist/defaults/extensions/providers/dashDefaultDataProvider.js +395 -1
  34. package/dist/defaults/extensions/providers/index.js +24 -1
  35. package/dist/defaults/extensions/router/DashDefaultLanding.js +46 -1
  36. package/dist/defaults/extensions/router/dashDefaultPrivateRoutes.js +38 -1
  37. package/dist/defaults/extensions/router/dashDefaultPublicRoutes.js +65 -1
  38. package/dist/defaults/extensions/router/dashDefaultRouter.js +39 -1
  39. package/dist/defaults/extensions/router/dashDefaultSharedRoutes.js +40 -1
  40. package/dist/defaults/extensions/router/index.js +12 -1
  41. package/dist/defaults/extensions/state/dashDefaultAppCommon.js +57 -1
  42. package/dist/defaults/extensions/state/dashDefaultAppSettings.js +38 -1
  43. package/dist/defaults/extensions/state/dashDefaultInitialAppState.js +31 -1
  44. package/dist/defaults/extensions/state/dashDefaultLayoutSettings.js +48 -1
  45. package/dist/defaults/extensions/state/dashDefaultQueryClient.js +58 -1
  46. package/dist/defaults/extensions/state/dashDefaultResourcesIcons.js +24 -1
  47. package/dist/defaults/extensions/state/index.js +29 -1
  48. package/dist/defaults/extensions/styles/index.js +2 -1
  49. package/dist/defaults/extensions/theme/dashDefaultThemeOptions.js +74 -1
  50. package/dist/defaults/extensions/theme/index.js +6 -1
  51. package/dist/defaults/extensions/utils/IPCMessageBrokerContext.js +12 -1
  52. package/dist/defaults/extensions/utils/dashDefaultAuthProviderUtils.js +29 -1
  53. package/dist/defaults/extensions/utils/dashDefaultDataProviderUtils.js +37 -1
  54. package/dist/defaults/extensions/utils/dashDefaultNotificationsProcessor.js +149 -1
  55. package/dist/defaults/extensions/utils/index.js +44 -1
  56. package/dist/defaults/filters/demo/dashDefaultDemoFilters.js +36 -1
  57. package/dist/defaults/filters/demo/index.js +4 -1
  58. package/dist/defaults/filters/index.js +5 -1
  59. package/dist/defaults/index.js +14 -1
  60. package/dist/defaults/pages/Account/DashDefaultRegister.js +282 -1
  61. package/dist/defaults/pages/Account/DashDefaultSignUp.js +420 -1
  62. package/dist/defaults/pages/Account/DashDefaultSignUpSuccess.js +101 -1
  63. package/dist/defaults/pages/Account/index.js +8 -1
  64. package/dist/defaults/pages/DashDefaultLogin.js +218 -1
  65. package/dist/defaults/pages/Misc/DashDefaultNotFound.js +74 -1
  66. package/dist/defaults/pages/Misc/DashDefaultServerError.js +79 -1
  67. package/dist/defaults/pages/Misc/DashDefaultUnauthorized.js +90 -1
  68. package/dist/defaults/pages/Misc/index.js +8 -1
  69. package/dist/defaults/pages/Static/DashDefaultPrivacyPage.js +48 -1
  70. package/dist/defaults/pages/Static/DashDefaultStaticPage.js +55 -1
  71. package/dist/defaults/pages/Static/DashDefaultTermsPage.js +52 -1
  72. package/dist/defaults/pages/Static/index.js +8 -1
  73. package/dist/defaults/pages/index.js +25 -1
  74. package/dist/defaults/schemas/dashDefaultSchema.js +14 -1
  75. package/dist/defaults/schemas/demo/dashDefaultTodoSchema.js +24 -1
  76. package/dist/defaults/schemas/demo/index.js +4 -1
  77. package/dist/defaults/schemas/index.js +7 -1
  78. package/dist/filters/demo/todoFilters.js +36 -1
  79. package/dist/index.js +56 -5975
  80. package/dist/resources/demo/todoResource.js +58 -1
  81. package/dist/schemas/defaultSchema.js +14 -1
  82. package/dist/schemas/demo/todoSchema.js +24 -1
  83. package/package.json +132 -77
  84. package/src/DashApp.tsx +294 -0
  85. package/src/DashAppLoader.tsx +717 -0
  86. package/src/DashDefaultResources.tsx +10 -0
  87. package/src/components/DashDefaultPrivateApp.tsx +358 -0
  88. package/src/components/DashDefaultPrivateResources.tsx +10 -0
  89. package/src/components/DashDefaultPublicApp.tsx +40 -0
  90. package/src/components/DashResourceLoader.tsx +564 -0
  91. package/src/defaults/extensions/bootstrap/DashDefaultErrorBoundary.tsx +113 -0
  92. package/src/defaults/extensions/bootstrap/dashDefaultInitializeApp.ts +81 -0
  93. package/src/defaults/extensions/bootstrap/index.ts +32 -0
  94. package/src/defaults/extensions/components/DashDefaultLoader.tsx +59 -0
  95. package/src/defaults/extensions/components/dashDefaultAutoAdminComponents.tsx +49 -0
  96. package/src/defaults/extensions/components/dashDefaultNotification.tsx +37 -0
  97. package/src/defaults/extensions/components/dashDefaultStaticComponents.tsx +34 -0
  98. package/src/defaults/extensions/components/index.ts +17 -0
  99. package/src/defaults/extensions/electron/dashDefaultIpcListeners.ts +62 -0
  100. package/src/defaults/extensions/electron/index.ts +7 -0
  101. package/src/defaults/extensions/hooks/index.ts +11 -0
  102. package/src/defaults/extensions/hooks/useDashDefaultLazyAutoAdminComponents.ts +25 -0
  103. package/src/defaults/extensions/hooks/useDashDefaultLazyI18nProvider.ts +38 -0
  104. package/src/defaults/extensions/hooks/useDashDefaultLazyResources.ts +29 -0
  105. package/src/defaults/extensions/hooks/useDashDefaultMountTracker.ts +28 -0
  106. package/src/defaults/extensions/hooks/useDashDefaultRoutePath.ts +27 -0
  107. package/src/defaults/extensions/i18n/dashDefaultI18nProvider.ts +66 -0
  108. package/src/defaults/extensions/i18n/dashDefaultTranslations.ts +82 -0
  109. package/src/defaults/extensions/i18n/index.ts +22 -0
  110. package/src/defaults/extensions/index.ts +177 -0
  111. package/src/defaults/extensions/layout/dashDefaultDomainAppLayout.tsx +126 -0
  112. package/src/defaults/extensions/layout/index.ts +12 -0
  113. package/src/defaults/extensions/managers/DashDefaultWSMessagesManager.tsx +99 -0
  114. package/src/defaults/extensions/managers/index.ts +7 -0
  115. package/src/defaults/extensions/providers/dashDefaultAuthProvider.ts +256 -0
  116. package/src/defaults/extensions/providers/dashDefaultDataProvider.ts +513 -0
  117. package/src/defaults/extensions/providers/index.ts +25 -0
  118. package/src/defaults/extensions/router/DashDefaultLanding.tsx +51 -0
  119. package/src/defaults/extensions/router/dashDefaultPrivateRoutes.tsx +50 -0
  120. package/src/defaults/extensions/router/dashDefaultPublicRoutes.tsx +86 -0
  121. package/src/defaults/extensions/router/dashDefaultRouter.tsx +54 -0
  122. package/src/defaults/extensions/router/dashDefaultSharedRoutes.tsx +56 -0
  123. package/src/defaults/extensions/router/index.ts +13 -0
  124. package/src/defaults/extensions/state/dashDefaultAppCommon.ts +54 -0
  125. package/src/defaults/extensions/state/dashDefaultAppSettings.ts +29 -0
  126. package/src/defaults/extensions/state/dashDefaultInitialAppState.ts +75 -0
  127. package/src/defaults/extensions/state/dashDefaultLayoutSettings.ts +59 -0
  128. package/src/defaults/extensions/state/dashDefaultQueryClient.ts +59 -0
  129. package/src/defaults/extensions/state/dashDefaultResourcesIcons.tsx +23 -0
  130. package/src/defaults/extensions/state/index.ts +29 -0
  131. package/src/defaults/extensions/styles/index.ts +17 -0
  132. package/src/defaults/extensions/theme/dashDefaultThemeOptions.ts +89 -0
  133. package/src/defaults/extensions/theme/index.ts +7 -0
  134. package/src/defaults/extensions/utils/IPCMessageBrokerContext.tsx +22 -0
  135. package/src/defaults/extensions/utils/dashDefaultAuthProviderUtils.ts +36 -0
  136. package/src/defaults/extensions/utils/dashDefaultDataProviderUtils.ts +46 -0
  137. package/src/defaults/extensions/utils/dashDefaultNotificationsProcessor.tsx +208 -0
  138. package/src/defaults/extensions/utils/index.ts +42 -0
  139. package/src/defaults/filters/demo/dashDefaultDemoFilters.tsx +50 -0
  140. package/src/defaults/filters/demo/index.ts +6 -0
  141. package/src/defaults/filters/index.ts +11 -0
  142. package/src/defaults/index.ts +27 -0
  143. package/src/defaults/pages/Account/DashDefaultRegister.tsx +281 -0
  144. package/src/defaults/pages/Account/DashDefaultSignUp.tsx +492 -0
  145. package/src/defaults/pages/Account/DashDefaultSignUpSuccess.tsx +113 -0
  146. package/src/defaults/pages/Account/index.ts +8 -0
  147. package/src/defaults/pages/DashDefaultLogin.tsx +224 -0
  148. package/src/defaults/pages/Misc/DashDefaultNotFound.tsx +96 -0
  149. package/src/defaults/pages/Misc/DashDefaultServerError.tsx +102 -0
  150. package/src/defaults/pages/Misc/DashDefaultUnauthorized.tsx +116 -0
  151. package/src/defaults/pages/Misc/index.ts +8 -0
  152. package/src/defaults/pages/Static/DashDefaultPrivacyPage.tsx +85 -0
  153. package/src/defaults/pages/Static/DashDefaultStaticPage.tsx +75 -0
  154. package/src/defaults/pages/Static/DashDefaultTermsPage.tsx +69 -0
  155. package/src/defaults/pages/Static/index.ts +8 -0
  156. package/src/defaults/pages/index.ts +28 -0
  157. package/src/defaults/schemas/dashDefaultSchema.tsx +25 -0
  158. package/src/defaults/schemas/demo/dashDefaultTodoSchema.tsx +39 -0
  159. package/src/defaults/schemas/demo/index.ts +6 -0
  160. package/src/defaults/schemas/index.ts +14 -0
  161. package/src/filters/demo/todoFilters.tsx +39 -0
  162. package/src/index.ts +131 -0
  163. package/src/resources/demo/todoResource.tsx +68 -0
  164. package/src/schemas/defaultSchema.tsx +14 -0
  165. package/src/schemas/demo/todoSchema.tsx +27 -0
@@ -0,0 +1,717 @@
1
+ /**
2
+ * DashAppLoader
3
+ *
4
+ * The core authentication-aware application loader for Dash Framework.
5
+ * Handles auth state management, Redux session restoration, and conditional
6
+ * rendering of public/private apps based on authentication status.
7
+ *
8
+ * This component consolidates all app bootstrapping logic:
9
+ * - Auth state detection and initialization
10
+ * - Redux state restoration from localStorage
11
+ * - Device storage synchronization
12
+ * - Lazy loading of public/private app components
13
+ * - Loading and error state management
14
+ *
15
+ * Usage:
16
+ * ```tsx
17
+ * import { DashAppLoader, createDashAppLoader } from 'dash-app-common';
18
+ *
19
+ * // Option 1: Minimal usage with all defaults (zero-config)
20
+ * <DashAppLoader />
21
+ *
22
+ * // Option 2: Direct usage with custom apps
23
+ * <DashAppLoader
24
+ * publicAppImport={() => import('./PublicApp')}
25
+ * privateAppImport={() => import('./AdminApp')}
26
+ * />
27
+ *
28
+ * // Option 3: Factory function for reusable loader
29
+ * const AppLoader = createDashAppLoader({
30
+ * publicAppImport: () => import('./PublicApp'),
31
+ * privateAppImport: () => import('./AdminApp'),
32
+ * AdminHookComponent: MainAppHook,
33
+ * });
34
+ *
35
+ * <AppLoader />
36
+ * ```
37
+ */
38
+ import React, { ComponentType, Suspense, useEffect, useState, useCallback, lazy, useMemo } from 'react';
39
+ import { useSelector, useDispatch } from 'react-redux';
40
+ import { useDeviceStorageSync } from 'dash-utils';
41
+
42
+ // Import framework defaults that can't be imported in dash-utils due to circular deps
43
+ import DASHAuthenticationService from 'dash-admin/src/contexts/auth/DASHAuthenticationService';
44
+ import { DASH_REDUX_ACTIONS } from 'dash-admin-state';
45
+ import { ACTION_UPDATE_AUTH } from 'dash-admin-state/src/redux/reducers/Auth';
46
+ import AppLoadingFallback from 'dash-components/src/components/theme/AppLoadingFallback';
47
+ import DashDefaultPublicApp from './components/DashDefaultPublicApp';
48
+ import DashDefaultPrivateApp from './components/DashDefaultPrivateApp';
49
+
50
+ // ============================================================================
51
+ // TYPES & INTERFACES
52
+ // ============================================================================
53
+
54
+ /**
55
+ * Interface for Redux auth state
56
+ * Represents the authentication state in Redux store
57
+ */
58
+ export interface IDashAuthState {
59
+ isLogged: boolean;
60
+ isLoading: boolean;
61
+ token: string | null;
62
+ user: Record<string, any> | null;
63
+ [key: string]: any;
64
+ }
65
+
66
+ /**
67
+ * Interface for Redux app state with auth
68
+ * Represents the full Redux state structure
69
+ */
70
+ export interface IDashAppState {
71
+ auth: IDashAuthState;
72
+ [key: string]: any;
73
+ }
74
+
75
+ /**
76
+ * Interface for auth service (like DASHAuthenticationService)
77
+ * Defines the contract for authentication services
78
+ */
79
+ export interface IDashAuthService {
80
+ /** Initialize app authentication - checks for existing tokens and restores session */
81
+ initializeApp?: (forceGetAuth?: boolean) => Promise<{ success: boolean; user?: any; token?: string; error?: string }>;
82
+ /** Alternative init method for simpler auth services */
83
+ init?: () => Promise<{ success: boolean; user?: any }>;
84
+ login?: (credentials: any) => Promise<any>;
85
+ logout?: () => Promise<void>;
86
+ checkAuth?: () => Promise<boolean>;
87
+ getToken?: () => string | null;
88
+ getUser?: () => any;
89
+ getInitialAuthState?: () => { authenticated: boolean; user: any; auth: any };
90
+ [key: string]: any;
91
+ }
92
+
93
+ /**
94
+ * Interface for Redux actions
95
+ * Defines the contract for Redux action creators
96
+ */
97
+ export interface IDashReduxActions {
98
+ loginSuccess?: (user: any, token?: string) => { type: string; payload: any };
99
+ logout?: () => { type: string };
100
+ setLoading?: (loading: boolean) => { type: string; payload: boolean };
101
+ updateAuth?: (type: any, auth: any) => { type: string; payload: any };
102
+ [key: string]: any;
103
+ }
104
+
105
+ /**
106
+ * Configuration for DashAppLoader
107
+ * All props are optional - sensible defaults are provided
108
+ */
109
+ export interface DashAppLoaderConfig {
110
+ /**
111
+ * Function to import the public app component (lazy loaded)
112
+ * If not provided, uses DefaultPublicApp with basic login/auth routes
113
+ * @optional
114
+ * @example () => import('./DASHPublicApp')
115
+ */
116
+ publicAppImport?: () => Promise<{ default: ComponentType<any> }>;
117
+
118
+ /**
119
+ * Function to import the private/admin app component (lazy loaded)
120
+ * If not provided, uses DefaultPrivateApp with minimal admin shell
121
+ * @optional
122
+ * @example () => import('./DASHLazyAdminApp')
123
+ */
124
+ privateAppImport?: () => Promise<{ default: ComponentType<any> }>;
125
+
126
+ /**
127
+ * The authentication service for checking auth status
128
+ * If not provided, uses DASHAuthenticationService from dash-admin
129
+ * @optional
130
+ */
131
+ authService?: IDashAuthService;
132
+
133
+ /**
134
+ * Redux actions for auth state management
135
+ * If not provided, uses DASH_REDUX_ACTIONS from dash-admin-state
136
+ * @optional
137
+ */
138
+ reduxActions?: IDashReduxActions;
139
+
140
+ /**
141
+ * The Redux action type for updating auth state
142
+ * Used to dispatch auth restoration from localStorage
143
+ * @default ACTION_UPDATE_AUTH
144
+ */
145
+ updateAuthActionType?: string;
146
+
147
+ /**
148
+ * Optional admin hook component to inject into the private app
149
+ * This is rendered as the AdminHook prop of the private app
150
+ * @optional
151
+ */
152
+ AdminHookComponent?: ComponentType<any>;
153
+
154
+ /**
155
+ * Optional custom loading fallback component
156
+ * If not provided, uses AppLoadingFallback from dash-components
157
+ * @optional
158
+ */
159
+ LoadingFallback?: ComponentType<{ message?: string }>;
160
+
161
+ /**
162
+ * Optional custom error fallback component
163
+ * If not provided, uses the default error UI
164
+ * @optional
165
+ */
166
+ ErrorFallback?: ComponentType<{ error: string; onRetry: () => void }>;
167
+
168
+ /**
169
+ * Additional props to pass to the private app
170
+ * @optional
171
+ */
172
+ privateAppProps?: Record<string, any>;
173
+
174
+ /**
175
+ * Additional props to pass to the public app
176
+ * @optional
177
+ */
178
+ publicAppProps?: Record<string, any>;
179
+
180
+ /**
181
+ * Enable debug logging
182
+ * @default false
183
+ */
184
+ debug?: boolean;
185
+ }
186
+
187
+ /**
188
+ * Props for DashAppLoader - extends DashAppLoaderConfig
189
+ */
190
+ export interface DashAppLoaderProps extends DashAppLoaderConfig {}
191
+
192
+ // ============================================================================
193
+ // HELPER FUNCTIONS
194
+ // ============================================================================
195
+
196
+ /**
197
+ * Debug logging helper
198
+ * Only logs when debug mode is enabled
199
+ */
200
+ const log = (debug: boolean, ...args: any[]) => {
201
+ if (debug) {
202
+ console.log(...args);
203
+ }
204
+ };
205
+
206
+ // ============================================================================
207
+ // DEFAULT FALLBACK COMPONENTS
208
+ // ============================================================================
209
+
210
+ /**
211
+ * Default loading fallback component
212
+ * Shows a simple loading message with spinner
213
+ */
214
+ const DefaultLoadingFallback: React.FC<{ message?: string }> = ({ message }) => (
215
+ <div style={{
216
+ display: 'flex',
217
+ flexDirection: 'column',
218
+ justifyContent: 'center',
219
+ alignItems: 'center',
220
+ height: '100vh',
221
+ width: '100%',
222
+ backgroundColor: 'var(--dash-background, #121212)',
223
+ color: 'var(--dash-text, #ffffff)'
224
+ }}>
225
+ <div style={{
226
+ width: '40px',
227
+ height: '40px',
228
+ border: '3px solid rgba(255,255,255,0.3)',
229
+ borderTop: '3px solid var(--dash-primary, #90caf9)',
230
+ borderRadius: '50%',
231
+ animation: 'spin 1s linear infinite',
232
+ marginBottom: '16px'
233
+ }} />
234
+ <p style={{ margin: 0, fontSize: '14px', opacity: 0.8 }}>
235
+ {message || 'Loading application...'}
236
+ </p>
237
+ <style>{`
238
+ @keyframes spin {
239
+ 0% { transform: rotate(0deg); }
240
+ 100% { transform: rotate(360deg); }
241
+ }
242
+ `}</style>
243
+ </div>
244
+ );
245
+
246
+ /**
247
+ * Default error fallback component
248
+ * Shows error message with retry button
249
+ */
250
+ const DefaultErrorFallback: React.FC<{ error: string; onRetry: () => void }> = ({
251
+ error,
252
+ onRetry
253
+ }) => (
254
+ <div style={{
255
+ display: 'flex',
256
+ flexDirection: 'column',
257
+ justifyContent: 'center',
258
+ alignItems: 'center',
259
+ height: '100vh',
260
+ width: '100%',
261
+ backgroundColor: 'var(--dash-background, #121212)',
262
+ color: 'var(--dash-text, #ffffff)',
263
+ padding: '20px'
264
+ }}>
265
+ <h2 style={{ color: '#f44336', marginBottom: '16px' }}>Application Error</h2>
266
+ <p style={{
267
+ maxWidth: '500px',
268
+ textAlign: 'center',
269
+ marginBottom: '24px',
270
+ opacity: 0.8
271
+ }}>
272
+ {error}
273
+ </p>
274
+ <button
275
+ onClick={onRetry}
276
+ style={{
277
+ backgroundColor: 'var(--dash-primary, #90caf9)',
278
+ color: '#000',
279
+ border: 'none',
280
+ padding: '12px 24px',
281
+ borderRadius: '4px',
282
+ cursor: 'pointer',
283
+ fontSize: '14px',
284
+ fontWeight: 500
285
+ }}
286
+ >
287
+ Retry
288
+ </button>
289
+ </div>
290
+ );
291
+
292
+ /**
293
+ * Configuration error component
294
+ * Shows detailed error for missing required configuration
295
+ */
296
+ const ConfigurationError: React.FC<{ missingProps: string[] }> = ({ missingProps }) => (
297
+ <div style={{
298
+ display: 'flex',
299
+ flexDirection: 'column',
300
+ justifyContent: 'center',
301
+ alignItems: 'center',
302
+ height: '100vh',
303
+ width: '100%',
304
+ backgroundColor: '#1a1a1a',
305
+ color: '#ffffff',
306
+ padding: '40px',
307
+ boxSizing: 'border-box'
308
+ }}>
309
+ <h2 style={{ color: '#f44336', marginBottom: '16px' }}>DashAppLoader Configuration Error</h2>
310
+ <p style={{ marginBottom: '16px' }}>The following required props are missing:</p>
311
+ <ul style={{ textAlign: 'left', marginBottom: '24px' }}>
312
+ {missingProps.map(prop => (
313
+ <li key={prop} style={{ color: '#ff9800' }}>{prop}</li>
314
+ ))}
315
+ </ul>
316
+ <div style={{
317
+ backgroundColor: '#2d2d2d',
318
+ padding: '16px',
319
+ borderRadius: '8px',
320
+ maxWidth: '600px',
321
+ width: '100%'
322
+ }}>
323
+ <p style={{ marginBottom: '8px', color: '#90caf9' }}>Example usage:</p>
324
+ <pre style={{
325
+ backgroundColor: '#1e1e1e',
326
+ padding: '12px',
327
+ borderRadius: '4px',
328
+ overflow: 'auto',
329
+ fontSize: '12px'
330
+ }}>
331
+ {`import { DashAppLoader } from 'dash-app-common';
332
+
333
+ <DashAppLoader
334
+ publicAppImport={() => import('./DASHPublicApp')}
335
+ privateAppImport={() => import('./DASHLazyAdminApp')}
336
+ />`}
337
+ </pre>
338
+ </div>
339
+ </div>
340
+ );
341
+
342
+ // ============================================================================
343
+ // DEFAULT APP IMPORTS
344
+ // ============================================================================
345
+
346
+ /**
347
+ * Default public app import function
348
+ * Returns the DashDefaultPublicApp component which provides basic login/auth routes
349
+ */
350
+ const dashDefaultPublicAppImport = (): Promise<{ default: ComponentType<any> }> =>
351
+ Promise.resolve({ default: DashDefaultPublicApp });
352
+
353
+ /**
354
+ * Default private app import function
355
+ * Returns the DashDefaultPrivateApp component which provides a minimal admin shell
356
+ */
357
+ const dashDefaultPrivateAppImport = (): Promise<{ default: ComponentType<any> }> =>
358
+ Promise.resolve({ default: DashDefaultPrivateApp });
359
+
360
+ // ============================================================================
361
+ // INTERNAL COMPONENT - DashAppLoaderInner
362
+ // ============================================================================
363
+
364
+ /**
365
+ * Internal component that handles the auth initialization and app loading
366
+ * This is wrapped by DashAppLoader which handles validation
367
+ */
368
+ interface DashAppLoaderInnerProps {
369
+ publicAppImport: () => Promise<{ default: ComponentType<any> }>;
370
+ privateAppImport: () => Promise<{ default: ComponentType<any> }>;
371
+ authService: IDashAuthService;
372
+ reduxActions: IDashReduxActions;
373
+ updateAuthActionType: string;
374
+ AdminHookComponent?: ComponentType<any>;
375
+ LoadingFallback: ComponentType<{ message?: string }>;
376
+ ErrorFallback: ComponentType<{ error: string; onRetry: () => void }>;
377
+ privateAppProps: Record<string, any>;
378
+ publicAppProps: Record<string, any>;
379
+ debug: boolean;
380
+ }
381
+
382
+ const DashAppLoaderInner: React.FC<DashAppLoaderInnerProps> = ({
383
+ publicAppImport,
384
+ privateAppImport,
385
+ authService,
386
+ reduxActions,
387
+ updateAuthActionType,
388
+ AdminHookComponent,
389
+ LoadingFallback,
390
+ ErrorFallback,
391
+ privateAppProps,
392
+ publicAppProps,
393
+ debug
394
+ }) => {
395
+ const dispatch = useDispatch();
396
+ const auth = useSelector((state: IDashAppState) => state.auth);
397
+
398
+ // State for initialization
399
+ const [isInitializing, setIsInitializing] = useState(true);
400
+ const [initError, setInitError] = useState<string | null>(null);
401
+ const [hasRestoredFromStorage, setHasRestoredFromStorage] = useState(false);
402
+
403
+ // Use device storage sync hook for Capacitor/Electron environments
404
+ useDeviceStorageSync();
405
+
406
+ // Log current auth state in debug mode
407
+ log(debug, '🔍 DashAppLoader: Redux auth state:', {
408
+ isLogged: auth?.isLogged,
409
+ hasUser: !!auth?.user,
410
+ hasToken: !!auth?.token,
411
+ });
412
+
413
+ // Effect to restore auth from localStorage to Redux if needed
414
+ useEffect(() => {
415
+ if (!hasRestoredFromStorage && !auth?.isLogged) {
416
+ log(debug, '🔍 DashAppLoader: Checking for persisted auth data...');
417
+
418
+ // Check localStorage for persisted auth data
419
+ const storedToken = localStorage.getItem('token');
420
+ const storedUserRaw = localStorage.getItem('user');
421
+ const storedTenantRaw = localStorage.getItem('tenant');
422
+
423
+ log(debug, '🔍 DashAppLoader: Persisted data check:', {
424
+ hasToken: !!storedToken,
425
+ hasUser: !!storedUserRaw,
426
+ hasTenant: !!storedTenantRaw
427
+ });
428
+
429
+ // If we have persisted auth data but Redux is empty, restore it
430
+ if (storedToken && storedUserRaw && !auth?.isLogged) {
431
+ log(debug, '🔄 DashAppLoader: Restoring auth session to Redux from localStorage');
432
+
433
+ let parsedUser: any = null;
434
+ try {
435
+ // User is stored as JSON string in localStorage
436
+ parsedUser = JSON.parse(storedUserRaw);
437
+
438
+ // Handle double-stringified case (JSON inside JSON)
439
+ if (typeof parsedUser === 'string') {
440
+ log(debug, '🔄 DashAppLoader: User was double-stringified, parsing again...');
441
+ parsedUser = JSON.parse(parsedUser);
442
+ }
443
+ } catch (e) {
444
+ console.error('❌ DashAppLoader: Cannot parse user from localStorage, clearing auth data');
445
+ localStorage.removeItem('token');
446
+ localStorage.removeItem('user');
447
+ localStorage.removeItem('tenant');
448
+ localStorage.removeItem('authenticated');
449
+ setHasRestoredFromStorage(true);
450
+ return;
451
+ }
452
+
453
+ let parsedTenant: any = null;
454
+ if (storedTenantRaw) {
455
+ try {
456
+ parsedTenant = JSON.parse(storedTenantRaw);
457
+ if (typeof parsedTenant === 'string') {
458
+ parsedTenant = JSON.parse(parsedTenant);
459
+ }
460
+ } catch {
461
+ // Tenant parsing failed, continue without it
462
+ log(debug, '⚠️ DashAppLoader: Could not parse tenant, continuing without it');
463
+ }
464
+ }
465
+
466
+ // Dispatch auth restoration action directly to Redux
467
+ dispatch({
468
+ type: updateAuthActionType,
469
+ payload: {
470
+ isLogged: true,
471
+ token: storedToken,
472
+ user: parsedUser,
473
+ tenant: parsedTenant
474
+ }
475
+ });
476
+ log(debug, '✅ DashAppLoader: Redux state restored from persisted data');
477
+ }
478
+
479
+ setHasRestoredFromStorage(true);
480
+ }
481
+ }, [auth?.isLogged, hasRestoredFromStorage, dispatch, updateAuthActionType, debug]);
482
+
483
+ // Effect to initialize auth service
484
+ useEffect(() => {
485
+ let mounted = true;
486
+
487
+ const initializeAuth = async () => {
488
+ if (!hasRestoredFromStorage) {
489
+ // Wait for storage restoration to complete first
490
+ return;
491
+ }
492
+
493
+ try {
494
+ log(debug, '🚀 DashAppLoader: Starting app initialization...');
495
+
496
+ // Call auth service initialization method
497
+ // DASHAuthenticationService uses initializeApp(), fallback to init() for other implementations
498
+ let initResult: { success: boolean; user?: any; error?: string };
499
+
500
+ if (authService.initializeApp) {
501
+ initResult = await authService.initializeApp();
502
+ } else if (authService.init) {
503
+ initResult = await authService.init();
504
+ } else {
505
+ // No init method available, just continue
506
+ log(debug, '⚠️ DashAppLoader: No init method on authService, skipping initialization');
507
+ setIsInitializing(false);
508
+ return;
509
+ }
510
+
511
+ if (!mounted) return;
512
+
513
+ log(debug, '🔍 DashAppLoader: Initialization result:', initResult);
514
+
515
+ if (initResult.success && initResult.user) {
516
+ log(debug, '✅ DashAppLoader: Auto-login successful');
517
+ // Auth service should have updated Redux via its own dispatch
518
+ }
519
+
520
+ setIsInitializing(false);
521
+ } catch (error) {
522
+ if (!mounted) return;
523
+ console.error('❌ DashAppLoader initialization error:', error);
524
+ setInitError(error instanceof Error ? error.message : 'Unknown error during initialization');
525
+ setIsInitializing(false);
526
+ }
527
+ };
528
+
529
+ initializeAuth();
530
+
531
+ return () => {
532
+ mounted = false;
533
+ };
534
+ }, [authService, hasRestoredFromStorage, debug]);
535
+
536
+ // Create lazy loaded components - memoized to prevent recreating on each render
537
+ const PublicApp = useMemo(() => lazy(publicAppImport), [publicAppImport]);
538
+ const PrivateApp = useMemo(() => lazy(privateAppImport), [privateAppImport]);
539
+
540
+ // Retry handler for errors
541
+ const handleRetry = useCallback(() => {
542
+ setInitError(null);
543
+ setIsInitializing(true);
544
+ setHasRestoredFromStorage(false);
545
+ }, []);
546
+
547
+ // Show error state
548
+ if (initError) {
549
+ return <ErrorFallback error={initError} onRetry={handleRetry} />;
550
+ }
551
+
552
+ // Show loading state during initialization
553
+ if (isInitializing || !hasRestoredFromStorage) {
554
+ return <LoadingFallback message="Initializing application..." />;
555
+ }
556
+
557
+ // Determine which app to show based on auth state
558
+ const isAuthenticated = auth?.isLogged === true;
559
+
560
+ log(debug, '🎯 DashAppLoader: Rendering app:', {
561
+ isAuthenticated,
562
+ appType: isAuthenticated ? 'Private' : 'Public'
563
+ });
564
+
565
+ // Render the appropriate app
566
+ return (
567
+ <Suspense fallback={<LoadingFallback message={isAuthenticated ? 'Loading admin...' : 'Loading...'} />}>
568
+ {isAuthenticated ? (
569
+ <PrivateApp
570
+ AdminHookComponent={AdminHookComponent}
571
+ {...privateAppProps}
572
+ />
573
+ ) : (
574
+ <PublicApp {...publicAppProps} />
575
+ )}
576
+ </Suspense>
577
+ );
578
+ };
579
+
580
+ // ============================================================================
581
+ // MAIN COMPONENT - DashAppLoader
582
+ // ============================================================================
583
+
584
+ /**
585
+ * DashAppLoader - Authentication-aware application loader
586
+ *
587
+ * This component provides all the required framework dependencies:
588
+ * - DASHAuthenticationService from dash-admin
589
+ * - DASH_REDUX_ACTIONS from dash-admin-state
590
+ * - ACTION_UPDATE_AUTH from dash-admin-state
591
+ * - AppLoadingFallback from dash-components
592
+ * - DashDefaultPublicApp for unauthenticated users (if no publicAppImport provided)
593
+ * - DashDefaultPrivateApp for authenticated users (if no privateAppImport provided)
594
+ *
595
+ * Key features:
596
+ * - Auth state management and initialization
597
+ * - Redux state restoration from localStorage
598
+ * - Device storage synchronization (Capacitor/Electron)
599
+ * - Lazy loading of public/private apps
600
+ * - Loading and error state handling
601
+ *
602
+ * Can be used with zero configuration for a basic working app:
603
+ * ```tsx
604
+ * <DashAppLoader />
605
+ * ```
606
+ *
607
+ * Or with custom apps:
608
+ * ```tsx
609
+ * <DashAppLoader
610
+ * publicAppImport={() => import('./DASHPublicApp')}
611
+ * privateAppImport={() => import('./DASHLazyAdminApp')}
612
+ * AdminHookComponent={MainAppHookComponent}
613
+ * debug={import.meta.env.DEV}
614
+ * />
615
+ * ```
616
+ */
617
+ export const DashAppLoader: React.FC<DashAppLoaderProps> = ({
618
+ publicAppImport,
619
+ privateAppImport,
620
+ AdminHookComponent,
621
+ LoadingFallback = AppLoadingFallback,
622
+ ErrorFallback = DefaultErrorFallback,
623
+ privateAppProps = {},
624
+ publicAppProps = {},
625
+ debug = false,
626
+ // Allow overrides but provide defaults
627
+ authService = DASHAuthenticationService,
628
+ reduxActions = DASH_REDUX_ACTIONS,
629
+ updateAuthActionType = ACTION_UPDATE_AUTH,
630
+ }) => {
631
+ // Use default app imports if not provided
632
+ const resolvedPublicAppImport = publicAppImport || dashDefaultPublicAppImport;
633
+ const resolvedPrivateAppImport = privateAppImport || dashDefaultPrivateAppImport;
634
+
635
+ // Validate that we have the framework dependencies
636
+ // These should always be available since we provide defaults
637
+ const missingProps: string[] = [];
638
+ if (!authService) missingProps.push('authService');
639
+ if (!reduxActions) missingProps.push('reduxActions');
640
+ if (!updateAuthActionType) missingProps.push('updateAuthActionType');
641
+
642
+ if (missingProps.length > 0) {
643
+ return <ConfigurationError missingProps={missingProps} />;
644
+ }
645
+
646
+ return (
647
+ <DashAppLoaderInner
648
+ publicAppImport={resolvedPublicAppImport}
649
+ privateAppImport={resolvedPrivateAppImport}
650
+ authService={authService}
651
+ reduxActions={reduxActions}
652
+ updateAuthActionType={updateAuthActionType}
653
+ AdminHookComponent={AdminHookComponent}
654
+ LoadingFallback={LoadingFallback}
655
+ ErrorFallback={ErrorFallback}
656
+ privateAppProps={privateAppProps}
657
+ publicAppProps={publicAppProps}
658
+ debug={debug}
659
+ />
660
+ );
661
+ };
662
+
663
+ // ============================================================================
664
+ // FACTORY FUNCTION
665
+ // ============================================================================
666
+
667
+ /**
668
+ * Create a configured DashAppLoader component with framework defaults
669
+ *
670
+ * This factory function creates a component with all configuration baked in,
671
+ * perfect for use in your app's entry point.
672
+ *
673
+ * @param config - Configuration for the app loader (all optional)
674
+ * @returns A configured React component
675
+ *
676
+ * @example
677
+ * ```tsx
678
+ * import { createDashAppLoader } from 'dash-app-common';
679
+ * import MainAppHook from './components/MainAppHookComponent';
680
+ *
681
+ * // Minimal - uses all defaults
682
+ * const AppLoader = createDashAppLoader({});
683
+ *
684
+ * // With custom apps
685
+ * const AppLoader = createDashAppLoader({
686
+ * publicAppImport: () => import('./DASHPublicApp'),
687
+ * privateAppImport: () => import('./DASHLazyAdminApp'),
688
+ * AdminHookComponent: MainAppHook,
689
+ * debug: import.meta.env.DEV,
690
+ * });
691
+ *
692
+ * // In your component:
693
+ * export const DASHAppLoader = () => <AppLoader />;
694
+ * ```
695
+ */
696
+ export const createDashAppLoader = (
697
+ config: DashAppLoaderProps = {}
698
+ ): React.FC => {
699
+ return () => <DashAppLoader {...config} />;
700
+ };
701
+
702
+ // ============================================================================
703
+ // RE-EXPORTS FOR CONVENIENCE
704
+ // ============================================================================
705
+
706
+ // Re-export framework services for advanced customization
707
+ export { DASHAuthenticationService, DASH_REDUX_ACTIONS, ACTION_UPDATE_AUTH, AppLoadingFallback };
708
+
709
+ // Re-export default apps and routes
710
+ export { DashDefaultPublicApp, DashDefaultPrivateApp };
711
+ export { dashDefaultPublicRoutes } from './defaults/extensions/router/dashDefaultPublicRoutes';
712
+ export { dashDefaultPrivateRoutes } from './defaults/extensions/router/dashDefaultPrivateRoutes';
713
+
714
+ // Re-export default fallback components
715
+ export { DefaultLoadingFallback, DefaultErrorFallback };
716
+
717
+ export default DashAppLoader;