@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,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
-