@djangocfg/layouts 2.1.10 → 2.1.14

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 (105) hide show
  1. package/README.md +53 -161
  2. package/package.json +6 -6
  3. package/src/components/RedirectPage/RedirectPage.tsx +1 -1
  4. package/src/index.ts +0 -6
  5. package/src/layouts/AppLayout/AppLayout.tsx +1 -1
  6. package/src/layouts/AppLayout/BaseApp.tsx +1 -1
  7. package/src/layouts/AuthLayout/AuthContext.tsx +1 -1
  8. package/src/layouts/AuthLayout/OAuthCallback.tsx +1 -1
  9. package/src/layouts/AuthLayout/OAuthProviders.tsx +1 -1
  10. package/src/layouts/PrivateLayout/PrivateLayout.tsx +1 -1
  11. package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +1 -1
  12. package/src/layouts/ProfileLayout/components/AvatarSection.tsx +2 -2
  13. package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
  14. package/src/layouts/PublicLayout/components/PublicNavigation.tsx +1 -1
  15. package/src/layouts/_components/UserMenu.tsx +1 -1
  16. package/src/layouts/index.ts +0 -2
  17. package/src/snippets/Analytics/useAnalytics.ts +1 -1
  18. package/src/snippets/index.ts +0 -3
  19. package/src/auth/README.md +0 -962
  20. package/src/auth/context/AccountsContext.tsx +0 -240
  21. package/src/auth/context/AuthContext.tsx +0 -604
  22. package/src/auth/context/index.ts +0 -4
  23. package/src/auth/context/types.ts +0 -68
  24. package/src/auth/hooks/index.ts +0 -17
  25. package/src/auth/hooks/useAuthForm.ts +0 -332
  26. package/src/auth/hooks/useAuthGuard.ts +0 -25
  27. package/src/auth/hooks/useAuthRedirect.ts +0 -51
  28. package/src/auth/hooks/useAutoAuth.ts +0 -49
  29. package/src/auth/hooks/useGithubAuth.ts +0 -184
  30. package/src/auth/hooks/useLocalStorage.ts +0 -214
  31. package/src/auth/hooks/useProfileCache.ts +0 -146
  32. package/src/auth/hooks/useSessionStorage.ts +0 -189
  33. package/src/auth/index.ts +0 -10
  34. package/src/auth/middlewares/index.ts +0 -1
  35. package/src/auth/middlewares/proxy.ts +0 -32
  36. package/src/auth/server.ts +0 -6
  37. package/src/auth/utils/errors.ts +0 -34
  38. package/src/auth/utils/index.ts +0 -2
  39. package/src/auth/utils/validation.ts +0 -14
  40. package/src/contexts/LeadsContext.tsx +0 -156
  41. package/src/contexts/NewsletterContext.tsx +0 -263
  42. package/src/contexts/SupportContext.tsx +0 -256
  43. package/src/contexts/index.ts +0 -59
  44. package/src/contexts/knowbase/ChatContext.tsx +0 -174
  45. package/src/contexts/knowbase/DocumentsContext.tsx +0 -304
  46. package/src/contexts/knowbase/SessionsContext.tsx +0 -174
  47. package/src/contexts/knowbase/index.ts +0 -61
  48. package/src/contexts/payments/BalancesContext.tsx +0 -65
  49. package/src/contexts/payments/CurrenciesContext.tsx +0 -66
  50. package/src/contexts/payments/OverviewContext.tsx +0 -174
  51. package/src/contexts/payments/PaymentsContext.tsx +0 -132
  52. package/src/contexts/payments/README.md +0 -201
  53. package/src/contexts/payments/RootPaymentsContext.tsx +0 -68
  54. package/src/contexts/payments/index.ts +0 -50
  55. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -92
  56. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -291
  57. package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -290
  58. package/src/layouts/PaymentsLayout/components/index.ts +0 -2
  59. package/src/layouts/PaymentsLayout/events.ts +0 -47
  60. package/src/layouts/PaymentsLayout/index.ts +0 -16
  61. package/src/layouts/PaymentsLayout/types.ts +0 -6
  62. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -128
  63. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -142
  64. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
  65. package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -20
  66. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -276
  67. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
  68. package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -17
  69. package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -273
  70. package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
  71. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -17
  72. package/src/layouts/SupportLayout/README.md +0 -91
  73. package/src/layouts/SupportLayout/SupportLayout.tsx +0 -179
  74. package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +0 -155
  75. package/src/layouts/SupportLayout/components/MessageInput.tsx +0 -92
  76. package/src/layouts/SupportLayout/components/MessageList.tsx +0 -314
  77. package/src/layouts/SupportLayout/components/TicketCard.tsx +0 -96
  78. package/src/layouts/SupportLayout/components/TicketList.tsx +0 -153
  79. package/src/layouts/SupportLayout/components/index.ts +0 -6
  80. package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +0 -263
  81. package/src/layouts/SupportLayout/context/index.ts +0 -2
  82. package/src/layouts/SupportLayout/events.ts +0 -33
  83. package/src/layouts/SupportLayout/hooks/index.ts +0 -2
  84. package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +0 -119
  85. package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +0 -92
  86. package/src/layouts/SupportLayout/index.ts +0 -8
  87. package/src/layouts/SupportLayout/types.ts +0 -21
  88. package/src/snippets/Chat/ChatUIContext.tsx +0 -110
  89. package/src/snippets/Chat/ChatWidget.tsx +0 -476
  90. package/src/snippets/Chat/README.md +0 -122
  91. package/src/snippets/Chat/components/MessageInput.tsx +0 -124
  92. package/src/snippets/Chat/components/MessageList.tsx +0 -169
  93. package/src/snippets/Chat/components/SessionList.tsx +0 -192
  94. package/src/snippets/Chat/components/index.ts +0 -9
  95. package/src/snippets/Chat/hooks/index.ts +0 -6
  96. package/src/snippets/Chat/hooks/useInfiniteSessions.ts +0 -82
  97. package/src/snippets/Chat/index.tsx +0 -45
  98. package/src/snippets/Chat/types.ts +0 -80
  99. package/src/snippets/ContactForm/ContactForm.tsx +0 -346
  100. package/src/snippets/ContactForm/ContactFormProvider.tsx +0 -153
  101. package/src/snippets/ContactForm/ContactInfo.tsx +0 -114
  102. package/src/snippets/ContactForm/ContactPage.tsx +0 -131
  103. package/src/snippets/ContactForm/dynamic.tsx +0 -55
  104. package/src/snippets/ContactForm/index.ts +0 -34
  105. package/src/snippets/ContactForm/types.ts +0 -110
@@ -1,122 +0,0 @@
1
- # Knowledge Chat Widget
2
-
3
- RAG-powered chat widget with beautiful animations powered by `tailwindcss-animate`.
4
-
5
- ## Features
6
-
7
- - 🎨 **Beautiful Animations**: Smooth transitions and micro-interactions
8
- - 💬 **Real-time Chat**: RAG-powered responses with sources
9
- - 📱 **Responsive**: Works on desktop and mobile
10
- - 🗂️ **Session Management**: Create and manage chat sessions
11
- - 🎯 **Context-aware**: Uses SWR hooks for data management
12
-
13
- ## Animations
14
-
15
- ### Widget Animations
16
- - **Entry**: Fade in + slide from bottom with scale
17
- - **Exit**: Fade out + slide to bottom with scale reduction
18
- - **FAB Button**: Fade in + slide from bottom, hover scale, active scale
19
-
20
- ### Message Animations
21
- - **Message Entry**: Staggered fade in + slide from bottom (50ms delay between messages)
22
- - **Message Bubble**: Hover shadow effect with smooth transition
23
- - **Loading State**: Pulsing avatar with bouncing bot icon
24
-
25
- ### Sources Animations
26
- - **Container**: Fade in + slide from left with 100ms delay
27
- - **Individual Badges**: Staggered zoom in (100ms per badge)
28
- - **Badge Hover**: Scale up + active scale down
29
-
30
- ### Session List Animations
31
- - **Session Items**: Staggered fade in + slide from left (50ms delay)
32
- - **Hover State**: Border color + shadow transition
33
- - **Action Buttons**:
34
- - Slide in from right on hover
35
- - Scale up on hover, scale down on active
36
-
37
- ### Input Animations
38
- - **Textarea**: Focus ring animation
39
- - **Send Button**: Scale up on hover, scale down on active
40
- - **Send Icon**: Smooth transitions
41
-
42
- ## Usage
43
-
44
- ```tsx
45
- import { KnowledgeChat } from '@djangocfg/layouts/snippets';
46
-
47
- // Simple usage
48
- <KnowledgeChat />
49
-
50
- // With props
51
- <KnowledgeChat
52
- autoOpen={false}
53
- persistent={true}
54
- onToggle={(isOpen) => console.log('Chat toggled:', isOpen)}
55
- onMessage={(message) => console.log('Message sent:', message)}
56
- />
57
- ```
58
-
59
- ## Components
60
-
61
- ### KnowledgeChat
62
- Main component with all providers integrated.
63
-
64
- ### ChatWidget
65
- Core chat interface (use with providers manually if needed).
66
-
67
- ### ChatUIProvider
68
- Manages UI state (open/closed, expanded, sources visibility).
69
-
70
- ### MessageList
71
- Displays chat messages with sources and animations.
72
-
73
- ### MessageInput
74
- Input field with auto-resize and keyboard shortcuts.
75
-
76
- ### SessionList
77
- Drawer with chat sessions list.
78
-
79
- ## Animation Timing
80
-
81
- - **Fast**: 200ms (buttons, badges)
82
- - **Normal**: 300ms (widget, messages, drawer)
83
- - **Stagger Delay**: 50-100ms (lists)
84
-
85
- ## Tailwind Classes Used
86
-
87
- ### tailwindcss-animate
88
- - `animate-in`
89
- - `fade-in`
90
- - `slide-in-from-bottom-{n}`
91
- - `slide-in-from-left-{n}`
92
- - `zoom-in-95`
93
- - `duration-{n}`
94
- - `delay-{n}`
95
-
96
- ### Tailwind Core
97
- - `transition-all`
98
- - `transition-transform`
99
- - `transition-colors`
100
- - `duration-{n}`
101
- - `ease-out`
102
- - `animate-spin`
103
- - `animate-pulse`
104
- - `animate-bounce`
105
- - `hover:scale-{n}`
106
- - `active:scale-{n}`
107
-
108
- ## Performance
109
-
110
- All animations use CSS transforms and opacity for optimal performance:
111
- - Hardware-accelerated transforms (translate, scale)
112
- - Efficient opacity transitions
113
- - No layout thrashing
114
- - Minimal reflows
115
-
116
- ## Accessibility
117
-
118
- - Respects `prefers-reduced-motion`
119
- - Keyboard navigation support
120
- - ARIA labels
121
- - Screen reader friendly
122
-
@@ -1,124 +0,0 @@
1
- /**
2
- * Message Input Component
3
- * Input field for sending chat messages
4
- */
5
-
6
- 'use client';
7
-
8
- import React, { useState, useCallback, useRef, useEffect } from 'react';
9
- import { Button, Textarea } from '@djangocfg/ui-nextjs';
10
- import { Send, Loader2 } from 'lucide-react';
11
- import { chatLogger } from '../../../utils/logger';
12
- import type { MessageInputProps } from '../types';
13
-
14
- // ─────────────────────────────────────────────────────────────────────────
15
- // Message Input Component
16
- // ─────────────────────────────────────────────────────────────────────────
17
-
18
- export const MessageInput: React.FC<MessageInputProps> = ({
19
- onSend,
20
- isLoading = false,
21
- disabled = false,
22
- placeholder = 'Ask me anything...',
23
- className = '',
24
- }) => {
25
- const [message, setMessage] = useState('');
26
- const [rows, setRows] = useState(1);
27
- const textareaRef = useRef<HTMLTextAreaElement>(null);
28
-
29
- // Auto-resize textarea based on content
30
- useEffect(() => {
31
- if (textareaRef.current) {
32
- const lineHeight = 24; // approximate line height
33
- const maxRows = 5;
34
- const minRows = 1;
35
-
36
- // If message is empty, reset to min height
37
- if (!message) {
38
- textareaRef.current.style.height = 'auto';
39
- setRows(minRows);
40
- return;
41
- }
42
-
43
- textareaRef.current.style.height = 'auto';
44
- const scrollHeight = textareaRef.current.scrollHeight;
45
- const newRows = Math.max(minRows, Math.min(maxRows, Math.floor(scrollHeight / lineHeight)));
46
- setRows(newRows);
47
- }
48
- }, [message]);
49
-
50
- const handleSubmit = useCallback(
51
- async (e?: React.FormEvent) => {
52
- e?.preventDefault();
53
-
54
- const trimmedMessage = message.trim();
55
- if (!trimmedMessage || isLoading || disabled) return;
56
-
57
- try {
58
- await onSend(trimmedMessage);
59
- setMessage('');
60
- setRows(1);
61
-
62
- // Reset textarea height
63
- if (textareaRef.current) {
64
- textareaRef.current.style.height = 'auto';
65
- }
66
- } catch (error) {
67
- chatLogger.error('Failed to send message from input:', error);
68
- }
69
- },
70
- [message, isLoading, disabled, onSend]
71
- );
72
-
73
- const handleKeyDown = useCallback(
74
- (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
75
- // Send on Enter (without Shift)
76
- if (e.key === 'Enter' && !e.shiftKey) {
77
- e.preventDefault();
78
- handleSubmit();
79
- }
80
- },
81
- [handleSubmit]
82
- );
83
-
84
- const isDisabled = disabled || isLoading;
85
- const canSend = message.trim().length > 0 && !isDisabled;
86
-
87
- return (
88
- <form onSubmit={handleSubmit} className={`border-t p-4 ${className}`}>
89
- <div className="flex items-end gap-2">
90
- <Textarea
91
- ref={textareaRef}
92
- value={message}
93
- onChange={(e) => setMessage(e.target.value)}
94
- onKeyDown={handleKeyDown}
95
- placeholder={placeholder}
96
- disabled={isDisabled}
97
- rows={rows}
98
- className="resize-none min-h-[40px] max-h-[120px] transition-all duration-200
99
- focus:ring-2 focus:ring-primary/20"
100
- style={{ resize: 'none' }}
101
- />
102
-
103
- <Button
104
- type="submit"
105
- size="icon"
106
- disabled={!canSend}
107
- className="shrink-0 h-10 w-10 transition-all duration-200
108
- hover:scale-110 active:scale-95 disabled:scale-100"
109
- >
110
- {isLoading ? (
111
- <Loader2 className="h-4 w-4 animate-spin" />
112
- ) : (
113
- <Send className="h-4 w-4" />
114
- )}
115
- </Button>
116
- </div>
117
-
118
- <p className="text-xs text-muted-foreground mt-2">
119
- Press Enter to send, Shift+Enter for new line
120
- </p>
121
- </form>
122
- );
123
- };
124
-
@@ -1,169 +0,0 @@
1
- /**
2
- * Message List Component
3
- * Displays chat messages with markdown support and sources
4
- */
5
-
6
- 'use client';
7
-
8
- import React, { useEffect, useRef } from 'react';
9
- import { Card, CardContent, Avatar, AvatarImage, AvatarFallback, Badge, ScrollArea } from '@djangocfg/ui-nextjs';
10
- import { Bot, User, ExternalLink, Loader2 } from 'lucide-react';
11
- import { Enums } from '@djangocfg/api/cfg/generated';
12
- import { useAuth } from '../../../auth';
13
- import type { MessageListProps } from '../types';
14
-
15
- // ─────────────────────────────────────────────────────────────────────────
16
- // Message List Component
17
- // ─────────────────────────────────────────────────────────────────────────
18
-
19
- export const MessageList: React.FC<MessageListProps> = ({
20
- messages,
21
- isLoading = false,
22
- showSources = true,
23
- showTimestamps = false,
24
- autoScroll = true,
25
- className = '',
26
- }) => {
27
- const scrollRef = useRef<HTMLDivElement>(null);
28
- const { user } = useAuth();
29
-
30
- // Auto-scroll to bottom on new messages
31
- useEffect(() => {
32
- if (autoScroll && scrollRef.current) {
33
- const scrollContainer = scrollRef.current.querySelector('[data-radix-scroll-area-viewport]');
34
- if (scrollContainer) {
35
- scrollContainer.scrollTop = scrollContainer.scrollHeight;
36
- }
37
- }
38
- }, [messages, isLoading, autoScroll]);
39
-
40
- const formatTimestamp = (timestamp: string) => {
41
- const date = new Date(timestamp);
42
- return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
43
- };
44
-
45
- return (
46
- <ScrollArea className={`h-full bg-muted/50 ${className}`} viewportRef={scrollRef}>
47
- <div className="space-y-4 p-4">
48
- {messages.length === 0 && !isLoading ? (
49
- <div className="flex flex-col items-center justify-center h-full text-center py-12">
50
- <Bot className="h-12 w-12 text-muted-foreground mb-4" />
51
- <h3 className="text-lg font-semibold text-foreground mb-2">
52
- Start a Conversation
53
- </h3>
54
- <p className="text-sm text-muted-foreground max-w-md">
55
- Ask me anything about the documentation, features, or get help with your project.
56
- </p>
57
- </div>
58
- ) : (
59
- messages.map((message, index) => {
60
- const isUser = message.role === Enums.ChatMessageRole.USER;
61
- const isAssistant = message.role === Enums.ChatMessageRole.ASSISTANT;
62
-
63
- return (
64
- <div
65
- key={message.id}
66
- className={`flex gap-3 ${isUser ? 'justify-end' : 'justify-start'}
67
- animate-in fade-in slide-in-from-bottom-2 duration-300`}
68
- style={{ animationDelay: `${index * 50}ms` }}
69
- >
70
- {/* Avatar */}
71
- {isAssistant && (
72
- <Avatar className="h-8 w-8 shrink-0">
73
- <div className="flex h-full w-full items-center justify-center bg-primary text-primary-foreground">
74
- <Bot className="h-5 w-5" />
75
- </div>
76
- </Avatar>
77
- )}
78
-
79
- {/* Message Content */}
80
- <div
81
- className={`flex flex-col gap-2 flex-1 max-w-[80%] ${
82
- isUser ? 'items-end' : 'items-start'
83
- }`}
84
- >
85
- {/* Message Bubble */}
86
- <Card
87
- className={`${
88
- isUser
89
- ? 'bg-primary text-primary-foreground'
90
- : 'bg-muted'
91
- } transition-all duration-200 hover:shadow-md`}
92
- >
93
- <CardContent className="p-3">
94
- <div className="text-sm whitespace-pre-wrap break-words">
95
- {message.content}
96
- </div>
97
- </CardContent>
98
- </Card>
99
-
100
- {/* Timestamp */}
101
- {showTimestamps && message.created_at && (
102
- <span className="text-xs text-muted-foreground px-1">
103
- {formatTimestamp(message.created_at)}
104
- </span>
105
- )}
106
-
107
- {/* Sources */}
108
- {showSources &&
109
- isAssistant &&
110
- message.sources &&
111
- message.sources.length > 0 && (
112
- <div className="flex flex-wrap gap-2 px-1 animate-in fade-in slide-in-from-left-2 duration-300 delay-100">
113
- {message.sources.map((source, idx) => (
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>
126
- ))}
127
- </div>
128
- )}
129
- </div>
130
-
131
- {/* User Avatar */}
132
- {isUser && (
133
- <Avatar className="h-8 w-8 shrink-0">
134
- {user?.avatar && <AvatarImage src={user.avatar} alt={user.display_username || user.email || 'User'} />}
135
- <AvatarFallback className="bg-primary/10 text-primary font-semibold">
136
- {user?.display_username?.charAt(0)?.toUpperCase() ||
137
- user?.email?.charAt(0)?.toUpperCase() ||
138
- <User className="h-5 w-5" />}
139
- </AvatarFallback>
140
- </Avatar>
141
- )}
142
- </div>
143
- );
144
- })
145
- )}
146
-
147
- {/* Loading Indicator */}
148
- {isLoading && (
149
- <div className="flex gap-3 justify-start animate-in fade-in slide-in-from-bottom-2 duration-300">
150
- <Avatar className="h-8 w-8 shrink-0 animate-pulse">
151
- <div className="flex h-full w-full items-center justify-center bg-primary text-primary-foreground">
152
- <Bot className="h-5 w-5 animate-bounce" />
153
- </div>
154
- </Avatar>
155
- <Card className="bg-muted animate-pulse">
156
- <CardContent className="p-3">
157
- <div className="flex items-center gap-2 text-sm text-muted-foreground">
158
- <Loader2 className="h-4 w-4 animate-spin" />
159
- <span className="animate-pulse">Thinking...</span>
160
- </div>
161
- </CardContent>
162
- </Card>
163
- </div>
164
- )}
165
- </div>
166
- </ScrollArea>
167
- );
168
- };
169
-
@@ -1,192 +0,0 @@
1
- /**
2
- * Session List Component
3
- * Drawer with list of chat sessions
4
- */
5
-
6
- 'use client';
7
-
8
- import React, { useCallback, useRef, useEffect } from 'react';
9
- import {
10
- Sheet,
11
- SheetContent,
12
- SheetHeader,
13
- SheetTitle,
14
- SheetDescription,
15
- Button,
16
- ScrollArea,
17
- Badge,
18
- } from '@djangocfg/ui-nextjs';
19
- import { MessageSquare, Clock, Archive, Trash2, Loader2 } from 'lucide-react';
20
- import { useKnowbaseSessionsContext } from '@djangocfg/layouts/contexts';
21
- import { useInfiniteSessions } from '../hooks/useInfiniteSessions';
22
- import type { SessionListProps } from '../types';
23
-
24
- // ─────────────────────────────────────────────────────────────────────────
25
- // Session List Component
26
- // ─────────────────────────────────────────────────────────────────────────
27
-
28
- export const SessionList: React.FC<SessionListProps> = ({
29
- isOpen,
30
- onClose,
31
- onSelectSession,
32
- className = '',
33
- }) => {
34
- const { deleteSession, archiveSession } = useKnowbaseSessionsContext();
35
- const {
36
- sessions,
37
- isLoading,
38
- isLoadingMore,
39
- hasMore,
40
- loadMore,
41
- } = useInfiniteSessions();
42
-
43
- const scrollRef = useRef<HTMLDivElement>(null);
44
-
45
- // Handle scroll to load more
46
- const handleScroll = useCallback(() => {
47
- if (!scrollRef.current || isLoadingMore || !hasMore) return;
48
-
49
- const scrollContainer = scrollRef.current.querySelector('[data-radix-scroll-area-viewport]');
50
- if (!scrollContainer) return;
51
-
52
- const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
53
- const scrollPercentage = (scrollTop + clientHeight) / scrollHeight;
54
-
55
- // Load more when scrolled 80% down
56
- if (scrollPercentage > 0.8) {
57
- loadMore();
58
- }
59
- }, [hasMore, isLoadingMore, loadMore]);
60
-
61
- // Attach scroll listener
62
- useEffect(() => {
63
- const scrollContainer = scrollRef.current?.querySelector('[data-radix-scroll-area-viewport]');
64
- if (!scrollContainer) return;
65
-
66
- scrollContainer.addEventListener('scroll', handleScroll);
67
- return () => scrollContainer.removeEventListener('scroll', handleScroll);
68
- }, [handleScroll]);
69
-
70
- const formatDate = (dateString: string) => {
71
- const date = new Date(dateString);
72
- return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
73
- };
74
-
75
- return (
76
- <Sheet open={isOpen} onOpenChange={onClose}>
77
- <SheetContent side="left" className={`w-[400px] sm:w-[540px] ${className}`}>
78
- <SheetHeader>
79
- <SheetTitle>Chat Sessions</SheetTitle>
80
- <SheetDescription>
81
- View and manage your chat history
82
- </SheetDescription>
83
- </SheetHeader>
84
-
85
- <ScrollArea className="h-[calc(100vh-120px)] mt-6" viewportRef={scrollRef}>
86
- {isLoading ? (
87
- <div className="flex items-center justify-center py-12">
88
- <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
89
- </div>
90
- ) : !sessions || sessions.length === 0 ? (
91
- <div className="flex flex-col items-center justify-center py-12 text-center">
92
- <MessageSquare className="h-12 w-12 text-muted-foreground mb-4" />
93
- <h3 className="text-lg font-semibold text-foreground mb-2">
94
- No Sessions Yet
95
- </h3>
96
- <p className="text-sm text-muted-foreground max-w-md">
97
- Start a new conversation to create your first session.
98
- </p>
99
- </div>
100
- ) : (
101
- <div className="space-y-2">
102
- {sessions.map((session, index) => (
103
- <div
104
- key={session.id}
105
- className="group relative flex items-start gap-3 p-4 border rounded-sm
106
- hover:bg-muted/50 hover:border-primary/50 hover:shadow-md
107
- transition-all duration-200 cursor-pointer
108
- animate-in fade-in slide-in-from-left-2"
109
- style={{ animationDelay: `${index * 50}ms` }}
110
- onClick={() => {
111
- onSelectSession(session.id);
112
- onClose();
113
- }}
114
- >
115
- {/* Session Icon */}
116
- <div className="shrink-0 mt-1">
117
- <MessageSquare className="h-5 w-5 text-muted-foreground" />
118
- </div>
119
-
120
- {/* Session Info */}
121
- <div className="flex-1 min-w-0">
122
- <div className="flex items-start justify-between gap-2 mb-1">
123
- <h4 className="font-medium text-sm truncate">
124
- {session.title || 'Untitled Session'}
125
- </h4>
126
- {session.is_active && (
127
- <Badge variant="default" className="shrink-0 text-xs">
128
- Active
129
- </Badge>
130
- )}
131
- </div>
132
-
133
- <div className="flex items-center gap-2 text-xs text-muted-foreground">
134
- <Clock className="h-3 w-3" />
135
- <span>{formatDate(session.created_at)}</span>
136
- </div>
137
- </div>
138
-
139
- {/* Actions */}
140
- <div className="shrink-0 flex items-center gap-1 opacity-0 group-hover:opacity-100
141
- transition-all duration-200 group-hover:translate-x-0 translate-x-2">
142
- <Button
143
- variant="ghost"
144
- size="sm"
145
- className="h-8 w-8 p-0 hover:scale-110 active:scale-95 transition-transform duration-200"
146
- onClick={(e) => {
147
- e.stopPropagation();
148
- archiveSession(session.id, {});
149
- }}
150
- title="Archive"
151
- >
152
- <Archive className="h-4 w-4" />
153
- </Button>
154
- <Button
155
- variant="ghost"
156
- size="sm"
157
- className="h-8 w-8 p-0 text-destructive hover:text-destructive
158
- hover:scale-110 active:scale-95 transition-transform duration-200"
159
- onClick={(e) => {
160
- e.stopPropagation();
161
- deleteSession(session.id);
162
- }}
163
- title="Delete"
164
- >
165
- <Trash2 className="h-4 w-4" />
166
- </Button>
167
- </div>
168
- </div>
169
- ))}
170
-
171
- {/* Loading more indicator */}
172
- {isLoadingMore && (
173
- <div className="flex items-center justify-center py-6">
174
- <Loader2 className="h-5 w-5 animate-spin text-muted-foreground mr-2" />
175
- <span className="text-sm text-muted-foreground">Loading more...</span>
176
- </div>
177
- )}
178
-
179
- {/* No more sessions indicator */}
180
- {!hasMore && sessions.length > 0 && (
181
- <div className="text-center py-4">
182
- <span className="text-xs text-muted-foreground">No more sessions</span>
183
- </div>
184
- )}
185
- </div>
186
- )}
187
- </ScrollArea>
188
- </SheetContent>
189
- </Sheet>
190
- );
191
- };
192
-
@@ -1,9 +0,0 @@
1
- /**
2
- * Chat Components
3
- * Re-exports for all chat components
4
- */
5
-
6
- export { MessageList } from './MessageList';
7
- export { MessageInput } from './MessageInput';
8
- export { SessionList } from './SessionList';
9
-
@@ -1,6 +0,0 @@
1
- /**
2
- * Chat hooks
3
- */
4
-
5
- export { useInfiniteSessions } from './useInfiniteSessions';
6
-