@djangocfg/layouts 1.4.30 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +277 -18
- package/package.json +15 -24
- package/src/auth/context/AuthContext.tsx +5 -5
- package/src/auth/hooks/useAuthGuard.ts +1 -1
- package/src/auth/hooks/useAutoAuth.ts +8 -7
- package/src/components/ErrorBoundary.tsx +78 -0
- package/src/components/JsonLd.tsx +31 -0
- package/src/components/LucideIcon.tsx +91 -0
- package/src/components/PageProgress.tsx +127 -0
- package/src/components/Suspense.tsx +29 -0
- package/src/{layouts/AppLayout/components → components}/UpdateNotifier/UpdateNotifier.tsx +56 -49
- package/src/components/index.ts +10 -0
- package/src/index.ts +25 -7
- package/src/layouts/AdminLayout/AdminLayout.tsx +46 -0
- package/src/layouts/AdminLayout/index.ts +7 -0
- package/src/layouts/AppLayout/AppLayout.tsx +278 -326
- package/src/layouts/AppLayout/index.ts +2 -39
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthContext.tsx +3 -2
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthHelp.tsx +1 -0
- package/src/layouts/AuthLayout/AuthLayout.tsx +61 -0
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/IdentifierForm.tsx +47 -34
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/OTPForm.tsx +2 -3
- package/src/layouts/AuthLayout/index.ts +24 -0
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/types.ts +1 -0
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +144 -0
- package/src/layouts/PrivateLayout/components/PrivateContent.tsx +32 -0
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +57 -0
- package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +141 -0
- package/src/layouts/PrivateLayout/components/index.ts +8 -0
- package/src/layouts/PrivateLayout/index.ts +7 -0
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +15 -7
- package/src/layouts/PublicLayout/PublicLayout.tsx +121 -0
- package/src/layouts/PublicLayout/components/PublicFooter.tsx +190 -0
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +117 -0
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +101 -0
- package/src/layouts/PublicLayout/components/index.ts +8 -0
- package/src/layouts/PublicLayout/index.ts +7 -0
- package/src/layouts/_components/UserMenu.tsx +160 -0
- package/src/layouts/_components/index.ts +7 -0
- package/src/layouts/index.ts +15 -8
- package/src/snippets/Analytics/AnalyticsProvider.tsx +8 -4
- package/src/snippets/Analytics/useAnalytics.ts +11 -21
- package/src/snippets/Chat/ChatWidget.tsx +4 -4
- package/src/snippets/ContactForm/ContactFormProvider.tsx +32 -19
- package/src/snippets/ContactForm/ContactPage.tsx +2 -4
- package/src/snippets/ContactForm/types.ts +3 -2
- package/src/snippets/index.ts +0 -1
- package/src/layouts/AppLayout/README.md +0 -204
- package/src/layouts/AppLayout/SUMMARY.md +0 -240
- package/src/layouts/AppLayout/USAGE.md +0 -312
- package/src/layouts/AppLayout/components/ErrorBoundary.tsx +0 -112
- package/src/layouts/AppLayout/components/PageProgress.tsx +0 -123
- package/src/layouts/AppLayout/components/Seo.tsx +0 -171
- package/src/layouts/AppLayout/components/UserMenu.tsx +0 -385
- package/src/layouts/AppLayout/components/index.ts +0 -11
- package/src/layouts/AppLayout/context/AppContext.tsx +0 -151
- package/src/layouts/AppLayout/context/index.ts +0 -5
- package/src/layouts/AppLayout/hooks/index.ts +0 -8
- package/src/layouts/AppLayout/hooks/useLayoutMode.ts +0 -26
- package/src/layouts/AppLayout/hooks/useNavigation.ts +0 -51
- package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +0 -224
- package/src/layouts/AppLayout/layouts/AdminLayout/README.md +0 -409
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.example.tsx +0 -98
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.tsx +0 -149
- package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +0 -146
- package/src/layouts/AppLayout/layouts/AdminLayout/components/index.ts +0 -3
- package/src/layouts/AppLayout/layouts/AdminLayout/context/CfgAppContext.tsx +0 -48
- package/src/layouts/AppLayout/layouts/AdminLayout/context/index.ts +0 -2
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/index.ts +0 -6
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +0 -279
- package/src/layouts/AppLayout/layouts/AdminLayout/index.ts +0 -24
- package/src/layouts/AppLayout/layouts/AdminLayout/lottie/energizing.json +0 -1
- package/src/layouts/AppLayout/layouts/AdminLayout/types/index.ts +0 -45
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +0 -41
- package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +0 -15
- package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +0 -82
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +0 -62
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +0 -89
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +0 -181
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +0 -9
- package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +0 -5
- package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +0 -44
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +0 -242
- package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileDrawer.tsx +0 -150
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +0 -169
- package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +0 -5
- package/src/layouts/AppLayout/layouts/index.ts +0 -7
- package/src/layouts/AppLayout/providers/CoreProviders.tsx +0 -80
- package/src/layouts/AppLayout/providers/index.ts +0 -5
- package/src/layouts/AppLayout/types/config.ts +0 -79
- package/src/layouts/AppLayout/types/index.ts +0 -11
- package/src/layouts/AppLayout/types/layout.ts +0 -54
- package/src/layouts/AppLayout/types/navigation.ts +0 -43
- package/src/layouts/AppLayout/types/page.ts +0 -80
- package/src/layouts/AppLayout/types/routes.ts +0 -43
- package/src/layouts/AppLayout/utils/index.ts +0 -5
- package/src/layouts/AppLayout/utils/routeDetection.ts +0 -31
- package/src/layouts/ErrorLayout/ErrorLayout.tsx +0 -173
- package/src/layouts/ErrorLayout/errorConfig.tsx +0 -152
- package/src/layouts/ErrorLayout/index.ts +0 -8
- package/src/layouts/SimpleLayout/SimpleLayout.tsx +0 -72
- package/src/layouts/SimpleLayout/index.ts +0 -3
- package/src/snippets/VideoPlayer/README.md +0 -238
- package/src/snippets/VideoPlayer/VideoControls.tsx +0 -137
- package/src/snippets/VideoPlayer/VideoPlayer.tsx +0 -248
- package/src/snippets/VideoPlayer/index.ts +0 -8
- package/src/snippets/VideoPlayer/types.ts +0 -61
- package/src/types/index.ts +0 -2
- package/src/types/pageConfig.ts +0 -100
- /package/src/{validation → components/ErrorsTracker}/README.md +0 -0
- /package/src/{validation → components/ErrorsTracker}/components/ErrorButtons.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/components/ErrorToast.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/hooks.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/index.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/providers/ErrorTrackingProvider.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/types.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/utils/curl-generator.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/utils/formatters.ts +0 -0
- /package/src/{layouts/AppLayout/components → components}/UpdateNotifier/index.ts +0 -0
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration Types
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { ReactNode } from 'react';
|
|
6
|
-
import type { PublicLayoutConfig, PrivateLayoutConfig, AdminLayoutConfig } from './layout';
|
|
7
|
-
import type { RouteConfig } from './routes';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Main AppLayout configuration
|
|
11
|
-
*/
|
|
12
|
-
export interface AppLayoutConfig {
|
|
13
|
-
/** Application metadata */
|
|
14
|
-
app: {
|
|
15
|
-
name: string;
|
|
16
|
-
description?: string;
|
|
17
|
-
logoPath: string;
|
|
18
|
-
siteUrl?: string;
|
|
19
|
-
icons?: {
|
|
20
|
-
logo192?: string;
|
|
21
|
-
logo384?: string;
|
|
22
|
-
logo512?: string;
|
|
23
|
-
logoVector?: string;
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/** Theme configuration */
|
|
28
|
-
theme?: {
|
|
29
|
-
/** Default theme (default: 'light') */
|
|
30
|
-
defaultTheme?: 'light' | 'dark';
|
|
31
|
-
/** Storage key for theme persistence (default: 'theme') */
|
|
32
|
-
storageKey?: string;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/** API configuration */
|
|
36
|
-
api: {
|
|
37
|
-
baseUrl: string;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/** Route configuration */
|
|
41
|
-
routes: RouteConfig;
|
|
42
|
-
|
|
43
|
-
/** Public layout configuration */
|
|
44
|
-
publicLayout: PublicLayoutConfig;
|
|
45
|
-
|
|
46
|
-
/** Private layout configuration */
|
|
47
|
-
privateLayout: PrivateLayoutConfig;
|
|
48
|
-
|
|
49
|
-
/** Admin layout configuration (optional) */
|
|
50
|
-
adminLayout?: AdminLayoutConfig;
|
|
51
|
-
|
|
52
|
-
/** Error handling configuration */
|
|
53
|
-
errors?: {
|
|
54
|
-
/** Enable automatic error boundary (default: true) */
|
|
55
|
-
enableErrorBoundary?: boolean;
|
|
56
|
-
/** Support email for error pages */
|
|
57
|
-
supportEmail?: string;
|
|
58
|
-
/** Custom error handler callback */
|
|
59
|
-
onError?: (error: Error, errorInfo?: React.ErrorInfo) => void;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/** Auth configuration */
|
|
63
|
-
auth?: {
|
|
64
|
-
/** Terms of Service URL */
|
|
65
|
-
termsUrl?: string;
|
|
66
|
-
/** Privacy Policy URL */
|
|
67
|
-
privacyUrl?: string;
|
|
68
|
-
/** Support URL for auth help */
|
|
69
|
-
supportUrl?: string;
|
|
70
|
-
/** Enable phone authentication (default: false) */
|
|
71
|
-
enablePhoneAuth?: boolean;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
/** Analytics configuration */
|
|
75
|
-
analytics?: {
|
|
76
|
-
/** Google Analytics Tracking ID (e.g., 'G-XXXXXXXXXX' or 'UA-XXXXXXXXX-X') */
|
|
77
|
-
googleTrackingId?: string;
|
|
78
|
-
};
|
|
79
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layout Configuration Types
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { ReactNode } from 'react';
|
|
6
|
-
import type { LucideIcon } from 'lucide-react';
|
|
7
|
-
import type { NavigationSection, DashboardMenuGroup } from './navigation';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Public layout configuration
|
|
11
|
-
*/
|
|
12
|
-
export interface PublicLayoutConfig {
|
|
13
|
-
navigation: {
|
|
14
|
-
homePath: string;
|
|
15
|
-
menuSections: NavigationSection[];
|
|
16
|
-
};
|
|
17
|
-
userMenu: {
|
|
18
|
-
dashboardPath?: string;
|
|
19
|
-
profilePath: string;
|
|
20
|
-
};
|
|
21
|
-
footer: {
|
|
22
|
-
badge: {
|
|
23
|
-
icon: LucideIcon;
|
|
24
|
-
text: string;
|
|
25
|
-
};
|
|
26
|
-
links: {
|
|
27
|
-
docs?: string;
|
|
28
|
-
privacy?: string;
|
|
29
|
-
terms?: string;
|
|
30
|
-
security?: string;
|
|
31
|
-
cookies?: string;
|
|
32
|
-
};
|
|
33
|
-
menuSections: NavigationSection[];
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Private/Dashboard layout configuration
|
|
39
|
-
*/
|
|
40
|
-
export interface PrivateLayoutConfig {
|
|
41
|
-
homeHref: string;
|
|
42
|
-
profileHref: string;
|
|
43
|
-
showChat?: boolean;
|
|
44
|
-
menuGroups: DashboardMenuGroup[];
|
|
45
|
-
contentPadding?: 'default' | 'none';
|
|
46
|
-
headerActions?: ReactNode;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Admin layout configuration
|
|
51
|
-
*/
|
|
52
|
-
export interface AdminLayoutConfig {
|
|
53
|
-
menuSections: NavigationSection[];
|
|
54
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Navigation Types
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { LucideIcon } from 'lucide-react';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Navigation menu item
|
|
9
|
-
*/
|
|
10
|
-
export interface NavigationItem {
|
|
11
|
-
label: string;
|
|
12
|
-
path: string;
|
|
13
|
-
icon?: LucideIcon;
|
|
14
|
-
badge?: string | number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Navigation menu section
|
|
19
|
-
*/
|
|
20
|
-
export interface NavigationSection {
|
|
21
|
-
title: string;
|
|
22
|
-
items: NavigationItem[];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Dashboard menu item with icon
|
|
27
|
-
*/
|
|
28
|
-
export interface DashboardMenuItem {
|
|
29
|
-
path: string;
|
|
30
|
-
label: string;
|
|
31
|
-
icon: LucideIcon;
|
|
32
|
-
badge?: string | number;
|
|
33
|
-
subItems?: DashboardMenuItem[]; // Support nested menu items
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Dashboard menu group
|
|
38
|
-
*/
|
|
39
|
-
export interface DashboardMenuGroup {
|
|
40
|
-
label: string;
|
|
41
|
-
order: number;
|
|
42
|
-
items: DashboardMenuItem[];
|
|
43
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Page Type Definitions
|
|
3
|
-
*
|
|
4
|
-
* Universal types for Next.js pages with layout and SEO support
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { ReactElement, ReactNode } from 'react';
|
|
8
|
-
import type { NextPage } from 'next';
|
|
9
|
-
import type { PageConfig } from '../../../types/pageConfig';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Layout mode types
|
|
13
|
-
*
|
|
14
|
-
* - 'public': Public layout (landing, docs, etc.)
|
|
15
|
-
* - 'private': Private layout (dashboard, authenticated pages)
|
|
16
|
-
* - 'auth': Auth layout (login, register, OTP)
|
|
17
|
-
* - 'admin': Admin layout (Django CFG iframe integration)
|
|
18
|
-
* - 'none': No layout (fully custom page)
|
|
19
|
-
*/
|
|
20
|
-
export type LayoutMode = 'public' | 'private' | 'auth' | 'admin' | 'none';
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Universal Page Type - combines layout and SEO configuration
|
|
24
|
-
*
|
|
25
|
-
* Extends NextPage with:
|
|
26
|
-
* - Layout control (getLayout, layoutMode)
|
|
27
|
-
* - SEO metadata (pageConfig)
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```tsx
|
|
31
|
-
* // Custom layout using getLayout
|
|
32
|
-
* const Page: PageWithLayout = () => <div>Content</div>;
|
|
33
|
-
* Page.getLayout = (page) => <CustomLayout>{page}</CustomLayout>;
|
|
34
|
-
*
|
|
35
|
-
* // Force specific layout mode
|
|
36
|
-
* const DashboardPage: PageWithLayout = () => <div>Dashboard</div>;
|
|
37
|
-
* DashboardPage.layoutMode = 'private';
|
|
38
|
-
*
|
|
39
|
-
* // With SEO config
|
|
40
|
-
* const HomePage: PageWithLayout = () => <div>Home</div>;
|
|
41
|
-
* HomePage.pageConfig = {
|
|
42
|
-
* title: 'Home Page',
|
|
43
|
-
* description: 'Welcome to our site',
|
|
44
|
-
* ogImage: { title: 'Home', subtitle: 'Welcome' }
|
|
45
|
-
* };
|
|
46
|
-
*
|
|
47
|
-
* // Disable all layouts
|
|
48
|
-
* const LandingPage: PageWithLayout = () => <FullPageDesign />;
|
|
49
|
-
* LandingPage.layoutMode = 'none';
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
export type PageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
|
53
|
-
/**
|
|
54
|
-
* Custom layout function
|
|
55
|
-
* When provided, AppLayout will use this instead of automatic layout detection
|
|
56
|
-
*
|
|
57
|
-
* @param page - The page component wrapped in React element
|
|
58
|
-
* @returns Layout-wrapped page
|
|
59
|
-
*/
|
|
60
|
-
getLayout?: (page: ReactElement) => ReactNode;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Force specific layout mode
|
|
64
|
-
* Overrides automatic route detection
|
|
65
|
-
*
|
|
66
|
-
* - undefined: Auto-detect based on route
|
|
67
|
-
* - 'public': Always use PublicLayout
|
|
68
|
-
* - 'private': Always use PrivateLayout
|
|
69
|
-
* - 'auth': Always use AuthLayout
|
|
70
|
-
* - 'admin': Always use AdminLayout
|
|
71
|
-
* - 'none': No layout (page renders directly)
|
|
72
|
-
*/
|
|
73
|
-
layoutMode?: LayoutMode;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Page SEO and metadata configuration
|
|
77
|
-
* Used for <head> tags, OpenGraph, Twitter cards, etc.
|
|
78
|
-
*/
|
|
79
|
-
pageConfig?: PageConfig;
|
|
80
|
-
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route Types
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Route configuration
|
|
7
|
-
*/
|
|
8
|
-
export interface RouteConfig {
|
|
9
|
-
/** Authentication route */
|
|
10
|
-
auth: string;
|
|
11
|
-
|
|
12
|
-
/** Default redirect after login */
|
|
13
|
-
defaultCallback: string;
|
|
14
|
-
|
|
15
|
-
/** Redirect for authenticated users on auth page */
|
|
16
|
-
defaultAuthCallback?: string;
|
|
17
|
-
|
|
18
|
-
/** Route detector functions */
|
|
19
|
-
detectors: RouteDetectors;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Route detection functions
|
|
24
|
-
*/
|
|
25
|
-
export interface RouteDetectors {
|
|
26
|
-
/** Check if route is public */
|
|
27
|
-
isPublicRoute: (path: string) => boolean;
|
|
28
|
-
|
|
29
|
-
/** Check if route is private/protected */
|
|
30
|
-
isPrivateRoute: (path: string) => boolean;
|
|
31
|
-
|
|
32
|
-
/** Check if route is admin */
|
|
33
|
-
isAdminRoute: (path: string) => boolean;
|
|
34
|
-
|
|
35
|
-
/** Check if route is auth page */
|
|
36
|
-
isAuthRoute: (path: string) => boolean;
|
|
37
|
-
|
|
38
|
-
/** Get redirect URL for unauthenticated users */
|
|
39
|
-
getUnauthenticatedRedirect: (path: string) => string | null;
|
|
40
|
-
|
|
41
|
-
/** Get page title for route */
|
|
42
|
-
getPageTitle: (path: string) => string;
|
|
43
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route Detection Utilities
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { LayoutMode, RouteDetectors } from '../types';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Determine layout mode from pathname
|
|
9
|
-
*/
|
|
10
|
-
export function determineLayoutMode(
|
|
11
|
-
pathname: string,
|
|
12
|
-
detectors: RouteDetectors
|
|
13
|
-
): LayoutMode {
|
|
14
|
-
if (detectors.isAuthRoute(pathname)) return 'auth';
|
|
15
|
-
if (detectors.isPrivateRoute(pathname)) return 'private';
|
|
16
|
-
return 'public';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Check if redirect is needed for unauthenticated user
|
|
21
|
-
*/
|
|
22
|
-
export function getRedirectUrl(
|
|
23
|
-
pathname: string,
|
|
24
|
-
isAuthenticated: boolean,
|
|
25
|
-
detectors: RouteDetectors
|
|
26
|
-
): string | null {
|
|
27
|
-
if (!isAuthenticated) {
|
|
28
|
-
return detectors.getUnauthenticatedRedirect(pathname);
|
|
29
|
-
}
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ErrorLayout - Universal Error Display
|
|
3
|
-
*
|
|
4
|
-
* Minimalist error page with customizable content
|
|
5
|
-
* Works with Next.js error pages (404.tsx, 500.tsx, error.tsx)
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* ```tsx
|
|
9
|
-
* // pages/404.tsx
|
|
10
|
-
* import { ErrorLayout } from '@djangocfg/layouts/AppLayout';
|
|
11
|
-
*
|
|
12
|
-
* export default function NotFound() {
|
|
13
|
-
* return (
|
|
14
|
-
* <ErrorLayout
|
|
15
|
-
* code="404"
|
|
16
|
-
* title="Page Not Found"
|
|
17
|
-
* description="The page you're looking for doesn't exist."
|
|
18
|
-
* />
|
|
19
|
-
* );
|
|
20
|
-
* }
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
'use client';
|
|
25
|
-
|
|
26
|
-
import React from 'react';
|
|
27
|
-
import { Button } from '@djangocfg/ui/components';
|
|
28
|
-
import { getErrorContent } from './errorConfig';
|
|
29
|
-
|
|
30
|
-
export interface ErrorLayoutProps {
|
|
31
|
-
/** Error code (e.g., "404", "500", "403") - if provided, auto-configures title/description/icon */
|
|
32
|
-
code?: string | number;
|
|
33
|
-
/** Error title (auto-generated from code if not provided) */
|
|
34
|
-
title?: string;
|
|
35
|
-
/** Error description (auto-generated from code if not provided) */
|
|
36
|
-
description?: string;
|
|
37
|
-
/** Custom action buttons */
|
|
38
|
-
actions?: React.ReactNode;
|
|
39
|
-
/** Show default actions (back, home) */
|
|
40
|
-
showDefaultActions?: boolean;
|
|
41
|
-
/** Custom illustration/icon (auto-generated from code if not provided) */
|
|
42
|
-
illustration?: React.ReactNode;
|
|
43
|
-
/** Support email for contact link */
|
|
44
|
-
supportEmail?: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* ErrorLayout Component
|
|
49
|
-
*
|
|
50
|
-
* Clean, minimal error display with semantic colors
|
|
51
|
-
* Standalone layout - doesn't depend on AppLayout context
|
|
52
|
-
*
|
|
53
|
-
* Smart auto-configuration:
|
|
54
|
-
* - Pass only `code` prop and everything is configured automatically
|
|
55
|
-
* - Or override with custom title/description/illustration
|
|
56
|
-
*/
|
|
57
|
-
export function ErrorLayout({
|
|
58
|
-
code,
|
|
59
|
-
title,
|
|
60
|
-
description,
|
|
61
|
-
actions,
|
|
62
|
-
showDefaultActions = true,
|
|
63
|
-
illustration,
|
|
64
|
-
supportEmail = 'support@example.com',
|
|
65
|
-
}: ErrorLayoutProps) {
|
|
66
|
-
// Auto-configure content from error code if not provided
|
|
67
|
-
const autoContent = code && (!title || !description || !illustration)
|
|
68
|
-
? getErrorContent(code)
|
|
69
|
-
: null;
|
|
70
|
-
|
|
71
|
-
// Use provided values or fall back to auto-generated
|
|
72
|
-
const finalTitle = title || autoContent?.title || 'Error';
|
|
73
|
-
const finalDescription = description || autoContent?.description;
|
|
74
|
-
const finalIllustration = illustration || autoContent?.icon;
|
|
75
|
-
|
|
76
|
-
const handleGoBack = () => {
|
|
77
|
-
// Use native navigation with full page reload instead of Next.js client-side navigation
|
|
78
|
-
if (document.referrer && document.referrer !== window.location.href) {
|
|
79
|
-
// Navigate to referrer with full reload
|
|
80
|
-
window.location.href = document.referrer;
|
|
81
|
-
} else if (window.history.length > 1) {
|
|
82
|
-
// Fallback: go back in history
|
|
83
|
-
window.history.back();
|
|
84
|
-
} else {
|
|
85
|
-
// Last resort: go to home
|
|
86
|
-
window.location.href = '/';
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const handleGoHome = () => {
|
|
91
|
-
// Full page reload to home
|
|
92
|
-
window.location.href = '/';
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<div className="min-h-screen flex items-center justify-center px-4">
|
|
97
|
-
<div className="max-w-2xl w-full text-center space-y-8">
|
|
98
|
-
{/* Error Code */}
|
|
99
|
-
{code && (
|
|
100
|
-
<div className="relative">
|
|
101
|
-
<h1
|
|
102
|
-
className="text-[12rem] font-bold leading-none text-muted/20 select-none"
|
|
103
|
-
aria-hidden="true"
|
|
104
|
-
>
|
|
105
|
-
{code}
|
|
106
|
-
</h1>
|
|
107
|
-
</div>
|
|
108
|
-
)}
|
|
109
|
-
|
|
110
|
-
{/* Illustration */}
|
|
111
|
-
{finalIllustration && (
|
|
112
|
-
<div className="flex justify-center py-8">
|
|
113
|
-
{finalIllustration}
|
|
114
|
-
</div>
|
|
115
|
-
)}
|
|
116
|
-
|
|
117
|
-
{/* Error Content */}
|
|
118
|
-
<div className="space-y-4">
|
|
119
|
-
<h2 className="text-4xl font-bold text-foreground">
|
|
120
|
-
{finalTitle}
|
|
121
|
-
</h2>
|
|
122
|
-
|
|
123
|
-
{finalDescription && (
|
|
124
|
-
<p className="text-lg text-muted-foreground max-w-md mx-auto">
|
|
125
|
-
{finalDescription}
|
|
126
|
-
</p>
|
|
127
|
-
)}
|
|
128
|
-
</div>
|
|
129
|
-
|
|
130
|
-
{/* Actions */}
|
|
131
|
-
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 pt-4">
|
|
132
|
-
{/* Custom actions */}
|
|
133
|
-
{actions}
|
|
134
|
-
|
|
135
|
-
{/* Default actions */}
|
|
136
|
-
{showDefaultActions && !actions && (
|
|
137
|
-
<>
|
|
138
|
-
<Button
|
|
139
|
-
variant="outline"
|
|
140
|
-
size="lg"
|
|
141
|
-
onClick={handleGoBack}
|
|
142
|
-
style={{ minWidth: '140px', padding: '12px 32px' }}
|
|
143
|
-
>
|
|
144
|
-
Go Back
|
|
145
|
-
</Button>
|
|
146
|
-
<Button
|
|
147
|
-
variant="default"
|
|
148
|
-
size="lg"
|
|
149
|
-
onClick={handleGoHome}
|
|
150
|
-
style={{ minWidth: '140px', padding: '12px 32px' }}
|
|
151
|
-
>
|
|
152
|
-
Go Home
|
|
153
|
-
</Button>
|
|
154
|
-
</>
|
|
155
|
-
)}
|
|
156
|
-
</div>
|
|
157
|
-
|
|
158
|
-
{/* Additional Info */}
|
|
159
|
-
<div className="pt-8 text-sm text-muted-foreground">
|
|
160
|
-
<p>
|
|
161
|
-
Need help? Contact{' '}
|
|
162
|
-
<a
|
|
163
|
-
href={`mailto:${supportEmail}`}
|
|
164
|
-
className="text-primary hover:underline"
|
|
165
|
-
>
|
|
166
|
-
support
|
|
167
|
-
</a>
|
|
168
|
-
</p>
|
|
169
|
-
</div>
|
|
170
|
-
</div>
|
|
171
|
-
</div>
|
|
172
|
-
);
|
|
173
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Universal Error Configuration
|
|
3
|
-
*
|
|
4
|
-
* Provides standard error content for common HTTP status codes
|
|
5
|
-
* Use this to maintain consistency across error pages
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import { FileQuestion, ServerCrash, ShieldAlert, Clock, AlertTriangle, Ban } from 'lucide-react';
|
|
10
|
-
|
|
11
|
-
export interface ErrorContent {
|
|
12
|
-
title: string;
|
|
13
|
-
description: string;
|
|
14
|
-
icon: React.ReactNode;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Get standardized error content based on status code
|
|
19
|
-
*
|
|
20
|
-
* @param statusCode - HTTP status code or custom error type
|
|
21
|
-
* @returns Error content configuration
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```tsx
|
|
25
|
-
* const { title, description, icon } = getErrorContent(404);
|
|
26
|
-
* <ErrorLayout title={title} description={description} illustration={icon} />
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export function getErrorContent(statusCode?: number | string): ErrorContent {
|
|
30
|
-
const code = typeof statusCode === 'string' ? parseInt(statusCode, 10) : statusCode;
|
|
31
|
-
|
|
32
|
-
switch (code) {
|
|
33
|
-
// 400 Bad Request
|
|
34
|
-
case 400:
|
|
35
|
-
return {
|
|
36
|
-
title: 'Bad Request',
|
|
37
|
-
description: 'The request could not be understood. Please check your input and try again.',
|
|
38
|
-
icon: <AlertTriangle className="w-24 h-24 text-warning/50" strokeWidth={1.5} />,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// 401 Unauthorized
|
|
42
|
-
case 401:
|
|
43
|
-
return {
|
|
44
|
-
title: 'Authentication Required',
|
|
45
|
-
description: 'You need to sign in to access this page.',
|
|
46
|
-
icon: <ShieldAlert className="w-24 h-24 text-warning/50" strokeWidth={1.5} />,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// 403 Forbidden
|
|
50
|
-
case 403:
|
|
51
|
-
return {
|
|
52
|
-
title: 'Access Denied',
|
|
53
|
-
description: "You don't have permission to access this resource.",
|
|
54
|
-
icon: <Ban className="w-24 h-24 text-destructive/50" strokeWidth={1.5} />,
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// 404 Not Found
|
|
58
|
-
case 404:
|
|
59
|
-
return {
|
|
60
|
-
title: 'Page Not Found',
|
|
61
|
-
description: "The page you're looking for doesn't exist or has been moved.",
|
|
62
|
-
icon: <FileQuestion className="w-24 h-24 text-muted-foreground/50" strokeWidth={1.5} />,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// 408 Request Timeout
|
|
66
|
-
case 408:
|
|
67
|
-
return {
|
|
68
|
-
title: 'Request Timeout',
|
|
69
|
-
description: 'The request took too long to process. Please try again.',
|
|
70
|
-
icon: <Clock className="w-24 h-24 text-warning/50" strokeWidth={1.5} />,
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// 500 Internal Server Error
|
|
74
|
-
case 500:
|
|
75
|
-
return {
|
|
76
|
-
title: 'Server Error',
|
|
77
|
-
description: "Something went wrong on our end. We're working to fix it.",
|
|
78
|
-
icon: <ServerCrash className="w-24 h-24 text-destructive/50" strokeWidth={1.5} />,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// 502 Bad Gateway
|
|
82
|
-
case 502:
|
|
83
|
-
return {
|
|
84
|
-
title: 'Bad Gateway',
|
|
85
|
-
description: 'The server received an invalid response. Please try again later.',
|
|
86
|
-
icon: <ServerCrash className="w-24 h-24 text-destructive/50" strokeWidth={1.5} />,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// 503 Service Unavailable
|
|
90
|
-
case 503:
|
|
91
|
-
return {
|
|
92
|
-
title: 'Service Unavailable',
|
|
93
|
-
description: 'The service is temporarily unavailable. Please try again later.',
|
|
94
|
-
icon: <ServerCrash className="w-24 h-24 text-destructive/50" strokeWidth={1.5} />,
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// 504 Gateway Timeout
|
|
98
|
-
case 504:
|
|
99
|
-
return {
|
|
100
|
-
title: 'Gateway Timeout',
|
|
101
|
-
description: 'The server took too long to respond. Please try again.',
|
|
102
|
-
icon: <Clock className="w-24 h-24 text-warning/50" strokeWidth={1.5} />,
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// Default / Unknown Error
|
|
106
|
-
default:
|
|
107
|
-
return {
|
|
108
|
-
title: 'Something Went Wrong',
|
|
109
|
-
description: 'An unexpected error occurred. Please try again or contact support.',
|
|
110
|
-
icon: <AlertTriangle className="w-24 h-24 text-warning/50" strokeWidth={1.5} />,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Common error codes as constants
|
|
117
|
-
*/
|
|
118
|
-
export const ERROR_CODES = {
|
|
119
|
-
BAD_REQUEST: 400,
|
|
120
|
-
UNAUTHORIZED: 401,
|
|
121
|
-
FORBIDDEN: 403,
|
|
122
|
-
NOT_FOUND: 404,
|
|
123
|
-
TIMEOUT: 408,
|
|
124
|
-
SERVER_ERROR: 500,
|
|
125
|
-
BAD_GATEWAY: 502,
|
|
126
|
-
SERVICE_UNAVAILABLE: 503,
|
|
127
|
-
GATEWAY_TIMEOUT: 504,
|
|
128
|
-
} as const;
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Ready-to-use getInitialProps for Next.js _error.tsx page
|
|
132
|
-
*
|
|
133
|
-
* Extracts status code from response or error automatically
|
|
134
|
-
* Works for both server-side and client-side errors
|
|
135
|
-
*
|
|
136
|
-
* @example
|
|
137
|
-
* ```tsx
|
|
138
|
-
* // pages/_error.tsx
|
|
139
|
-
* import { ErrorLayout, errorPageGetInitialProps } from '@djangocfg/layouts';
|
|
140
|
-
*
|
|
141
|
-
* function ErrorPage({ statusCode }) {
|
|
142
|
-
* return <ErrorLayout code={statusCode} />;
|
|
143
|
-
* }
|
|
144
|
-
*
|
|
145
|
-
* ErrorPage.getInitialProps = errorPageGetInitialProps;
|
|
146
|
-
* export default ErrorPage;
|
|
147
|
-
* ```
|
|
148
|
-
*/
|
|
149
|
-
export const errorPageGetInitialProps = ({ res, err }: any) => {
|
|
150
|
-
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
|
|
151
|
-
return { statusCode };
|
|
152
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ErrorLayout Module
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { ErrorLayout } from './ErrorLayout';
|
|
6
|
-
export type { ErrorLayoutProps } from './ErrorLayout';
|
|
7
|
-
export { getErrorContent, ERROR_CODES, errorPageGetInitialProps } from './errorConfig';
|
|
8
|
-
export type { ErrorContent } from './errorConfig';
|