@djangocfg/layouts 1.4.27 → 1.4.29

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 (118) hide show
  1. package/package.json +8 -8
  2. package/src/auth/context/AuthContext.tsx +4 -1
  3. package/src/auth/hooks/index.ts +2 -0
  4. package/src/auth/hooks/useAuthForm.ts +2 -0
  5. package/src/auth/hooks/useAuthGuard.ts +2 -0
  6. package/src/auth/hooks/useAutoAuth.ts +16 -11
  7. package/src/auth/hooks/useLocalStorage.ts +2 -0
  8. package/src/auth/hooks/useProfileCache.ts +2 -0
  9. package/src/auth/hooks/useSessionStorage.ts +2 -0
  10. package/src/auth/middlewares/index.ts +1 -1
  11. package/src/auth/middlewares/proxy.ts +10 -2
  12. package/src/layouts/AppLayout/AppLayout.tsx +9 -7
  13. package/src/layouts/AppLayout/components/ErrorBoundary.tsx +6 -3
  14. package/src/layouts/AppLayout/components/PageProgress.tsx +2 -0
  15. package/src/layouts/AppLayout/components/Seo.tsx +2 -0
  16. package/src/layouts/AppLayout/components/UpdateNotifier/UpdateNotifier.tsx +3 -2
  17. package/src/layouts/AppLayout/hooks/index.ts +2 -0
  18. package/src/layouts/AppLayout/hooks/useNavigation.ts +3 -1
  19. package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +1 -0
  20. package/src/layouts/AppLayout/layouts/AuthLayout/AuthContext.tsx +2 -0
  21. package/src/layouts/AppLayout/layouts/AuthLayout/IdentifierForm.tsx +2 -0
  22. package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +4 -0
  23. package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +2 -0
  24. package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +1 -0
  25. package/src/layouts/AppLayout/providers/CoreProviders.tsx +1 -0
  26. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +1 -0
  27. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +1 -0
  28. package/src/layouts/PaymentsLayout/events.ts +2 -0
  29. package/src/layouts/ProfileLayout/ProfileLayout.tsx +1 -0
  30. package/src/layouts/ProfileLayout/components/ProfileForm.tsx +1 -0
  31. package/src/layouts/SimpleLayout/SimpleLayout.tsx +72 -0
  32. package/src/layouts/SimpleLayout/index.ts +3 -0
  33. package/src/layouts/SupportLayout/SupportLayout.tsx +1 -0
  34. package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +1 -0
  35. package/src/layouts/SupportLayout/components/TicketList.tsx +6 -5
  36. package/src/layouts/SupportLayout/events.ts +2 -0
  37. package/src/layouts/index.ts +1 -3
  38. package/src/snippets/AuthDialog/useAuthDialog.ts +2 -0
  39. package/src/snippets/Chat/components/MessageList.tsx +12 -11
  40. package/src/snippets/Chat/index.tsx +1 -0
  41. package/src/snippets/ContactForm/ContactForm.tsx +7 -2
  42. package/src/snippets/ContactForm/ContactPage.tsx +16 -3
  43. package/src/snippets/VideoPlayer/README.md +35 -0
  44. package/src/snippets/VideoPlayer/VideoControls.tsx +13 -9
  45. package/src/snippets/VideoPlayer/VideoPlayer.tsx +159 -25
  46. package/src/snippets/VideoPlayer/index.ts +1 -1
  47. package/src/validation/utils/curl-generator.ts +5 -1
  48. package/src/layouts/UILayout/README.md +0 -267
  49. package/src/layouts/UILayout/SUMMARY.md +0 -298
  50. package/src/layouts/UILayout/TOOLS_INTEGRATION.md +0 -216
  51. package/src/layouts/UILayout/components/AutoComponentDemo.tsx +0 -77
  52. package/src/layouts/UILayout/components/CategoryRenderer.tsx +0 -45
  53. package/src/layouts/UILayout/components/TailwindGuideRenderer.tsx +0 -138
  54. package/src/layouts/UILayout/components/index.ts +0 -15
  55. package/src/layouts/UILayout/components/layout/Header/CopyAIButton.tsx +0 -58
  56. package/src/layouts/UILayout/components/layout/Header/Header.tsx +0 -60
  57. package/src/layouts/UILayout/components/layout/Header/HeaderDesktop.tsx +0 -51
  58. package/src/layouts/UILayout/components/layout/Header/HeaderMobile.tsx +0 -71
  59. package/src/layouts/UILayout/components/layout/Header/TestValidationButton.tsx +0 -268
  60. package/src/layouts/UILayout/components/layout/Header/index.ts +0 -11
  61. package/src/layouts/UILayout/components/layout/MobileOverlay/MobileOverlay.tsx +0 -47
  62. package/src/layouts/UILayout/components/layout/MobileOverlay/index.ts +0 -6
  63. package/src/layouts/UILayout/components/layout/Sidebar/Sidebar.tsx +0 -95
  64. package/src/layouts/UILayout/components/layout/Sidebar/SidebarCategory.tsx +0 -54
  65. package/src/layouts/UILayout/components/layout/Sidebar/SidebarContent.tsx +0 -93
  66. package/src/layouts/UILayout/components/layout/Sidebar/SidebarFooter.tsx +0 -49
  67. package/src/layouts/UILayout/components/layout/Sidebar/index.ts +0 -9
  68. package/src/layouts/UILayout/components/layout/index.ts +0 -8
  69. package/src/layouts/UILayout/components/shared/Badge/CountBadge.tsx +0 -38
  70. package/src/layouts/UILayout/components/shared/Badge/index.ts +0 -5
  71. package/src/layouts/UILayout/components/shared/CodeBlock/CodeBlock.tsx +0 -48
  72. package/src/layouts/UILayout/components/shared/CodeBlock/CopyButton.tsx +0 -49
  73. package/src/layouts/UILayout/components/shared/CodeBlock/index.ts +0 -6
  74. package/src/layouts/UILayout/components/shared/Section/Section.tsx +0 -63
  75. package/src/layouts/UILayout/components/shared/Section/index.ts +0 -5
  76. package/src/layouts/UILayout/components/shared/index.ts +0 -8
  77. package/src/layouts/UILayout/config/ai-export.config.ts +0 -89
  78. package/src/layouts/UILayout/config/categories.config.tsx +0 -122
  79. package/src/layouts/UILayout/config/components/blocks.config.tsx +0 -239
  80. package/src/layouts/UILayout/config/components/data.config.tsx +0 -433
  81. package/src/layouts/UILayout/config/components/feedback.config.tsx +0 -290
  82. package/src/layouts/UILayout/config/components/forms.config.tsx +0 -996
  83. package/src/layouts/UILayout/config/components/hooks.config.tsx +0 -168
  84. package/src/layouts/UILayout/config/components/index.ts +0 -72
  85. package/src/layouts/UILayout/config/components/layout.config.tsx +0 -246
  86. package/src/layouts/UILayout/config/components/navigation.config.tsx +0 -352
  87. package/src/layouts/UILayout/config/components/overlay.config.tsx +0 -569
  88. package/src/layouts/UILayout/config/components/specialized.config.tsx +0 -400
  89. package/src/layouts/UILayout/config/components/tools.config.tsx +0 -234
  90. package/src/layouts/UILayout/config/components/types.ts +0 -14
  91. package/src/layouts/UILayout/config/index.ts +0 -42
  92. package/src/layouts/UILayout/config/tailwind.config.ts +0 -131
  93. package/src/layouts/UILayout/constants.ts +0 -23
  94. package/src/layouts/UILayout/context/ShowcaseContext.tsx +0 -81
  95. package/src/layouts/UILayout/context/index.ts +0 -1
  96. package/src/layouts/UILayout/core/UIGuideApp.client.tsx +0 -18
  97. package/src/layouts/UILayout/core/UIGuideApp.tsx +0 -33
  98. package/src/layouts/UILayout/core/UIGuideLanding.tsx +0 -172
  99. package/src/layouts/UILayout/core/UIGuideView.tsx +0 -61
  100. package/src/layouts/UILayout/core/UILayout.tsx +0 -125
  101. package/src/layouts/UILayout/core/UILayoutSidebar.tsx +0 -11
  102. package/src/layouts/UILayout/core/index.ts +0 -10
  103. package/src/layouts/UILayout/hooks/index.ts +0 -9
  104. package/src/layouts/UILayout/hooks/useAIExport.ts +0 -78
  105. package/src/layouts/UILayout/hooks/useCategoryNavigation.ts +0 -92
  106. package/src/layouts/UILayout/hooks/useComponentSearch.ts +0 -81
  107. package/src/layouts/UILayout/hooks/useSidebarState.ts +0 -36
  108. package/src/layouts/UILayout/index.ts +0 -160
  109. package/src/layouts/UILayout/types/component.ts +0 -45
  110. package/src/layouts/UILayout/types/index.ts +0 -23
  111. package/src/layouts/UILayout/types/layout.ts +0 -57
  112. package/src/layouts/UILayout/types/navigation.ts +0 -33
  113. package/src/layouts/UILayout/utils/ai-export/formatters.ts +0 -71
  114. package/src/layouts/UILayout/utils/ai-export/index.ts +0 -5
  115. package/src/layouts/UILayout/utils/component-helpers/filter.ts +0 -109
  116. package/src/layouts/UILayout/utils/component-helpers/index.ts +0 -6
  117. package/src/layouts/UILayout/utils/component-helpers/search.ts +0 -95
  118. package/src/layouts/UILayout/utils/index.ts +0 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "1.4.27",
3
+ "version": "1.4.29",
4
4
  "description": "Pre-built dashboard layouts, authentication pages, and admin templates for Next.js applications with Tailwind CSS",
5
5
  "keywords": [
6
6
  "layouts",
@@ -85,9 +85,9 @@
85
85
  "check": "tsc --noEmit"
86
86
  },
87
87
  "peerDependencies": {
88
- "@djangocfg/api": "^1.4.27",
89
- "@djangocfg/og-image": "^1.4.27",
90
- "@djangocfg/ui": "^1.4.27",
88
+ "@djangocfg/api": "^1.4.29",
89
+ "@djangocfg/og-image": "^1.4.29",
90
+ "@djangocfg/ui": "^1.4.29",
91
91
  "@hookform/resolvers": "^5.2.0",
92
92
  "consola": "^3.4.2",
93
93
  "lucide-react": "^0.468.0",
@@ -103,13 +103,13 @@
103
103
  "zod": "^4.0.10"
104
104
  },
105
105
  "dependencies": {
106
- "@vidstack/react": "^0.6.15",
107
- "maverick.js": "0.37.0",
106
+ "@vidstack/react": "next",
107
+ "media-icons": "next",
108
108
  "react-ga4": "^2.1.0",
109
- "vidstack": "0.6.15"
109
+ "vidstack": "next"
110
110
  },
111
111
  "devDependencies": {
112
- "@djangocfg/typescript-config": "^1.4.27",
112
+ "@djangocfg/typescript-config": "^1.4.29",
113
113
  "@types/node": "^24.7.2",
114
114
  "@types/react": "19.2.2",
115
115
  "@types/react-dom": "19.2.1",
@@ -1,4 +1,7 @@
1
- import { useRouter } from 'next/router';
1
+ // @ts-nocheck
2
+ 'use client';
3
+
4
+ import { useRouter } from 'next/navigation';
2
5
  import React, {
3
6
  createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState
4
7
  } from 'react';
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  export { useAuthRedirectManager } from './useAuthRedirect';
2
4
  export { useAuthGuard } from './useAuthGuard';
3
5
  export { useSessionStorage } from './useSessionStorage';
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  import { useCallback, useEffect, useState } from 'react';
2
4
 
3
5
  import { useAuth } from '../context';
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  import { useRouter } from 'next/router';
2
4
  import { useEffect } from 'react';
3
5
 
@@ -1,5 +1,7 @@
1
- import { useRouter } from 'next/router';
1
+ 'use client';
2
+
2
3
  import { useEffect } from 'react';
4
+ import { usePathname, useRouter, useSearchParams } from 'next/navigation';
3
5
  import { authLogger } from '../../utils/logger';
4
6
 
5
7
  export interface UseAutoAuthOptions {
@@ -13,12 +15,17 @@ export interface UseAutoAuthOptions {
13
15
  */
14
16
  export const useAutoAuth = (options: UseAutoAuthOptions = {}) => {
15
17
  const { onOTPDetected, cleanupUrl = true } = options;
18
+ const searchParams = useSearchParams();
19
+ const pathname = usePathname();
16
20
  const router = useRouter();
17
21
 
22
+ const isReady = !!pathname && !!searchParams.get('otp');
23
+ const hasOTP = !!(searchParams.get('otp'));
24
+
18
25
  useEffect(() => {
19
- if (!router.isReady) return;
26
+ if (!isReady) return;
20
27
 
21
- const queryOtp = router.query.otp as string;
28
+ const queryOtp = searchParams.get('otp') as string;
22
29
 
23
30
  // Handle OTP detection
24
31
  if (queryOtp && typeof queryOtp === 'string' && queryOtp.length === 6) {
@@ -28,16 +35,14 @@ export const useAutoAuth = (options: UseAutoAuthOptions = {}) => {
28
35
 
29
36
  // Clean up URL to remove sensitive params for security
30
37
  if (cleanupUrl && queryOtp) {
31
- const { otp: _, ...cleanQuery } = router.query;
32
- router.replace({
33
- pathname: router.pathname,
34
- query: cleanQuery
35
- }, undefined, { shallow: true });
38
+ const cleanQuery = Object.fromEntries(searchParams.entries());
39
+ delete cleanQuery.otp;
40
+ router.push(`${pathname}?${new URLSearchParams(cleanQuery).toString()}`);
36
41
  }
37
- }, [router.isReady, router.query, router.pathname, onOTPDetected, cleanupUrl]);
42
+ }, [pathname, searchParams, onOTPDetected, cleanupUrl]);
38
43
 
39
44
  return {
40
- isReady: router.isReady,
41
- hasOTP: !!(router.query.otp),
45
+ isReady,
46
+ hasOTP,
42
47
  };
43
48
  };
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  import { useEffect, useState } from 'react';
2
4
  import { authLogger } from '../../utils/logger';
3
5
 
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  /**
2
4
  * Profile Cache Hook
3
5
  *
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  import { useState } from 'react';
2
4
  import { authLogger } from '../../utils/logger';
3
5
 
@@ -1 +1 @@
1
- export { middleware, config } from './proxy';
1
+ export { proxyMiddleware, proxyMiddlewareConfig } from './proxy';
@@ -1,6 +1,10 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
2
 
3
- export function middleware(request: NextRequest) {
3
+ /**
4
+ * Proxy middleware for media and API endpoints
5
+ * Use this in your middleware.ts file
6
+ */
7
+ export function proxyMiddleware(request: NextRequest) {
4
8
  const { pathname, search } = request.nextUrl;
5
9
  const apiUrl = process.env.NEXT_PUBLIC_API_URL;
6
10
 
@@ -19,6 +23,10 @@ export function middleware(request: NextRequest) {
19
23
  return NextResponse.next();
20
24
  }
21
25
 
22
- export const config = {
26
+ /**
27
+ * Recommended matcher config for proxy middleware
28
+ * Add this to your middleware.ts config
29
+ */
30
+ export const proxyMiddlewareConfig = {
23
31
  matcher: ['/media/:path*', '/api/:path*'],
24
32
  };
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * AppLayout - Unified Application Layout System
3
4
  *
@@ -342,13 +343,14 @@ export function AppLayout({ children, config, component, pageProps, fontFamily,
342
343
  // Wrap with ErrorBoundary if enabled
343
344
  if (enableErrorBoundary) {
344
345
  return (
345
- <ErrorBoundary
346
- key={router.pathname}
347
- supportEmail={supportEmail}
348
- onError={onError}
349
- >
350
- {content}
351
- </ErrorBoundary>
346
+ <React.Fragment key={router.pathname}>
347
+ <ErrorBoundary
348
+ supportEmail={supportEmail}
349
+ onError={onError}
350
+ >
351
+ {content}
352
+ </ErrorBoundary>
353
+ </React.Fragment>
352
354
  );
353
355
  }
354
356
 
@@ -37,6 +37,9 @@ interface ErrorBoundaryState {
37
37
  * Catches React errors and shows ErrorLayout
38
38
  */
39
39
  export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
40
+ declare state: ErrorBoundaryState;
41
+ declare props: Readonly<ErrorBoundaryProps>;
42
+
40
43
  constructor(props: ErrorBoundaryProps) {
41
44
  super(props);
42
45
  this.state = { hasError: false };
@@ -62,12 +65,12 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
62
65
  // Call optional callback
63
66
  this.props.onError?.(error, errorInfo);
64
67
 
65
- // Store error info in state
66
- this.setState({ errorInfo });
68
+ // Store error info in state using base class method
69
+ Component.prototype.setState.call(this, { errorInfo });
67
70
  }
68
71
 
69
72
  resetError = () => {
70
- this.setState({ hasError: false, error: undefined, errorInfo: undefined });
73
+ Component.prototype.setState.call(this, { hasError: false, error: undefined, errorInfo: undefined });
71
74
  };
72
75
 
73
76
  render() {
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  import { useRouter } from 'next/router';
2
4
  import { useEffect, useRef, useState } from 'react';
3
5
 
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  import { Fragment } from 'react';
2
4
  import Head from 'next/head';
3
5
  import { useRouter } from 'next/router';
@@ -9,6 +9,7 @@
9
9
  'use client';
10
10
 
11
11
  import React, { useEffect, useState } from 'react';
12
+ import consola from 'consola';
12
13
  import { toast } from '@djangocfg/ui/hooks';
13
14
 
14
15
  const PACKAGE_NAME = '@djangocfg/layouts';
@@ -63,7 +64,7 @@ async function fetchLatestVersion(): Promise<string | null> {
63
64
  const data = await response.json();
64
65
  return data.version || null;
65
66
  } catch (error) {
66
- console.warn('[UpdateNotifier] Failed to check for updates:', error);
67
+ consola.warn('[UpdateNotifier] Failed to check for updates:', error);
67
68
  return null;
68
69
  }
69
70
  }
@@ -94,7 +95,7 @@ function setCache(data: UpdateCheckCache): void {
94
95
  try {
95
96
  localStorage.setItem(CACHE_KEY, JSON.stringify(data));
96
97
  } catch (error) {
97
- console.warn('[UpdateNotifier] Failed to cache update check:', error);
98
+ consola.warn('[UpdateNotifier] Failed to cache update check:', error);
98
99
  }
99
100
  }
100
101
 
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  /**
2
4
  * Hooks Module
3
5
  */
@@ -4,7 +4,9 @@
4
4
  * Navigation utilities
5
5
  */
6
6
 
7
- import { useRouter } from 'next/router';
7
+ 'use client';
8
+
9
+ import { useRouter } from 'next/navigation';
8
10
  import { useAppContext } from '../context';
9
11
 
10
12
  export interface UseNavigationReturn {
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  // ============================================================================
2
3
  // AdminLayout - Django CFG Layout with iframe Integration
3
4
  // ============================================================================
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  import React, { createContext, useContext } from 'react';
2
4
 
3
5
  import { useAuthForm } from '../../../../auth/hooks';
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  import React, { useState, useEffect } from 'react';
2
4
  import { Mail, Phone, User, Send } from 'lucide-react';
3
5
 
@@ -1,3 +1,6 @@
1
+ "use client"
2
+
3
+ // @ts-nocheck
1
4
  import React from 'react';
2
5
  import { Mail, MessageCircle, ArrowLeft, RotateCw, ShieldCheck } from 'lucide-react';
3
6
 
@@ -70,6 +73,7 @@ export const OTPForm: React.FC = () => {
70
73
  Enter verification code
71
74
  </label>
72
75
  <div className="flex justify-center">
76
+ {/* @ts-expect-error - TypeScript doesn't recognize children in JSX props for discriminated union */}
73
77
  <InputOTP
74
78
  value={otp}
75
79
  onChange={setOtp}
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  export { AuthLayout } from './AuthLayout';
2
4
  export {
3
5
  AuthProvider as AuthLayoutProvider,
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * Private Layout
3
4
  *
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * Core Providers
3
4
  *
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * Payments Layout (v2.0 - Simplified)
3
4
  *
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * Create Payment Dialog (v2.0 - Simplified)
3
4
  * Dialog for creating new payments
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  /**
2
4
  * Payment Events System (v2.0 - Simplified)
3
5
  *
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  'use client';
2
3
 
3
4
  import React from 'react';
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  'use client';
2
3
 
3
4
  import React, { useEffect, useState } from 'react';
@@ -0,0 +1,72 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * SimpleLayout - Lightweight provider for docs and marketing sites
4
+ *
5
+ * Provides essential UI infrastructure without the overhead of full AppLayout:
6
+ * - TooltipProvider for all tooltip components
7
+ * - Toaster for notifications
8
+ * - Basic theme support
9
+ *
10
+ * Perfect for documentation sites, landing pages, and simple apps.
11
+ */
12
+
13
+ 'use client';
14
+
15
+ import React from 'react';
16
+ import { TooltipProvider, Toaster } from '@djangocfg/ui';
17
+
18
+ export interface SimpleLayoutProps {
19
+ children: React.ReactNode;
20
+ /**
21
+ * Delay before tooltips appear (in milliseconds)
22
+ * @default 200
23
+ */
24
+ tooltipDelayDuration?: number;
25
+ /**
26
+ * Skip delay when moving between tooltips
27
+ * @default 300
28
+ */
29
+ tooltipSkipDelayDuration?: number;
30
+ }
31
+
32
+ /**
33
+ * Lightweight layout provider for documentation and marketing sites.
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * // In your root layout.tsx
38
+ * import { SimpleLayout } from '@djangocfg/layouts';
39
+ *
40
+ * export default function RootLayout({ children }) {
41
+ * return (
42
+ * <html>
43
+ * <body>
44
+ * <SimpleLayout>
45
+ * {children}
46
+ * </SimpleLayout>
47
+ * </body>
48
+ * </html>
49
+ * );
50
+ * }
51
+ * ```
52
+ */
53
+ export function SimpleLayout({
54
+ children,
55
+ tooltipDelayDuration = 200,
56
+ tooltipSkipDelayDuration = 300,
57
+ }: SimpleLayoutProps) {
58
+ return (
59
+ <>
60
+ <TooltipProvider
61
+ delayDuration={tooltipDelayDuration}
62
+ skipDelayDuration={tooltipSkipDelayDuration}
63
+ >
64
+ {children}
65
+ </TooltipProvider>
66
+ <Toaster />
67
+ </>
68
+ );
69
+ }
70
+
71
+ SimpleLayout.displayName = 'SimpleLayout';
72
+
@@ -0,0 +1,3 @@
1
+ export { SimpleLayout } from './SimpleLayout';
2
+ export type { SimpleLayoutProps } from './SimpleLayout';
3
+
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * Support Layout
3
4
  * Modern support layout with resizable panels for desktop and mobile-optimized view
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * Create Ticket Dialog
3
4
  * Dialog for creating new support tickets
@@ -73,11 +73,12 @@ export const TicketList: React.FC = () => {
73
73
  return (
74
74
  <div className="p-4 space-y-2">
75
75
  {[1, 2, 3, 4, 5].map((i) => (
76
- <Skeleton
77
- key={i}
78
- className="h-24 w-full animate-pulse"
79
- style={{ animationDelay: `${i * 100}ms` }}
80
- />
76
+ <div key={i}>
77
+ <Skeleton
78
+ className="h-24 w-full animate-pulse"
79
+ style={{ animationDelay: `${i * 100}ms` }}
80
+ />
81
+ </div>
81
82
  ))}
82
83
  </div>
83
84
  );
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  /**
2
4
  * Support Layout Events
3
5
  * Event system for SupportLayout
@@ -8,9 +8,7 @@ export * from './SupportLayout';
8
8
  export * from './PaymentsLayout';
9
9
  export * from './AppLayout';
10
10
  export * from './ErrorLayout';
11
-
12
- // UILayout - Config-driven UI documentation layout
13
- export * from './UILayout';
11
+ export * from './SimpleLayout';
14
12
 
15
13
  // Note: CfgLayout is now part of AppLayout exports
16
14
  // Import it via: import { CfgLayout, useCfgApp } from '@djangocfg/layouts';
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  import { useCallback } from 'react';
2
4
 
3
5
  import { AUTH_EVENTS, type OpenAuthDialogPayload } from './events';
@@ -111,17 +111,18 @@ export const MessageList: React.FC<MessageListProps> = ({
111
111
  message.sources.length > 0 && (
112
112
  <div className="flex flex-wrap gap-2 px-1 animate-in fade-in slide-in-from-left-2 duration-300 delay-100">
113
113
  {message.sources.map((source, idx) => (
114
- <Badge
115
- key={idx}
116
- variant="secondary"
117
- className="text-xs flex items-center gap-1 cursor-pointer
118
- hover:bg-secondary/80 hover:scale-105 active:scale-95
119
- transition-all duration-200 animate-in fade-in zoom-in-95"
120
- style={{ animationDelay: `${(idx + 1) * 100}ms` }}
121
- >
122
- {source.document_title || `Source ${idx + 1}`}
123
- <ExternalLink className="h-3 w-3" />
124
- </Badge>
114
+ <div key={idx}>
115
+ <Badge
116
+ variant="secondary"
117
+ className="text-xs flex items-center gap-1 cursor-pointer
118
+ hover:bg-secondary/80 hover:scale-105 active:scale-95
119
+ transition-all duration-200 animate-in fade-in zoom-in-95"
120
+ style={{ animationDelay: `${(idx + 1) * 100}ms` }}
121
+ >
122
+ {source.document_title || `Source ${idx + 1}`}
123
+ <ExternalLink className="h-3 w-3" />
124
+ </Badge>
125
+ </div>
125
126
  ))}
126
127
  </div>
127
128
  )}
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * Knowledge Chat Module
3
4
  * Complete RAG-powered chat widget with context providers
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  'use client';
2
3
 
3
4
  import React, { useEffect, useState } from 'react';
@@ -131,12 +132,13 @@ function ContactFormInner({
131
132
  // Apply draft from localStorage and set site_url
132
133
  const currentValues = form.getValues();
133
134
  const hasDraftData = draft.name || draft.email || draft.company || draft.subject || draft.message;
135
+ const currentSiteUrl = typeof window !== 'undefined' ? window.location.href : '';
134
136
 
135
137
  if (hasDraftData || !currentValues.site_url) {
136
138
  form.reset({
137
139
  ...emptyDraft,
138
140
  ...draft,
139
- site_url: window.location.href,
141
+ site_url: currentSiteUrl,
140
142
  });
141
143
  }
142
144
  }, [draft, form, isHydrated]);
@@ -165,14 +167,17 @@ function ContactFormInner({
165
167
  const result = await submit(data);
166
168
  if (resetOnSuccess) {
167
169
  // Keep contact info (name, email, company), clear only message content
170
+ // Store current site_url before reset to avoid re-reading window.location
168
171
  const currentValues = form.getValues();
172
+ const currentSiteUrl = currentValues.site_url || (typeof window !== 'undefined' ? window.location.href : '');
173
+
169
174
  form.reset({
170
175
  name: currentValues.name,
171
176
  email: currentValues.email,
172
177
  company: currentValues.company,
173
178
  subject: '',
174
179
  message: '',
175
- site_url: currentValues.site_url,
180
+ site_url: currentSiteUrl, // Keep the original site_url
176
181
  });
177
182
  // Update draft to keep contact info
178
183
  setDraft({
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import React from 'react';
4
- import { Mail, MapPin, Calendar } from 'lucide-react';
4
+ import { Mail, MapPin, Calendar, MessageCircle } from 'lucide-react';
5
5
  import { ContactFormBase as ContactForm } from './ContactForm';
6
6
  import { ContactInfo } from './ContactInfo';
7
7
  import type { ContactDetail, LeadSubmissionResult } from './types';
@@ -15,6 +15,7 @@ const isDev = process.env.NODE_ENV === 'development';
15
15
  const DEFAULT_CONFIG = {
16
16
  apiUrl: isDev ? 'http://localhost:8000' : 'https://api.reforms.ai',
17
17
  email: 'markolofsen@gmail.com',
18
+ whatsapp: '+62 813 39646301',
18
19
  calendly: 'https://calendly.com/markolofsen/meeting',
19
20
  };
20
21
 
@@ -27,6 +28,8 @@ export interface ContactPageProps {
27
28
  apiUrl?: string;
28
29
  /** Override email */
29
30
  email?: string;
31
+ /** Override whatsapp */
32
+ whatsapp?: string;
30
33
  /** Override calendly link */
31
34
  calendlyUrl?: string;
32
35
  /** Page title */
@@ -61,6 +64,7 @@ export interface ContactPageProps {
61
64
  export function ContactPageBase({
62
65
  apiUrl = DEFAULT_CONFIG.apiUrl,
63
66
  email = DEFAULT_CONFIG.email,
67
+ whatsapp = DEFAULT_CONFIG.whatsapp,
64
68
  calendlyUrl = DEFAULT_CONFIG.calendly,
65
69
  title = 'Get in Touch',
66
70
  subtitle = "Have a question or want to work together? We'd love to hear from you.",
@@ -69,6 +73,9 @@ export function ContactPageBase({
69
73
  className,
70
74
  onSuccess,
71
75
  }: ContactPageProps) {
76
+ // Format phone for WhatsApp link (remove spaces and special chars)
77
+ const whatsappPhone = whatsapp.replace(/[\s\-\(\)]/g, '');
78
+
72
79
  const contactDetails: ContactDetail[] = [
73
80
  {
74
81
  icon: <Mail className="h-5 w-5" />,
@@ -76,6 +83,12 @@ export function ContactPageBase({
76
83
  value: email,
77
84
  href: `mailto:${email}`,
78
85
  },
86
+ {
87
+ icon: <MessageCircle className="h-5 w-5" />,
88
+ label: 'WhatsApp',
89
+ value: whatsapp,
90
+ href: `https://wa.me/${whatsappPhone}`,
91
+ },
79
92
  {
80
93
  icon: <MapPin className="h-5 w-5" />,
81
94
  label: 'Location',
@@ -96,8 +109,8 @@ export function ContactPageBase({
96
109
  </div>
97
110
 
98
111
  {/* Content Grid */}
99
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
100
- <div className="lg:col-span-2">
112
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 w-full">
113
+ <div>
101
114
  <ContactForm apiUrl={apiUrl} onSuccess={onSuccess} />
102
115
  </div>
103
116
  <div>