@djangocfg/layouts 2.1.36 → 2.1.38

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 (64) hide show
  1. package/README.md +204 -18
  2. package/package.json +5 -5
  3. package/src/components/errors/index.ts +9 -0
  4. package/src/components/errors/types.ts +38 -0
  5. package/src/layouts/AppLayout/AppLayout.tsx +33 -45
  6. package/src/layouts/AppLayout/BaseApp.tsx +105 -28
  7. package/src/layouts/AuthLayout/AuthContext.tsx +7 -1
  8. package/src/layouts/AuthLayout/OAuthProviders.tsx +1 -10
  9. package/src/layouts/AuthLayout/OTPForm.tsx +1 -0
  10. package/src/layouts/PrivateLayout/PrivateLayout.tsx +1 -1
  11. package/src/layouts/PublicLayout/PublicLayout.tsx +1 -1
  12. package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
  13. package/src/layouts/PublicLayout/components/PublicNavigation.tsx +1 -1
  14. package/src/layouts/_components/UserMenu.tsx +1 -1
  15. package/src/layouts/index.ts +1 -1
  16. package/src/layouts/types/index.ts +47 -0
  17. package/src/layouts/types/layout.types.ts +61 -0
  18. package/src/layouts/types/providers.types.ts +65 -0
  19. package/src/layouts/types/ui.types.ts +103 -0
  20. package/src/snippets/Analytics/index.ts +1 -0
  21. package/src/snippets/Analytics/types.ts +10 -0
  22. package/src/snippets/McpChat/context/ChatContext.tsx +9 -0
  23. package/src/snippets/PWAInstall/@docs/README.md +92 -0
  24. package/src/snippets/PWAInstall/@docs/research/ios-android-install-flows.md +576 -0
  25. package/src/snippets/PWAInstall/README.md +185 -0
  26. package/src/snippets/PWAInstall/components/A2HSHint.tsx +227 -0
  27. package/src/snippets/PWAInstall/components/DesktopGuide.tsx +229 -0
  28. package/src/snippets/PWAInstall/components/IOSGuide.tsx +29 -0
  29. package/src/snippets/PWAInstall/components/IOSGuideDrawer.tsx +101 -0
  30. package/src/snippets/PWAInstall/components/IOSGuideModal.tsx +101 -0
  31. package/src/snippets/PWAInstall/context/InstallContext.tsx +102 -0
  32. package/src/snippets/PWAInstall/hooks/useInstallPrompt.ts +167 -0
  33. package/src/snippets/PWAInstall/hooks/useIsPWA.ts +115 -0
  34. package/src/snippets/PWAInstall/index.ts +76 -0
  35. package/src/snippets/PWAInstall/types/components.ts +95 -0
  36. package/src/snippets/PWAInstall/types/config.ts +22 -0
  37. package/src/snippets/PWAInstall/types/index.ts +26 -0
  38. package/src/snippets/PWAInstall/types/install.ts +38 -0
  39. package/src/snippets/PWAInstall/types/platform.ts +29 -0
  40. package/src/snippets/PWAInstall/utils/localStorage.ts +181 -0
  41. package/src/snippets/PWAInstall/utils/logger.ts +149 -0
  42. package/src/snippets/PWAInstall/utils/platform.ts +151 -0
  43. package/src/snippets/PushNotifications/@docs/README.md +191 -0
  44. package/src/snippets/PushNotifications/@docs/guides/django-integration.md +648 -0
  45. package/src/snippets/PushNotifications/@docs/guides/service-worker.md +467 -0
  46. package/src/snippets/PushNotifications/@docs/guides/vapid-setup.md +352 -0
  47. package/src/snippets/PushNotifications/README.md +328 -0
  48. package/src/snippets/PushNotifications/components/PushPrompt.tsx +165 -0
  49. package/src/snippets/PushNotifications/config.ts +20 -0
  50. package/src/snippets/PushNotifications/context/DjangoPushContext.tsx +190 -0
  51. package/src/snippets/PushNotifications/hooks/useDjangoPush.ts +259 -0
  52. package/src/snippets/PushNotifications/hooks/usePushNotifications.ts +209 -0
  53. package/src/snippets/PushNotifications/index.ts +87 -0
  54. package/src/snippets/PushNotifications/types/config.ts +28 -0
  55. package/src/snippets/PushNotifications/types/index.ts +9 -0
  56. package/src/snippets/PushNotifications/types/push.ts +21 -0
  57. package/src/snippets/PushNotifications/utils/localStorage.ts +60 -0
  58. package/src/snippets/PushNotifications/utils/logger.ts +149 -0
  59. package/src/snippets/PushNotifications/utils/platform.ts +151 -0
  60. package/src/snippets/PushNotifications/utils/vapid.ts +226 -0
  61. package/src/snippets/index.ts +55 -0
  62. package/src/layouts/shared/index.ts +0 -21
  63. package/src/layouts/shared/types.ts +0 -211
  64. /package/src/layouts/{shared → types}/README.md +0 -0
@@ -6,6 +6,11 @@
6
6
  * - TooltipProvider (tooltip positioning)
7
7
  * - SWRConfig (data fetching)
8
8
  * - AuthProvider (authentication context)
9
+ * - AuthDialog (global auth prompt dialog)
10
+ * - AnalyticsProvider (Google Analytics, optional)
11
+ * - CentrifugoProvider (WebSocket real-time, optional)
12
+ * - PwaProvider (PWA installation, optional)
13
+ * - DjangoPushProvider (Django web push integration, optional)
9
14
  * - ErrorTrackingProvider (error handling)
10
15
  * - ErrorBoundary (React error boundary, enabled by default)
11
16
  * - MCP Chat Widget (optional, lazy loaded)
@@ -14,10 +19,17 @@
14
19
  * ```tsx
15
20
  * import { BaseApp } from '@djangocfg/layouts';
16
21
  *
17
- * // With MCP Chat enabled
22
+ * // With PWA Install and Push Notifications
18
23
  * <BaseApp
19
24
  * theme={{ defaultTheme: 'system' }}
20
25
  * auth={{ loginPath: '/auth/login' }}
26
+ * analytics={{ googleTrackingId: 'G-XXXXXXXXXX' }}
27
+ * pwaInstall={{ enabled: true, logo: '/logo192.png' }}
28
+ * pushNotifications={{
29
+ * enabled: true,
30
+ * vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY,
31
+ * requirePWA: true
32
+ * }}
21
33
  * mcpChat={{ enabled: true, autoDetectEnvironment: true }}
22
34
  * >
23
35
  * {children}
@@ -35,12 +47,18 @@
35
47
  import { ReactNode } from 'react';
36
48
  import dynamic from 'next/dynamic';
37
49
  import { SWRConfig } from 'swr';
38
- import { ThemeProvider, Toaster, TooltipProvider } from '@djangocfg/ui-nextjs';
50
+ import { ThemeProvider, SonnerToaster, TooltipProvider } from '@djangocfg/ui-nextjs';
39
51
  import { AuthProvider } from '@djangocfg/api/auth';
52
+ import { CentrifugoProvider } from '@djangocfg/centrifugo';
53
+ import { getCentrifugoAuthTokenRetrieve } from '@djangocfg/api';
40
54
  import { ErrorTrackingProvider } from '../../components/errors/ErrorsTracker';
41
55
  import { ErrorBoundary } from '../../components/errors/ErrorBoundary';
42
56
  import { PageProgress } from '../../components/core/PageProgress';
43
- import type { BaseLayoutProps } from '../shared/types';
57
+ import { AnalyticsProvider } from '../../snippets/Analytics';
58
+ import { PwaProvider, A2HSHint } from '../../snippets/PWAInstall';
59
+ import { DjangoPushProvider, PushPrompt } from '../../snippets/PushNotifications';
60
+ import { AuthDialog } from '../../snippets/AuthDialog';
61
+ import type { BaseLayoutProps } from '../types/layout.types';
44
62
 
45
63
  // Lazy load MCP Chat Widget with dynamic import
46
64
  const AIChatWidget = dynamic(
@@ -54,21 +72,38 @@ export type BaseAppProps = BaseLayoutProps;
54
72
  /**
55
73
  * BaseApp - Core providers wrapper for any React/Next.js app
56
74
  *
57
- * Includes: ThemeProvider, TooltipProvider, SWRConfig, AuthProvider, ErrorTrackingProvider, ErrorBoundary (optional), MCP Chat (optional)
75
+ * Includes: ThemeProvider, TooltipProvider, SWRConfig, AuthProvider, AnalyticsProvider,
76
+ * CentrifugoProvider, PwaProvider, PushProvider, ErrorTrackingProvider,
77
+ * ErrorBoundary (optional), MCP Chat (optional)
58
78
  * Also renders global Toaster and PageProgress components
59
79
  */
60
80
  export function BaseApp({
61
81
  children,
62
82
  theme,
63
83
  auth,
84
+ analytics,
85
+ centrifugo,
64
86
  errorTracking,
65
87
  errorBoundary,
66
88
  swr,
67
89
  mcpChat,
90
+ pwaInstall,
91
+ pushNotifications,
68
92
  }: BaseAppProps) {
69
93
  // ErrorBoundary is enabled by default
70
94
  const enableErrorBoundary = errorBoundary?.enabled !== false;
71
95
 
96
+ // Check if PWA Install is enabled
97
+ const pwaInstallEnabled = pwaInstall?.enabled !== false;
98
+ const showInstallHint = pwaInstallEnabled && pwaInstall?.showInstallHint !== false;
99
+
100
+ // Check if Push Notifications is enabled
101
+ const pushEnabled = pushNotifications?.enabled !== false && !!pushNotifications?.vapidPublicKey;
102
+
103
+ // Centrifugo configuration
104
+ const centrifugoUrl = centrifugo?.url || process.env.NEXT_PUBLIC_CENTRIFUGO_URL;
105
+ const centrifugoEnabled = centrifugo?.enabled !== false;
106
+
72
107
  const content = (
73
108
  <ThemeProvider
74
109
  defaultTheme={theme?.defaultTheme || 'system'}
@@ -83,31 +118,73 @@ export function BaseApp({
83
118
  }}
84
119
  >
85
120
  <AuthProvider config={auth}>
86
- <ErrorTrackingProvider
87
- validation={errorTracking?.validation}
88
- cors={errorTracking?.cors}
89
- network={errorTracking?.network}
90
- onError={errorTracking?.onError}
91
- >
92
- {children}
93
- <PageProgress />
94
- <Toaster />
121
+ <AnalyticsProvider trackingId={analytics?.googleTrackingId}>
122
+ <CentrifugoProvider
123
+ enabled={centrifugoEnabled}
124
+ autoConnect={centrifugoEnabled && centrifugo?.autoConnect}
125
+ url={centrifugoUrl}
126
+ onTokenRefresh={async () => {
127
+ const response = await getCentrifugoAuthTokenRetrieve();
128
+ return response.token;
129
+ }}
130
+ >
131
+ <PwaProvider enabled={pwaInstallEnabled}>
132
+ <DjangoPushProvider
133
+ vapidPublicKey={pushNotifications?.vapidPublicKey || ''}
134
+ autoSubscribe={pushNotifications?.autoSubscribe}
135
+ >
136
+ <ErrorTrackingProvider
137
+ validation={errorTracking?.validation}
138
+ cors={errorTracking?.cors}
139
+ network={errorTracking?.network}
140
+ onError={errorTracking?.onError}
141
+ >
142
+ {children}
143
+ <PageProgress />
144
+ <SonnerToaster />
145
+
146
+ {/* PWA Install Hint */}
147
+ {showInstallHint && (
148
+ <A2HSHint
149
+ resetAfterDays={pwaInstall?.resetAfterDays}
150
+ delayMs={pwaInstall?.delayMs}
151
+ logo={pwaInstall?.logo}
152
+ />
153
+ )}
154
+
155
+ {/* Push Notifications Prompt */}
156
+ {pushEnabled && (
157
+ <PushPrompt
158
+ vapidPublicKey={pushNotifications!.vapidPublicKey}
159
+ subscribeEndpoint={pushNotifications?.subscribeEndpoint}
160
+ requirePWA={pushNotifications?.requirePWA ?? true}
161
+ delayMs={pushNotifications?.delayMs}
162
+ resetAfterDays={pushNotifications?.resetAfterDays}
163
+ />
164
+ )}
165
+
166
+ {/* MCP Chat Widget - lazy loaded */}
167
+ {mcpChat?.enabled && (
168
+ <AIChatWidget
169
+ apiEndpoint={mcpChat.apiEndpoint}
170
+ title={mcpChat.title}
171
+ placeholder={mcpChat.placeholder}
172
+ greeting={mcpChat.greeting}
173
+ position={mcpChat.position}
174
+ variant={mcpChat.variant}
175
+ enableStreaming={mcpChat.enableStreaming}
176
+ autoDetectEnvironment={mcpChat.autoDetectEnvironment}
177
+ className={mcpChat.className}
178
+ />
179
+ )}
95
180
 
96
- {/* MCP Chat Widget - lazy loaded */}
97
- {mcpChat?.enabled && (
98
- <AIChatWidget
99
- apiEndpoint={mcpChat.apiEndpoint}
100
- title={mcpChat.title}
101
- placeholder={mcpChat.placeholder}
102
- greeting={mcpChat.greeting}
103
- position={mcpChat.position}
104
- variant={mcpChat.variant}
105
- enableStreaming={mcpChat.enableStreaming}
106
- autoDetectEnvironment={mcpChat.autoDetectEnvironment}
107
- className={mcpChat.className}
108
- />
109
- )}
110
- </ErrorTrackingProvider>
181
+ {/* Auth Dialog - global auth prompt */}
182
+ <AuthDialog authPath={auth?.routes?.auth} />
183
+ </ErrorTrackingProvider>
184
+ </DjangoPushProvider>
185
+ </PwaProvider>
186
+ </CentrifugoProvider>
187
+ </AnalyticsProvider>
111
188
  </AuthProvider>
112
189
  </SWRConfig>
113
190
  </TooltipProvider>
@@ -16,18 +16,24 @@ export const AuthProvider: React.FC<AuthProps> = ({
16
16
  privacyUrl,
17
17
  enablePhoneAuth = false, // Default to false for backward compatibility
18
18
  enableGithubAuth = false, // Default to false for backward compatibility
19
+ redirectUrl, // URL to redirect after successful OTP auth
19
20
  onIdentifierSuccess,
20
21
  onOTPSuccess,
21
22
  onError,
22
23
  }) => {
23
24
  const sourceUrl = sourceUrlProp || (typeof window !== 'undefined' ? window.location.origin : '');
24
25
 
25
- // Use the auth form hook with required sourceUrl
26
+ // Require terms acceptance only if terms or privacy URL is provided
27
+ const requireTermsAcceptance = Boolean(termsUrl || privacyUrl);
28
+
29
+ // Use the auth form hook with required sourceUrl and redirectUrl
26
30
  const authForm = useAuthForm({
27
31
  onIdentifierSuccess,
28
32
  onOTPSuccess,
29
33
  onError,
30
34
  sourceUrl,
35
+ redirectUrl,
36
+ requireTermsAcceptance,
31
37
  });
32
38
 
33
39
  const value: AuthContextType = {
@@ -15,11 +15,10 @@ import { useAuthContext } from './AuthContext';
15
15
  * Handles the OAuth flow initiation.
16
16
  */
17
17
  export const OAuthProviders: React.FC = () => {
18
- const { enableGithubAuth, sourceUrl, error: contextError, setError } = useAuthContext();
18
+ const { enableGithubAuth, sourceUrl, setError } = useAuthContext();
19
19
 
20
20
  const {
21
21
  isLoading: isGithubLoading,
22
- error: githubError,
23
22
  startGithubAuth,
24
23
  } = useGithubAuth({
25
24
  sourceUrl,
@@ -33,8 +32,6 @@ export const OAuthProviders: React.FC = () => {
33
32
  return null;
34
33
  }
35
34
 
36
- const error = githubError || contextError;
37
-
38
35
  return (
39
36
  <div className="space-y-4">
40
37
  {/* Divider */}
@@ -74,12 +71,6 @@ export const OAuthProviders: React.FC = () => {
74
71
  )}
75
72
  </div>
76
73
 
77
- {/* Error Message */}
78
- {error && (
79
- <div className="text-sm text-destructive bg-destructive/10 p-3 rounded-md border border-destructive/20">
80
- {error}
81
- </div>
82
- )}
83
74
  </div>
84
75
  );
85
76
  };
@@ -79,6 +79,7 @@ export const OTPForm: React.FC = () => {
79
79
  onChange={setOtp}
80
80
  disabled={isLoading}
81
81
  autoFocus={true}
82
+ autoSubmit={true}
82
83
  size="lg"
83
84
  />
84
85
  </div>
@@ -51,7 +51,7 @@ import { SidebarProvider, SidebarInset, Preloader, ButtonLink } from '@djangocfg
51
51
  import { useAuth } from '@djangocfg/api/auth';
52
52
  import { PrivateSidebar, PrivateHeader, PrivateContent } from './components';
53
53
  import type { LucideIcon as LucideIconType } from 'lucide-react';
54
- import { UserMenuConfig } from '../shared/types';
54
+ import { UserMenuConfig } from '../types';
55
55
 
56
56
  export interface SidebarItem {
57
57
  label: string;
@@ -30,7 +30,7 @@
30
30
 
31
31
  import { ReactNode, useState, useEffect } from 'react';
32
32
  import { usePathname } from 'next/navigation';
33
- import type { NavigationItem, UserMenuConfig } from '../shared/types';
33
+ import type { NavigationItem, UserMenuConfig } from '../types';
34
34
  import {
35
35
  PublicNavigation,
36
36
  PublicMobileDrawer,
@@ -20,7 +20,7 @@ import {
20
20
  import { ThemeToggle } from '@djangocfg/ui-nextjs/theme';
21
21
  import { useAuth } from '@djangocfg/api/auth';
22
22
  import { UserMenu } from '../../_components/UserMenu';
23
- import type { NavigationItem, UserMenuConfig } from '../../shared/types';
23
+ import type { NavigationItem, UserMenuConfig } from '../../types';
24
24
 
25
25
  interface PublicMobileDrawerProps {
26
26
  isOpen: boolean;
@@ -15,7 +15,7 @@ import { cn } from '@djangocfg/ui-nextjs/lib';
15
15
  import { useIsMobile } from '@djangocfg/ui-nextjs/hooks';
16
16
  import { useAuth } from '@djangocfg/api/auth';
17
17
  import { UserMenu } from '../../_components/UserMenu';
18
- import type { NavigationItem, UserMenuConfig } from '../../shared/types';
18
+ import type { NavigationItem, UserMenuConfig } from '../../types';
19
19
 
20
20
  interface PublicNavigationProps {
21
21
  logo?: string;
@@ -47,7 +47,7 @@ import {
47
47
  Button,
48
48
  } from '@djangocfg/ui-nextjs/components';
49
49
  import { useAuth } from '@djangocfg/api/auth';
50
- import type { UserMenuGroup } from '../shared/types';
50
+ import type { UserMenuGroup } from '../types';
51
51
 
52
52
  export interface UserMenuProps {
53
53
  /** Display variant */
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  // Shared types (universal type system)
9
- export * from './shared';
9
+ export * from './types';
10
10
 
11
11
  // Smart layout router
12
12
  export * from './AppLayout';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Shared Layout Types - Central Export
3
+ *
4
+ * This is the main entry point for all layout types.
5
+ * Types are organized by category and imported from their source modules.
6
+ */
7
+
8
+ // ============================================================================
9
+ // Provider Configuration Types
10
+ // ============================================================================
11
+
12
+ // Local provider configs
13
+ export type { ThemeConfig, SWRConfigOptions, McpChatConfig, CentrifugoConfig } from './providers.types';
14
+
15
+ // External provider configs (re-export for convenience)
16
+ export type { AnalyticsConfig } from '../../snippets/Analytics/types';
17
+ export type { PwaInstallConfig } from '../../snippets/PWAInstall/types';
18
+ export type { PushNotificationsConfig } from '../../snippets/PushNotifications/types';
19
+ export type {
20
+ ErrorBoundaryConfig,
21
+ ErrorTrackingConfig,
22
+ ValidationErrorConfig,
23
+ CORSErrorConfig,
24
+ NetworkErrorConfig,
25
+ } from '../../components/errors/types';
26
+
27
+ // ============================================================================
28
+ // UI Component Types
29
+ // ============================================================================
30
+
31
+ export type {
32
+ NavigationItem,
33
+ NavigationSection,
34
+ FooterLink,
35
+ FooterMenuSection,
36
+ FooterSocialLinks,
37
+ FooterConfig,
38
+ UserMenuItem,
39
+ UserMenuGroup,
40
+ UserMenuConfig,
41
+ } from './ui.types';
42
+
43
+ // ============================================================================
44
+ // Layout Types
45
+ // ============================================================================
46
+
47
+ export type { BaseLayoutProps } from './layout.types';
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Base Layout Types
3
+ *
4
+ * Core layout configuration that combines all provider configs
5
+ */
6
+
7
+ import type { ReactNode } from 'react';
8
+ import type { AuthConfig } from '@djangocfg/api/auth';
9
+
10
+ // Import provider configs from their modules
11
+ import type { AnalyticsConfig } from '../../snippets/Analytics/types';
12
+ import type { PwaInstallConfig } from '../../snippets/PWAInstall/types';
13
+ import type { PushNotificationsConfig } from '../../snippets/PushNotifications/types';
14
+ import type { ErrorBoundaryConfig, ErrorTrackingConfig } from '../../components/errors/types';
15
+
16
+ // Import local provider configs
17
+ import type { ThemeConfig, SWRConfigOptions, McpChatConfig, CentrifugoConfig } from './providers.types';
18
+
19
+ // ============================================================================
20
+ // Base Layout Props
21
+ // ============================================================================
22
+
23
+ /**
24
+ * Base Layout Props
25
+ *
26
+ * Core configuration for BaseApp component.
27
+ * All provider configs are imported from their respective modules.
28
+ */
29
+ export interface BaseLayoutProps {
30
+ children: ReactNode;
31
+
32
+ /** Theme configuration */
33
+ theme?: ThemeConfig;
34
+
35
+ /** Auth configuration */
36
+ auth?: AuthConfig;
37
+
38
+ /** Analytics configuration (from snippets/Analytics) */
39
+ analytics?: AnalyticsConfig;
40
+
41
+ /** Centrifugo configuration */
42
+ centrifugo?: CentrifugoConfig;
43
+
44
+ /** Error tracking configuration (from components/errors) */
45
+ errorTracking?: ErrorTrackingConfig;
46
+
47
+ /** SWR configuration */
48
+ swr?: SWRConfigOptions;
49
+
50
+ /** Error boundary configuration (from components/errors, enabled by default) */
51
+ errorBoundary?: ErrorBoundaryConfig;
52
+
53
+ /** MCP chat configuration */
54
+ mcpChat?: McpChatConfig;
55
+
56
+ /** PWA Install configuration (from snippets/PWAInstall) */
57
+ pwaInstall?: PwaInstallConfig;
58
+
59
+ /** Push Notifications configuration (from snippets/PushNotifications) */
60
+ pushNotifications?: PushNotificationsConfig;
61
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Provider Configuration Types
3
+ *
4
+ * Configuration types for layout providers (theme, SWR, MCP chat, Centrifugo)
5
+ * Note: Analytics, PWA, Push, and Error types are defined in their respective modules
6
+ */
7
+
8
+ // ============================================================================
9
+ // Theme Configuration
10
+ // ============================================================================
11
+
12
+ export interface ThemeConfig {
13
+ defaultTheme?: 'light' | 'dark' | 'system';
14
+ storageKey?: string;
15
+ }
16
+
17
+ // ============================================================================
18
+ // SWR Configuration
19
+ // ============================================================================
20
+
21
+ export interface SWRConfigOptions {
22
+ revalidateOnFocus?: boolean;
23
+ revalidateOnReconnect?: boolean;
24
+ dedupingInterval?: number;
25
+ }
26
+
27
+ // ============================================================================
28
+ // MCP Chat Configuration
29
+ // ============================================================================
30
+
31
+ export interface McpChatConfig {
32
+ /** Enable MCP chat widget */
33
+ enabled?: boolean;
34
+ /** API endpoint for chat */
35
+ apiEndpoint?: string;
36
+ /** Chat widget title */
37
+ title?: string;
38
+ /** Input placeholder */
39
+ placeholder?: string;
40
+ /** Greeting message */
41
+ greeting?: string;
42
+ /** Widget position */
43
+ position?: 'bottom-right' | 'bottom-left';
44
+ /** Widget variant */
45
+ variant?: 'default' | 'minimal';
46
+ /** Enable streaming responses */
47
+ enableStreaming?: boolean;
48
+ /** Auto-detect environment (dev/prod) */
49
+ autoDetectEnvironment?: boolean;
50
+ /** Custom class name */
51
+ className?: string;
52
+ }
53
+
54
+ // ============================================================================
55
+ // Centrifugo Configuration
56
+ // ============================================================================
57
+
58
+ export interface CentrifugoConfig {
59
+ /** Enable Centrifugo (default: true) */
60
+ enabled?: boolean;
61
+ /** Auto-connect on mount (default: false) */
62
+ autoConnect?: boolean;
63
+ /** Centrifugo server URL */
64
+ url?: string;
65
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * UI Component Types
3
+ *
4
+ * Types for layout UI components (navigation, footer, user menu)
5
+ */
6
+
7
+ import type { LucideIcon } from 'lucide-react';
8
+
9
+ // ============================================================================
10
+ // Navigation Types
11
+ // ============================================================================
12
+
13
+ export interface NavigationItem {
14
+ label: string;
15
+ href: string;
16
+ icon?: LucideIcon | string;
17
+ badge?: string | number;
18
+ external?: boolean;
19
+ }
20
+
21
+ export interface NavigationSection {
22
+ title?: string;
23
+ items: NavigationItem[];
24
+ }
25
+
26
+ // ============================================================================
27
+ // Footer Types
28
+ // ============================================================================
29
+
30
+ export interface FooterLink {
31
+ label: string;
32
+ path: string;
33
+ external?: boolean;
34
+ }
35
+
36
+ export interface FooterMenuSection {
37
+ title: string;
38
+ items: FooterLink[];
39
+ }
40
+
41
+ export interface FooterSocialLinks {
42
+ github?: string;
43
+ linkedin?: string;
44
+ twitter?: string;
45
+ telegram?: string;
46
+ youtube?: string;
47
+ facebook?: string;
48
+ instagram?: string;
49
+ whatsapp?: string;
50
+ email?: string;
51
+ }
52
+
53
+ export interface FooterConfig {
54
+ siteName?: string;
55
+ description?: string;
56
+ logo?: string;
57
+ badge?: {
58
+ icon: LucideIcon;
59
+ text: string;
60
+ };
61
+ socialLinks?: FooterSocialLinks;
62
+ links?: FooterLink[];
63
+ menuSections?: FooterMenuSection[];
64
+ copyright?: string;
65
+ credits?: {
66
+ text: string;
67
+ url?: string;
68
+ };
69
+ variant?: 'full' | 'simple';
70
+ }
71
+
72
+ // ============================================================================
73
+ // User Menu Types
74
+ // ============================================================================
75
+
76
+ export interface UserMenuItem {
77
+ /** Menu item label */
78
+ label: string;
79
+ /** Link href (optional if onClick is provided) */
80
+ href?: string;
81
+ /** Icon component */
82
+ icon?: LucideIcon;
83
+ /** Visual variant */
84
+ variant?: 'default' | 'destructive';
85
+ /** Click handler (optional if href is provided) */
86
+ onClick?: () => void;
87
+ /** Open link in new tab */
88
+ external?: boolean;
89
+ }
90
+
91
+ export interface UserMenuGroup {
92
+ /** Optional group title (renders DropdownMenuLabel) */
93
+ title?: string;
94
+ /** Menu items in this group */
95
+ items: UserMenuItem[];
96
+ }
97
+
98
+ export interface UserMenuConfig {
99
+ /** Menu groups for authenticated users */
100
+ groups?: UserMenuGroup[];
101
+ /** Auth page path (for sign in button) */
102
+ authPath?: string;
103
+ }
@@ -8,3 +8,4 @@ export { useAnalytics, Analytics } from './useAnalytics';
8
8
  export { AnalyticsProvider } from './AnalyticsProvider';
9
9
  export { AnalyticsEvent, AnalyticsCategory } from './events';
10
10
  export type { AnalyticsEventType, AnalyticsCategoryType } from './events';
11
+ export type { AnalyticsConfig } from './types';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Analytics Configuration Types
3
+ *
4
+ * Configuration for Google Analytics integration
5
+ */
6
+
7
+ export interface AnalyticsConfig {
8
+ /** Google Analytics tracking ID (e.g., 'G-XXXXXXXXXX') */
9
+ googleTrackingId?: string;
10
+ }
@@ -98,6 +98,7 @@ export function ChatProvider({
98
98
  const [error, setError] = useState<Error | null>(null);
99
99
  const [isMinimized, setIsMinimized] = useState(false);
100
100
  const isHydratedRef = useRef(false);
101
+ const mobileResetRef = useRef(false);
101
102
 
102
103
  // Display mode with localStorage persistence
103
104
  const [storedMode, setStoredMode] = useLocalStorage<ChatDisplayMode>(storageKeys.mode, 'closed');
@@ -110,6 +111,14 @@ export function ChatProvider({
110
111
 
111
112
  const isMobile = useIsMobile();
112
113
 
114
+ // On mobile, always start closed on page load (don't restore state)
115
+ useEffect(() => {
116
+ if (isMobile && !mobileResetRef.current && storedMode !== 'closed') {
117
+ mobileResetRef.current = true;
118
+ setStoredMode('closed');
119
+ }
120
+ }, [isMobile, storedMode, setStoredMode]);
121
+
113
122
  // Generate user ID if not exists
114
123
  useEffect(() => {
115
124
  if (!userId) {