@djangocfg/layouts 1.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/LICENSE +21 -0
- package/README.md +77 -0
- package/package.json +86 -0
- package/src/auth/README.md +962 -0
- package/src/auth/context/AuthContext.tsx +458 -0
- package/src/auth/context/index.ts +2 -0
- package/src/auth/context/types.ts +63 -0
- package/src/auth/hooks/index.ts +6 -0
- package/src/auth/hooks/useAuthForm.ts +329 -0
- package/src/auth/hooks/useAuthGuard.ts +23 -0
- package/src/auth/hooks/useAuthRedirect.ts +51 -0
- package/src/auth/hooks/useAutoAuth.ts +42 -0
- package/src/auth/hooks/useLocalStorage.ts +211 -0
- package/src/auth/hooks/useSessionStorage.ts +186 -0
- package/src/auth/index.ts +10 -0
- package/src/auth/middlewares/index.ts +1 -0
- package/src/auth/middlewares/proxy.ts +24 -0
- package/src/auth/server.ts +6 -0
- package/src/auth/utils/errors.ts +34 -0
- package/src/auth/utils/index.ts +2 -0
- package/src/auth/utils/validation.ts +14 -0
- package/src/index.ts +15 -0
- package/src/layouts/AppLayout/AppLayout.tsx +123 -0
- package/src/layouts/AppLayout/README.md +204 -0
- package/src/layouts/AppLayout/SUMMARY.md +240 -0
- package/src/layouts/AppLayout/USAGE.md +312 -0
- package/src/layouts/AppLayout/components/PageProgress.tsx +104 -0
- package/src/layouts/AppLayout/components/Seo.tsx +87 -0
- package/src/layouts/AppLayout/components/index.ts +6 -0
- package/src/layouts/AppLayout/context/AppContext.tsx +146 -0
- package/src/layouts/AppLayout/context/index.ts +5 -0
- package/src/layouts/AppLayout/hooks/index.ts +6 -0
- package/src/layouts/AppLayout/hooks/useLayoutMode.ts +26 -0
- package/src/layouts/AppLayout/hooks/useNavigation.ts +49 -0
- package/src/layouts/AppLayout/index.ts +31 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthContext.tsx +51 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthHelp.tsx +111 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +40 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/IdentifierForm.tsx +330 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +158 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +13 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/types.ts +61 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +92 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +60 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +170 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +164 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +7 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +5 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +44 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/components/DesktopUserMenu.tsx +136 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +262 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenu.tsx +289 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +159 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +5 -0
- package/src/layouts/AppLayout/layouts/index.ts +7 -0
- package/src/layouts/AppLayout/providers/CoreProviders.tsx +47 -0
- package/src/layouts/AppLayout/providers/index.ts +5 -0
- package/src/layouts/AppLayout/types/config.ts +40 -0
- package/src/layouts/AppLayout/types/index.ts +10 -0
- package/src/layouts/AppLayout/types/layout.ts +47 -0
- package/src/layouts/AppLayout/types/navigation.ts +41 -0
- package/src/layouts/AppLayout/types/routes.ts +45 -0
- package/src/layouts/AppLayout/utils/index.ts +5 -0
- package/src/layouts/AppLayout/utils/routeDetection.ts +31 -0
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +125 -0
- package/src/layouts/PaymentsLayout/README.md +133 -0
- package/src/layouts/PaymentsLayout/components/CreateApiKeyDialog.tsx +172 -0
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +203 -0
- package/src/layouts/PaymentsLayout/components/DeleteApiKeyDialog.tsx +100 -0
- package/src/layouts/PaymentsLayout/components/index.ts +4 -0
- package/src/layouts/PaymentsLayout/events.ts +106 -0
- package/src/layouts/PaymentsLayout/index.ts +20 -0
- package/src/layouts/PaymentsLayout/types.ts +19 -0
- package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeyMetrics.tsx +109 -0
- package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeysList.tsx +194 -0
- package/src/layouts/PaymentsLayout/views/apikeys/components/index.ts +3 -0
- package/src/layouts/PaymentsLayout/views/apikeys/index.tsx +19 -0
- package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +99 -0
- package/src/layouts/PaymentsLayout/views/overview/components/MetricsCards.tsx +103 -0
- package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +138 -0
- package/src/layouts/PaymentsLayout/views/overview/components/index.ts +4 -0
- package/src/layouts/PaymentsLayout/views/overview/index.tsx +23 -0
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +282 -0
- package/src/layouts/PaymentsLayout/views/payments/components/index.ts +2 -0
- package/src/layouts/PaymentsLayout/views/payments/index.tsx +18 -0
- package/src/layouts/PaymentsLayout/views/tariffs/index.tsx +29 -0
- package/src/layouts/PaymentsLayout/views/transactions/index.tsx +29 -0
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +110 -0
- package/src/layouts/ProfileLayout/components/AvatarSection.tsx +146 -0
- package/src/layouts/ProfileLayout/components/ProfileForm.tsx +208 -0
- package/src/layouts/ProfileLayout/components/index.ts +3 -0
- package/src/layouts/ProfileLayout/index.ts +3 -0
- package/src/layouts/SupportLayout/README.md +91 -0
- package/src/layouts/SupportLayout/SupportLayout.tsx +178 -0
- package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +154 -0
- package/src/layouts/SupportLayout/components/MessageInput.tsx +92 -0
- package/src/layouts/SupportLayout/components/MessageList.tsx +312 -0
- package/src/layouts/SupportLayout/components/TicketCard.tsx +96 -0
- package/src/layouts/SupportLayout/components/TicketList.tsx +152 -0
- package/src/layouts/SupportLayout/components/index.ts +6 -0
- package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +260 -0
- package/src/layouts/SupportLayout/context/index.ts +2 -0
- package/src/layouts/SupportLayout/events.ts +31 -0
- package/src/layouts/SupportLayout/hooks/index.ts +2 -0
- package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +118 -0
- package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +91 -0
- package/src/layouts/SupportLayout/index.ts +6 -0
- package/src/layouts/SupportLayout/types.ts +23 -0
- package/src/layouts/index.ts +9 -0
- package/src/snippets/AuthDialog/AuthDialog.tsx +88 -0
- package/src/snippets/AuthDialog/events.ts +21 -0
- package/src/snippets/AuthDialog/index.ts +3 -0
- package/src/snippets/AuthDialog/useAuthDialog.ts +27 -0
- package/src/snippets/Breadcrumbs.tsx +80 -0
- package/src/snippets/Chat/ChatUIContext.tsx +110 -0
- package/src/snippets/Chat/ChatWidget.tsx +476 -0
- package/src/snippets/Chat/README.md +122 -0
- package/src/snippets/Chat/components/MessageInput.tsx +124 -0
- package/src/snippets/Chat/components/MessageList.tsx +168 -0
- package/src/snippets/Chat/components/SessionList.tsx +192 -0
- package/src/snippets/Chat/components/index.ts +9 -0
- package/src/snippets/Chat/hooks/index.ts +6 -0
- package/src/snippets/Chat/hooks/useInfiniteSessions.ts +83 -0
- package/src/snippets/Chat/index.tsx +44 -0
- package/src/snippets/Chat/types.ts +79 -0
- package/src/snippets/VideoPlayer/README.md +203 -0
- package/src/snippets/VideoPlayer/VideoControls.tsx +133 -0
- package/src/snippets/VideoPlayer/VideoPlayer.tsx +114 -0
- package/src/snippets/VideoPlayer/index.ts +8 -0
- package/src/snippets/VideoPlayer/types.ts +61 -0
- package/src/snippets/index.ts +10 -0
- package/src/styles/dashboard.css +41 -0
- package/src/styles/index.css +20 -0
- package/src/styles/sources.css +6 -0
- package/src/types/index.ts +1 -0
- package/src/types/pageConfig.ts +103 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/logger.ts +57 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Layouts - Page Layout Components
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export * from './ProfileLayout';
|
|
7
|
+
export * from './SupportLayout';
|
|
8
|
+
export * from './PaymentsLayout';
|
|
9
|
+
export * from './AppLayout';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { LogIn } from 'lucide-react';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import React, { useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
Button,
|
|
9
|
+
Dialog,
|
|
10
|
+
DialogContent,
|
|
11
|
+
DialogHeader,
|
|
12
|
+
DialogTitle,
|
|
13
|
+
} from '@djangocfg/ui/components';
|
|
14
|
+
import { useEventListener } from '@djangocfg/ui/hooks';
|
|
15
|
+
|
|
16
|
+
// Re-export events for backwards compatibility
|
|
17
|
+
export const DIALOG_EVENTS = {
|
|
18
|
+
OPEN_AUTH_DIALOG: 'OPEN_AUTH_DIALOG',
|
|
19
|
+
CLOSE_AUTH_DIALOG: 'CLOSE_AUTH_DIALOG',
|
|
20
|
+
AUTH_SUCCESS: 'AUTH_SUCCESS',
|
|
21
|
+
AUTH_FAILURE: 'AUTH_FAILURE',
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
interface AuthDialogProps {
|
|
25
|
+
onAuthRequired?: () => void;
|
|
26
|
+
authPath?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const AuthDialog: React.FC<AuthDialogProps> = ({
|
|
30
|
+
onAuthRequired,
|
|
31
|
+
authPath = '/auth'
|
|
32
|
+
}) => {
|
|
33
|
+
const [open, setOpen] = useState(false);
|
|
34
|
+
const [message, setMessage] = useState<string>('Please sign in to continue');
|
|
35
|
+
const router = useRouter();
|
|
36
|
+
|
|
37
|
+
// Listen for open auth dialog event
|
|
38
|
+
useEventListener(DIALOG_EVENTS.OPEN_AUTH_DIALOG, (payload: any) => {
|
|
39
|
+
if (payload?.message) {
|
|
40
|
+
setMessage(payload.message);
|
|
41
|
+
}
|
|
42
|
+
setOpen(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Listen for close auth dialog event
|
|
46
|
+
useEventListener(DIALOG_EVENTS.CLOSE_AUTH_DIALOG, () => {
|
|
47
|
+
setOpen(false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const handleClose = () => {
|
|
51
|
+
setMessage('Please sign in to continue');
|
|
52
|
+
setOpen(false);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleGoToAuth = () => {
|
|
56
|
+
// Save current URL for redirect after successful auth
|
|
57
|
+
if (typeof window !== 'undefined') {
|
|
58
|
+
sessionStorage.setItem('redirectAfterAuth', window.location.pathname);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (onAuthRequired) {
|
|
62
|
+
onAuthRequired();
|
|
63
|
+
} else {
|
|
64
|
+
router.push(authPath);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
handleClose();
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Dialog open={open} onOpenChange={handleClose}>
|
|
72
|
+
<DialogContent className="max-w-sm">
|
|
73
|
+
<DialogHeader>
|
|
74
|
+
<DialogTitle>Authentication Required</DialogTitle>
|
|
75
|
+
</DialogHeader>
|
|
76
|
+
|
|
77
|
+
<div className="space-y-4">
|
|
78
|
+
<p className="text-sm text-muted-foreground">{message}</p>
|
|
79
|
+
|
|
80
|
+
<Button onClick={handleGoToAuth} className="w-full">
|
|
81
|
+
<LogIn className="h-4 w-4 mr-2" />
|
|
82
|
+
Go to Sign In
|
|
83
|
+
</Button>
|
|
84
|
+
</div>
|
|
85
|
+
</DialogContent>
|
|
86
|
+
</Dialog>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const AUTH_EVENTS = {
|
|
2
|
+
OPEN_AUTH_DIALOG: 'OPEN_AUTH_DIALOG',
|
|
3
|
+
CLOSE_AUTH_DIALOG: 'CLOSE_AUTH_DIALOG',
|
|
4
|
+
AUTH_SUCCESS: 'AUTH_SUCCESS',
|
|
5
|
+
AUTH_FAILURE: 'AUTH_FAILURE',
|
|
6
|
+
} as const;
|
|
7
|
+
|
|
8
|
+
export type AuthEventType = typeof AUTH_EVENTS[keyof typeof AUTH_EVENTS];
|
|
9
|
+
|
|
10
|
+
export interface OpenAuthDialogPayload {
|
|
11
|
+
message?: string;
|
|
12
|
+
redirectUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface AuthSuccessPayload {
|
|
16
|
+
user?: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AuthFailurePayload {
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { AUTH_EVENTS, type OpenAuthDialogPayload } from './events';
|
|
4
|
+
import { events } from '@djangocfg/ui';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Hook to control auth dialog from anywhere in the app
|
|
8
|
+
*/
|
|
9
|
+
export function useAuthDialog() {
|
|
10
|
+
const openAuthDialog = useCallback((options?: OpenAuthDialogPayload) => {
|
|
11
|
+
events.publish({
|
|
12
|
+
type: AUTH_EVENTS.OPEN_AUTH_DIALOG,
|
|
13
|
+
payload: options,
|
|
14
|
+
});
|
|
15
|
+
}, []);
|
|
16
|
+
|
|
17
|
+
const closeAuthDialog = useCallback(() => {
|
|
18
|
+
events.publish({
|
|
19
|
+
type: AUTH_EVENTS.CLOSE_AUTH_DIALOG,
|
|
20
|
+
});
|
|
21
|
+
}, []);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
openAuthDialog,
|
|
25
|
+
closeAuthDialog,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { usePathname } from "next/navigation";
|
|
5
|
+
import { ChevronRight, Home } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
export interface BreadcrumbItem {
|
|
8
|
+
path: string;
|
|
9
|
+
label: string;
|
|
10
|
+
isActive: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface BreadcrumbsProps {
|
|
14
|
+
items?: BreadcrumbItem[];
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const Breadcrumbs: React.FC<BreadcrumbsProps> = ({ items, className = "" }) => {
|
|
19
|
+
const pathname = usePathname();
|
|
20
|
+
|
|
21
|
+
// Use provided items or generate from pathname
|
|
22
|
+
const breadcrumbs = items || generateBreadcrumbsFromPath(pathname);
|
|
23
|
+
|
|
24
|
+
if (breadcrumbs.length <= 1) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<nav className={`flex items-center space-x-2 text-sm ${className}`} aria-label="Breadcrumb">
|
|
30
|
+
<ol className="flex items-center space-x-2">
|
|
31
|
+
{breadcrumbs.map((item: BreadcrumbItem, index: number) => (
|
|
32
|
+
<li key={item.path} className="flex items-center">
|
|
33
|
+
{index > 0 && (
|
|
34
|
+
<ChevronRight className="w-4 h-4 text-gray-400 mx-2" />
|
|
35
|
+
)}
|
|
36
|
+
|
|
37
|
+
{item.isActive ? (
|
|
38
|
+
<span className="text-gray-900 dark:text-white font-medium flex items-center gap-1">
|
|
39
|
+
{index === 0 && <Home className="w-4 h-4" />}
|
|
40
|
+
{item.label}
|
|
41
|
+
</span>
|
|
42
|
+
) : (
|
|
43
|
+
<Link
|
|
44
|
+
href={item.path}
|
|
45
|
+
className="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200
|
|
46
|
+
transition-colors duration-200 flex items-center gap-1 hover:underline"
|
|
47
|
+
>
|
|
48
|
+
{index === 0 && <Home className="w-4 h-4" />}
|
|
49
|
+
{item.label}
|
|
50
|
+
</Link>
|
|
51
|
+
)}
|
|
52
|
+
</li>
|
|
53
|
+
))}
|
|
54
|
+
</ol>
|
|
55
|
+
</nav>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Helper function to generate breadcrumbs from pathname
|
|
60
|
+
function generateBreadcrumbsFromPath(pathname: string): BreadcrumbItem[] {
|
|
61
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
62
|
+
const breadcrumbs: BreadcrumbItem[] = [
|
|
63
|
+
{ path: '/', label: 'Home', isActive: pathname === '/' }
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
let currentPath = '';
|
|
67
|
+
segments.forEach((segment, index) => {
|
|
68
|
+
currentPath += `/${segment}`;
|
|
69
|
+
breadcrumbs.push({
|
|
70
|
+
path: currentPath,
|
|
71
|
+
label: segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' '),
|
|
72
|
+
isActive: index === segments.length - 1
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return breadcrumbs;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default Breadcrumbs;
|
|
80
|
+
export { generateBreadcrumbsFromPath };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat UI Context
|
|
3
|
+
* Manages UI state for chat widget (toggle, expand, minimize)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import React, { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
|
|
9
|
+
import type { ChatUIState } from './types';
|
|
10
|
+
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
12
|
+
// Context Type
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export interface ChatUIContextValue {
|
|
16
|
+
uiState: ChatUIState;
|
|
17
|
+
toggleChat: () => void;
|
|
18
|
+
openChat: () => void;
|
|
19
|
+
closeChat: () => void;
|
|
20
|
+
expandChat: () => void;
|
|
21
|
+
collapseChat: () => void;
|
|
22
|
+
minimizeChat: () => void;
|
|
23
|
+
toggleSources: () => void;
|
|
24
|
+
toggleTimestamps: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
28
|
+
// Context
|
|
29
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
const ChatUIContext = createContext<ChatUIContextValue | undefined>(undefined);
|
|
32
|
+
|
|
33
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
34
|
+
// Provider
|
|
35
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
export interface ChatUIProviderProps {
|
|
38
|
+
children: ReactNode;
|
|
39
|
+
initialState?: Partial<ChatUIState>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function ChatUIProvider({ children, initialState }: ChatUIProviderProps) {
|
|
43
|
+
const [uiState, setUIState] = useState<ChatUIState>({
|
|
44
|
+
isOpen: false,
|
|
45
|
+
isExpanded: false,
|
|
46
|
+
isMinimized: false,
|
|
47
|
+
showSources: true,
|
|
48
|
+
showTimestamps: false,
|
|
49
|
+
...initialState,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const toggleChat = useCallback(() => {
|
|
53
|
+
setUIState((prev) => ({ ...prev, isOpen: !prev.isOpen }));
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const openChat = useCallback(() => {
|
|
57
|
+
setUIState((prev) => ({ ...prev, isOpen: true }));
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
const closeChat = useCallback(() => {
|
|
61
|
+
setUIState((prev) => ({ ...prev, isOpen: false }));
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
const expandChat = useCallback(() => {
|
|
65
|
+
setUIState((prev) => ({ ...prev, isExpanded: true, isMinimized: false }));
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
const collapseChat = useCallback(() => {
|
|
69
|
+
setUIState((prev) => ({ ...prev, isExpanded: false }));
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
const minimizeChat = useCallback(() => {
|
|
73
|
+
setUIState((prev) => ({ ...prev, isMinimized: true, isExpanded: false }));
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
const toggleSources = useCallback(() => {
|
|
77
|
+
setUIState((prev) => ({ ...prev, showSources: !prev.showSources }));
|
|
78
|
+
}, []);
|
|
79
|
+
|
|
80
|
+
const toggleTimestamps = useCallback(() => {
|
|
81
|
+
setUIState((prev) => ({ ...prev, showTimestamps: !prev.showTimestamps }));
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
const value: ChatUIContextValue = {
|
|
85
|
+
uiState,
|
|
86
|
+
toggleChat,
|
|
87
|
+
openChat,
|
|
88
|
+
closeChat,
|
|
89
|
+
expandChat,
|
|
90
|
+
collapseChat,
|
|
91
|
+
minimizeChat,
|
|
92
|
+
toggleSources,
|
|
93
|
+
toggleTimestamps,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return <ChatUIContext.Provider value={value}>{children}</ChatUIContext.Provider>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
100
|
+
// Hook
|
|
101
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
export function useChatUI(): ChatUIContextValue {
|
|
104
|
+
const context = useContext(ChatUIContext);
|
|
105
|
+
if (!context) {
|
|
106
|
+
throw new Error('useChatUI must be used within ChatUIProvider');
|
|
107
|
+
}
|
|
108
|
+
return context;
|
|
109
|
+
}
|
|
110
|
+
|