@djangocfg/layouts 2.1.9 → 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 (106) 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/McpChat/hooks/useAIChat.ts +16 -3
  19. package/src/snippets/index.ts +0 -3
  20. package/src/auth/README.md +0 -962
  21. package/src/auth/context/AccountsContext.tsx +0 -240
  22. package/src/auth/context/AuthContext.tsx +0 -604
  23. package/src/auth/context/index.ts +0 -4
  24. package/src/auth/context/types.ts +0 -68
  25. package/src/auth/hooks/index.ts +0 -17
  26. package/src/auth/hooks/useAuthForm.ts +0 -332
  27. package/src/auth/hooks/useAuthGuard.ts +0 -25
  28. package/src/auth/hooks/useAuthRedirect.ts +0 -51
  29. package/src/auth/hooks/useAutoAuth.ts +0 -49
  30. package/src/auth/hooks/useGithubAuth.ts +0 -184
  31. package/src/auth/hooks/useLocalStorage.ts +0 -214
  32. package/src/auth/hooks/useProfileCache.ts +0 -146
  33. package/src/auth/hooks/useSessionStorage.ts +0 -189
  34. package/src/auth/index.ts +0 -10
  35. package/src/auth/middlewares/index.ts +0 -1
  36. package/src/auth/middlewares/proxy.ts +0 -32
  37. package/src/auth/server.ts +0 -6
  38. package/src/auth/utils/errors.ts +0 -34
  39. package/src/auth/utils/index.ts +0 -2
  40. package/src/auth/utils/validation.ts +0 -14
  41. package/src/contexts/LeadsContext.tsx +0 -156
  42. package/src/contexts/NewsletterContext.tsx +0 -263
  43. package/src/contexts/SupportContext.tsx +0 -256
  44. package/src/contexts/index.ts +0 -59
  45. package/src/contexts/knowbase/ChatContext.tsx +0 -174
  46. package/src/contexts/knowbase/DocumentsContext.tsx +0 -304
  47. package/src/contexts/knowbase/SessionsContext.tsx +0 -174
  48. package/src/contexts/knowbase/index.ts +0 -61
  49. package/src/contexts/payments/BalancesContext.tsx +0 -65
  50. package/src/contexts/payments/CurrenciesContext.tsx +0 -66
  51. package/src/contexts/payments/OverviewContext.tsx +0 -174
  52. package/src/contexts/payments/PaymentsContext.tsx +0 -132
  53. package/src/contexts/payments/README.md +0 -201
  54. package/src/contexts/payments/RootPaymentsContext.tsx +0 -68
  55. package/src/contexts/payments/index.ts +0 -50
  56. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -92
  57. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -291
  58. package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -290
  59. package/src/layouts/PaymentsLayout/components/index.ts +0 -2
  60. package/src/layouts/PaymentsLayout/events.ts +0 -47
  61. package/src/layouts/PaymentsLayout/index.ts +0 -16
  62. package/src/layouts/PaymentsLayout/types.ts +0 -6
  63. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -128
  64. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -142
  65. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
  66. package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -20
  67. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -276
  68. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
  69. package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -17
  70. package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -273
  71. package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
  72. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -17
  73. package/src/layouts/SupportLayout/README.md +0 -91
  74. package/src/layouts/SupportLayout/SupportLayout.tsx +0 -179
  75. package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +0 -155
  76. package/src/layouts/SupportLayout/components/MessageInput.tsx +0 -92
  77. package/src/layouts/SupportLayout/components/MessageList.tsx +0 -314
  78. package/src/layouts/SupportLayout/components/TicketCard.tsx +0 -96
  79. package/src/layouts/SupportLayout/components/TicketList.tsx +0 -153
  80. package/src/layouts/SupportLayout/components/index.ts +0 -6
  81. package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +0 -263
  82. package/src/layouts/SupportLayout/context/index.ts +0 -2
  83. package/src/layouts/SupportLayout/events.ts +0 -33
  84. package/src/layouts/SupportLayout/hooks/index.ts +0 -2
  85. package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +0 -119
  86. package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +0 -92
  87. package/src/layouts/SupportLayout/index.ts +0 -8
  88. package/src/layouts/SupportLayout/types.ts +0 -21
  89. package/src/snippets/Chat/ChatUIContext.tsx +0 -110
  90. package/src/snippets/Chat/ChatWidget.tsx +0 -476
  91. package/src/snippets/Chat/README.md +0 -122
  92. package/src/snippets/Chat/components/MessageInput.tsx +0 -124
  93. package/src/snippets/Chat/components/MessageList.tsx +0 -169
  94. package/src/snippets/Chat/components/SessionList.tsx +0 -192
  95. package/src/snippets/Chat/components/index.ts +0 -9
  96. package/src/snippets/Chat/hooks/index.ts +0 -6
  97. package/src/snippets/Chat/hooks/useInfiniteSessions.ts +0 -82
  98. package/src/snippets/Chat/index.tsx +0 -45
  99. package/src/snippets/Chat/types.ts +0 -80
  100. package/src/snippets/ContactForm/ContactForm.tsx +0 -346
  101. package/src/snippets/ContactForm/ContactFormProvider.tsx +0 -153
  102. package/src/snippets/ContactForm/ContactInfo.tsx +0 -114
  103. package/src/snippets/ContactForm/ContactPage.tsx +0 -131
  104. package/src/snippets/ContactForm/dynamic.tsx +0 -55
  105. package/src/snippets/ContactForm/index.ts +0 -34
  106. package/src/snippets/ContactForm/types.ts +0 -110
@@ -1,273 +0,0 @@
1
- /**
2
- * Transactions List Component (v2.0 - Simplified)
3
- * Display transaction history with balance changes
4
- */
5
-
6
- 'use client';
7
-
8
- import React, { useState } from 'react';
9
- import {
10
- Card,
11
- CardContent,
12
- CardHeader,
13
- CardTitle,
14
- Table,
15
- TableBody,
16
- TableCell,
17
- TableHead,
18
- TableHeader,
19
- TableRow,
20
- Button,
21
- Badge,
22
- Input,
23
- Select,
24
- SelectContent,
25
- SelectItem,
26
- SelectTrigger,
27
- SelectValue,
28
- Skeleton,
29
- } from '@djangocfg/ui-nextjs';
30
- import { History, Search, Filter, RefreshCw, ArrowUpRight, ArrowDownLeft } from 'lucide-react';
31
- import { useOverviewContext } from '@djangocfg/layouts/contexts';
32
-
33
- export const TransactionsList: React.FC = () => {
34
- const {
35
- transactions,
36
- isLoadingTransactions,
37
- refreshTransactions,
38
- } = useOverviewContext();
39
-
40
- const [searchTerm, setSearchTerm] = useState('');
41
- const [typeFilter, setTypeFilter] = useState<string>('all');
42
-
43
- // Extract transactions array from response (handle different possible structures)
44
- const transactionsList = transactions?.results || transactions?.transactions || [];
45
-
46
- const formatCurrency = (amount?: number | null) => {
47
- if (amount === null || amount === undefined) return '$0.00';
48
- return new Intl.NumberFormat('en-US', {
49
- style: 'currency',
50
- currency: 'USD',
51
- minimumFractionDigits: 2,
52
- }).format(amount);
53
- };
54
-
55
- const formatDate = (date: string | null | undefined): string => {
56
- if (!date) return 'N/A';
57
- try {
58
- return new Date(date).toLocaleString('en-US', {
59
- year: 'numeric',
60
- month: 'short',
61
- day: 'numeric',
62
- hour: '2-digit',
63
- minute: '2-digit',
64
- });
65
- } catch {
66
- return 'Invalid date';
67
- }
68
- };
69
-
70
- const getRelativeTime = (date: string | null | undefined): string => {
71
- if (!date) return 'N/A';
72
-
73
- const now = new Date();
74
- const target = new Date(date);
75
- const diffInSeconds = Math.floor((now.getTime() - target.getTime()) / 1000);
76
-
77
- if (diffInSeconds < 60) return 'Just now';
78
- if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
79
- if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
80
- return `${Math.floor(diffInSeconds / 86400)}d ago`;
81
- };
82
-
83
- const getTypeVariant = (
84
- type: string | null | undefined
85
- ): 'default' | 'destructive' | 'outline' | 'secondary' => {
86
- switch (type?.toLowerCase()) {
87
- case 'deposit':
88
- case 'credit':
89
- return 'default';
90
- case 'withdrawal':
91
- case 'debit':
92
- return 'destructive';
93
- default:
94
- return 'outline';
95
- }
96
- };
97
-
98
- const getTypeIcon = (type: string | null | undefined) => {
99
- const isDeposit = type?.toLowerCase() === 'deposit' || type?.toLowerCase() === 'credit';
100
- return isDeposit ? (
101
- <ArrowDownLeft className="h-4 w-4 text-green-600" />
102
- ) : (
103
- <ArrowUpRight className="h-4 w-4 text-red-600" />
104
- );
105
- };
106
-
107
- const handleSearch = async (value: string) => {
108
- setSearchTerm(value);
109
- await refreshTransactions();
110
- };
111
-
112
- const handleTypeFilter = async (type: string) => {
113
- setTypeFilter(type);
114
- await refreshTransactions();
115
- };
116
-
117
- // Filter transactions client-side
118
- const filteredTransactions = transactionsList.filter((transaction: any) => {
119
- const matchesSearch = searchTerm
120
- ? transaction.id?.toString().toLowerCase().includes(searchTerm.toLowerCase()) ||
121
- transaction.description?.toLowerCase().includes(searchTerm.toLowerCase()) ||
122
- transaction.type?.toLowerCase().includes(searchTerm.toLowerCase())
123
- : true;
124
-
125
- const matchesType = typeFilter !== 'all'
126
- ? transaction.type?.toLowerCase() === typeFilter.toLowerCase()
127
- : true;
128
-
129
- return matchesSearch && matchesType;
130
- });
131
-
132
- if (isLoadingTransactions) {
133
- return (
134
- <Card>
135
- <CardHeader>
136
- <CardTitle className="flex items-center gap-2">
137
- <History className="h-5 w-5" />
138
- Transaction History
139
- </CardTitle>
140
- </CardHeader>
141
- <CardContent className="space-y-3">
142
- {Array.from({ length: 5 }).map((_, i) => (
143
- <div key={i} className="flex items-center justify-between p-4 border rounded-sm">
144
- <div className="space-y-2">
145
- <Skeleton className="h-4 w-32" />
146
- <Skeleton className="h-3 w-24" />
147
- </div>
148
- <Skeleton className="h-6 w-16" />
149
- </div>
150
- ))}
151
- </CardContent>
152
- </Card>
153
- );
154
- }
155
-
156
- return (
157
- <Card>
158
- <CardHeader>
159
- <CardTitle className="flex items-center justify-between">
160
- <div className="flex items-center gap-2">
161
- <History className="h-5 w-5" />
162
- Transaction History
163
- </div>
164
- <Button variant="outline" size="sm" onClick={refreshTransactions} disabled={isLoadingTransactions}>
165
- <RefreshCw className={`h-4 w-4 mr-2 ${isLoadingTransactions ? 'animate-spin' : ''}`} />
166
- Refresh
167
- </Button>
168
- </CardTitle>
169
- </CardHeader>
170
-
171
- <CardContent className="space-y-4">
172
- {/* Filters */}
173
- <div className="flex flex-col sm:flex-row gap-4">
174
- <div className="relative flex-1">
175
- <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
176
- <Input
177
- placeholder="Search by ID, description, or type..."
178
- value={searchTerm}
179
- onChange={(e) => handleSearch(e.target.value)}
180
- className="pl-10"
181
- />
182
- </div>
183
-
184
- <Select value={typeFilter} onValueChange={handleTypeFilter}>
185
- <SelectTrigger className="w-full sm:w-48">
186
- <Filter className="h-4 w-4 mr-2" />
187
- <SelectValue placeholder="Filter by type" />
188
- </SelectTrigger>
189
- <SelectContent>
190
- <SelectItem value="all">All Types</SelectItem>
191
- <SelectItem value="deposit">Deposits</SelectItem>
192
- <SelectItem value="withdrawal">Withdrawals</SelectItem>
193
- </SelectContent>
194
- </Select>
195
- </div>
196
-
197
- {/* Transactions Table */}
198
- {filteredTransactions.length === 0 ? (
199
- <div className="text-center py-12">
200
- <div className="w-16 h-16 mx-auto mb-4 bg-muted rounded-full flex items-center justify-center">
201
- <History className="w-8 h-8 text-muted-foreground" />
202
- </div>
203
- <h3 className="text-lg font-semibold mb-2">No Transactions Found</h3>
204
- <p className="text-muted-foreground">
205
- {searchTerm || typeFilter !== 'all'
206
- ? 'No transactions match your current filters'
207
- : "You don't have any transactions yet"}
208
- </p>
209
- </div>
210
- ) : (
211
- <div className="rounded-md border">
212
- <Table>
213
- <TableHeader>
214
- <TableRow>
215
- <TableHead>Date & Time</TableHead>
216
- <TableHead>Type</TableHead>
217
- <TableHead>Amount</TableHead>
218
- <TableHead>Balance After</TableHead>
219
- <TableHead>Description</TableHead>
220
- <TableHead>Reference</TableHead>
221
- </TableRow>
222
- </TableHeader>
223
- <TableBody>
224
- {filteredTransactions.map((transaction: any, index: number) => {
225
- const isDeposit = transaction.type?.toLowerCase() === 'deposit' || transaction.type?.toLowerCase() === 'credit';
226
- return (
227
- <TableRow key={transaction.id || index}>
228
- <TableCell>
229
- <div>
230
- <div className="font-medium">
231
- {formatDate(transaction.created_at || transaction.timestamp)}
232
- </div>
233
- <div className="text-sm text-muted-foreground">
234
- {getRelativeTime(transaction.created_at || transaction.timestamp)}
235
- </div>
236
- </div>
237
- </TableCell>
238
- <TableCell>
239
- <div className="flex items-center gap-2">
240
- {getTypeIcon(transaction.type)}
241
- <Badge variant={getTypeVariant(transaction.type)}>
242
- {transaction.type || 'Unknown'}
243
- </Badge>
244
- </div>
245
- </TableCell>
246
- <TableCell className="font-mono font-semibold">
247
- <span className={isDeposit ? 'text-green-600' : 'text-red-600'}>
248
- {isDeposit ? '+' : '-'}
249
- {formatCurrency(Math.abs(transaction.amount || transaction.amount_usd || 0))}
250
- </span>
251
- </TableCell>
252
- <TableCell className="font-mono">
253
- {formatCurrency(transaction.balance_after || 0)}
254
- </TableCell>
255
- <TableCell className="text-sm">
256
- {transaction.description || transaction.note || 'No description'}
257
- </TableCell>
258
- <TableCell className="font-mono text-sm text-muted-foreground">
259
- {transaction.reference || transaction.payment_id
260
- ? `${(transaction.reference || transaction.payment_id).toString().slice(0, 8)}...`
261
- : 'N/A'}
262
- </TableCell>
263
- </TableRow>
264
- );
265
- })}
266
- </TableBody>
267
- </Table>
268
- </div>
269
- )}
270
- </CardContent>
271
- </Card>
272
- );
273
- };
@@ -1 +0,0 @@
1
- export { TransactionsList } from './TransactionsList';
@@ -1,17 +0,0 @@
1
- /**
2
- * Transactions View (v2.0 - Simplified)
3
- * View transaction history and balance changes
4
- */
5
-
6
- 'use client';
7
-
8
- import React from 'react';
9
- import { TransactionsList } from './components';
10
-
11
- export const TransactionsView: React.FC = () => {
12
- return (
13
- <div className="space-y-6">
14
- <TransactionsList />
15
- </div>
16
- );
17
- };
@@ -1,91 +0,0 @@
1
- # Support Layout
2
-
3
- Modern support ticket system layout with resizable panels and mobile-optimized interface.
4
-
5
- ## Features
6
-
7
- - ✅ **Desktop**: Resizable split-panel view (ticket list | conversation)
8
- - ✅ **Mobile**: Single-column navigation with back/forward flow
9
- - ✅ **Real-time**: Auto-refresh messages after sending
10
- - ✅ **Event-driven**: Dialog management via custom events
11
- - ✅ **Type-safe**: Full TypeScript support with generated API types
12
- - ✅ **Smart UI**: Unread counters, status badges, relative timestamps
13
-
14
- ## Architecture
15
-
16
- ```
17
- SupportLayout
18
- ├── SupportLayoutProvider (UI state + events wrapper)
19
- │ └── SupportProvider (API context from @djangocfg/api)
20
- │ └── AccountsProvider (for user.id in ticket creation)
21
- └── Components
22
- ├── TicketList (scrollable ticket cards)
23
- ├── MessageList (conversation bubbles)
24
- ├── MessageInput (with keyboard shortcuts)
25
- └── CreateTicketDialog (event-driven)
26
- ```
27
-
28
- ## Usage
29
-
30
- ```tsx
31
- import { SupportLayout } from '@djangocfg/layouts';
32
-
33
- export default function SupportPage() {
34
- return <SupportLayout />;
35
- }
36
- ```
37
-
38
- ## Event-based Dialog Opening
39
-
40
- ```tsx
41
- import { openCreateTicketDialog } from '@djangocfg/layouts';
42
-
43
- function MyComponent() {
44
- return (
45
- <Button onClick={openCreateTicketDialog}>
46
- Create Ticket
47
- </Button>
48
- );
49
- }
50
- ```
51
-
52
- ## API Integration
53
-
54
- Uses generated SWR hooks from `@djangocfg/api/cfg/contexts`:
55
- - `useSupportContext()` - Tickets CRUD, messages CRUD
56
- - Automatic cache revalidation after mutations
57
- - Type-safe request/response handling
58
-
59
- ## Mobile Optimization
60
-
61
- - Auto-detects screen width (≤768px)
62
- - Single-column navigation when mobile
63
- - Back button to return to ticket list
64
- - Optimized touch targets
65
-
66
- ## Key Components
67
-
68
- ### TicketCard
69
- - Status badges with color-coding
70
- - Unread message counters
71
- - Relative timestamps
72
- - Click to select
73
-
74
- ### MessageList
75
- - Auto-scroll to latest message
76
- - User vs. Admin message styling
77
- - Avatar placeholders
78
- - Timestamp formatting
79
-
80
- ### MessageInput
81
- - Multi-line support (Shift+Enter)
82
- - Submit on Enter
83
- - Disabled when ticket closed
84
- - Loading states
85
-
86
- ### CreateTicketDialog
87
- - Subject + initial message
88
- - Zod validation
89
- - Auto-selects created ticket
90
- - Toast notifications
91
-
@@ -1,179 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Support Layout
4
- * Modern support layout with resizable panels for desktop and mobile-optimized view
5
- */
6
-
7
- 'use client';
8
-
9
- import React from 'react';
10
- import { SupportProvider } from '@djangocfg/layouts/contexts';
11
- import { SupportLayoutProvider, useSupportLayoutContext } from './context';
12
- import {
13
- TicketList,
14
- MessageList,
15
- MessageInput,
16
- CreateTicketDialog,
17
- } from './components';
18
- import {
19
- Button,
20
- ResizablePanelGroup,
21
- ResizablePanel,
22
- ResizableHandle,
23
- } from '@djangocfg/ui-nextjs';
24
- import { Plus, LifeBuoy, ArrowLeft } from 'lucide-react';
25
-
26
- // ─────────────────────────────────────────────────────────────────────────
27
- // Support Layout Content (with context)
28
- // ─────────────────────────────────────────────────────────────────────────
29
-
30
- const SupportLayoutContent: React.FC = () => {
31
- const { selectedTicket, selectTicket, openCreateDialog, getUnreadCount } =
32
- useSupportLayoutContext();
33
- const [isMobile, setIsMobile] = React.useState(false);
34
-
35
- React.useEffect(() => {
36
- const checkMobile = () => setIsMobile(window.innerWidth <= 768);
37
- checkMobile();
38
- window.addEventListener('resize', checkMobile);
39
- return () => window.removeEventListener('resize', checkMobile);
40
- }, []);
41
-
42
- const unreadCount = getUnreadCount();
43
-
44
- if (isMobile) {
45
- // Mobile layout - single column with navigation
46
- return (
47
- <div className="h-screen flex flex-col overflow-hidden">
48
- {/* Mobile Header */}
49
- <div className="flex items-center justify-between p-4 border-b bg-background flex-shrink-0">
50
- <div className="flex items-center gap-2">
51
- {selectedTicket ? (
52
- <Button
53
- variant="ghost"
54
- size="sm"
55
- onClick={() => selectTicket(null)}
56
- className="p-1"
57
- >
58
- <ArrowLeft className="h-5 w-5" />
59
- </Button>
60
- ) : (
61
- <LifeBuoy className="h-6 w-6 text-primary" />
62
- )}
63
- <h1 className="text-xl font-semibold">
64
- {selectedTicket ? selectedTicket.subject : 'Support'}
65
- </h1>
66
- {unreadCount > 0 && !selectedTicket && (
67
- <div className="h-5 w-5 bg-red-500 text-white text-xs rounded-full flex items-center justify-center">
68
- {unreadCount}
69
- </div>
70
- )}
71
- </div>
72
- {!selectedTicket && (
73
- <Button onClick={openCreateDialog} size="sm">
74
- <Plus className="h-4 w-4 mr-2" />
75
- New Ticket
76
- </Button>
77
- )}
78
- </div>
79
-
80
- {/* Mobile Content */}
81
- <div className="flex-1 min-h-0 overflow-hidden">
82
- {selectedTicket ? (
83
- // Show messages when ticket is selected
84
- <div className="h-full flex flex-col">
85
- <div className="flex-1 min-h-0 overflow-hidden">
86
- <MessageList />
87
- </div>
88
- <div className="flex-shrink-0">
89
- <MessageInput />
90
- </div>
91
- </div>
92
- ) : (
93
- // Show ticket list when no ticket is selected
94
- <TicketList />
95
- )}
96
- </div>
97
-
98
- {/* Dialog */}
99
- <CreateTicketDialog />
100
- </div>
101
- );
102
- }
103
-
104
- // Desktop layout - resizable panels
105
- return (
106
- <div className="h-screen flex flex-col overflow-hidden">
107
- {/* Desktop Header */}
108
- <div className="flex items-center justify-between p-6 border-b bg-background flex-shrink-0">
109
- <div className="flex items-center gap-3">
110
- <LifeBuoy className="h-7 w-7 text-primary" />
111
- <div>
112
- <h1 className="text-2xl font-bold">Support Center</h1>
113
- <p className="text-sm text-muted-foreground">Get help from our support team</p>
114
- </div>
115
- {unreadCount > 0 && (
116
- <div className="h-6 w-6 bg-red-500 text-white text-sm rounded-full flex items-center justify-center">
117
- {unreadCount}
118
- </div>
119
- )}
120
- </div>
121
-
122
- <Button onClick={openCreateDialog}>
123
- <Plus className="h-4 w-4 mr-2" />
124
- New Ticket
125
- </Button>
126
- </div>
127
-
128
- {/* Desktop Content */}
129
- <div className="flex-1 min-h-0 overflow-hidden">
130
- <ResizablePanelGroup direction="horizontal" className="h-full">
131
- {/* Ticket List Panel */}
132
- <ResizablePanel defaultSize={35} minSize={25} maxSize={50}>
133
- <div className="h-full border-r overflow-hidden">
134
- <TicketList />
135
- </div>
136
- </ResizablePanel>
137
-
138
- <ResizableHandle withHandle className="hover:bg-accent transition-colors" />
139
-
140
- {/* Messages Panel */}
141
- <ResizablePanel defaultSize={65} minSize={50}>
142
- <div className="h-full flex flex-col overflow-hidden">
143
- <div className="flex-1 min-h-0 overflow-hidden">
144
- <MessageList />
145
- </div>
146
- <div className="flex-shrink-0">
147
- <MessageInput />
148
- </div>
149
- </div>
150
- </ResizablePanel>
151
- </ResizablePanelGroup>
152
- </div>
153
-
154
- {/* Dialog */}
155
- <CreateTicketDialog />
156
- </div>
157
- );
158
- };
159
-
160
- // ─────────────────────────────────────────────────────────────────────────
161
- // Support Layout (with providers)
162
- // ─────────────────────────────────────────────────────────────────────────
163
-
164
- export interface SupportLayoutProps {
165
- children?: React.ReactNode;
166
- }
167
-
168
- export const SupportLayout: React.FC<SupportLayoutProps> = () => {
169
- return (
170
- <div className="h-screen w-full overflow-hidden">
171
- <SupportProvider>
172
- <SupportLayoutProvider>
173
- <SupportLayoutContent />
174
- </SupportLayoutProvider>
175
- </SupportProvider>
176
- </div>
177
- );
178
- };
179
-