@djangocfg/layouts 1.0.3 → 1.0.4

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 (20) hide show
  1. package/package.json +5 -5
  2. package/src/layouts/AppLayout/layouts/AuthLayout/AuthHelp.tsx +2 -2
  3. package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +6 -6
  4. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +1 -1
  5. package/src/layouts/AppLayout/layouts/PublicLayout/components/DesktopUserMenu.tsx +6 -6
  6. package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +1 -1
  7. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenu.tsx +43 -133
  8. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenuUserCard.tsx +150 -0
  9. package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +2 -2
  10. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +46 -24
  11. package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +94 -25
  12. package/src/layouts/PaymentsLayout/context/RootPaymentsContext.tsx +6 -11
  13. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeysList.tsx +2 -2
  14. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +2 -2
  15. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +3 -3
  16. package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +2 -3
  17. package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +2 -3
  18. package/src/snippets/Chat/components/SessionList.tsx +1 -1
  19. package/src/snippets/Chat/hooks/useInfiniteSessions.ts +2 -2
  20. package/src/snippets/VideoPlayer/VideoPlayer.tsx +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Layout system and components for Unrealon applications",
5
5
  "author": {
6
6
  "name": "DjangoCFG",
@@ -53,9 +53,9 @@
53
53
  "check": "tsc --noEmit"
54
54
  },
55
55
  "peerDependencies": {
56
- "@djangocfg/api": "^1.0.3",
57
- "@djangocfg/og-image": "^1.0.3",
58
- "@djangocfg/ui": "^1.0.3",
56
+ "@djangocfg/api": "^1.0.4",
57
+ "@djangocfg/og-image": "^1.0.4",
58
+ "@djangocfg/ui": "^1.0.4",
59
59
  "@hookform/resolvers": "^5.2.0",
60
60
  "consola": "^3.4.2",
61
61
  "lucide-react": "^0.468.0",
@@ -76,7 +76,7 @@
76
76
  "vidstack": "0.6.15"
77
77
  },
78
78
  "devDependencies": {
79
- "@djangocfg/typescript-config": "^1.0.3",
79
+ "@djangocfg/typescript-config": "^1.0.4",
80
80
  "@types/node": "^24.7.2",
81
81
  "@types/react": "19.2.2",
82
82
  "@types/react-dom": "19.2.1",
@@ -51,7 +51,7 @@ export const AuthHelp: React.FC<AuthHelpProps> = ({
51
51
  if (variant === 'compact') {
52
52
  return (
53
53
  <div
54
- className={`flex items-center justify-between p-3 bg-muted/30 rounded-lg border border-border ${className}`}
54
+ className={`flex items-center justify-between p-3 bg-muted/30 rounded-sm border border-border ${className}`}
55
55
  >
56
56
  <div className="flex items-center space-x-2">
57
57
  {getChannelIcon()}
@@ -78,7 +78,7 @@ export const AuthHelp: React.FC<AuthHelpProps> = ({
78
78
 
79
79
  return (
80
80
  <div
81
- className={`space-y-3 p-3 bg-muted/30 rounded-lg border border-border ${className}`}
81
+ className={`space-y-3 p-3 bg-muted/30 rounded-sm border border-border ${className}`}
82
82
  >
83
83
  <div className="flex items-start space-x-2">
84
84
  {getChannelIcon()}
@@ -80,27 +80,27 @@ export const OTPForm: React.FC = () => {
80
80
  <InputOTPGroup className="gap-2">
81
81
  <InputOTPSlot
82
82
  index={0}
83
- className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-lg shadow-sm"
83
+ className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-sm shadow-sm"
84
84
  />
85
85
  <InputOTPSlot
86
86
  index={1}
87
- className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-lg shadow-sm"
87
+ className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-sm shadow-sm"
88
88
  />
89
89
  <InputOTPSlot
90
90
  index={2}
91
- className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-lg shadow-sm"
91
+ className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-sm shadow-sm"
92
92
  />
93
93
  <InputOTPSlot
94
94
  index={3}
95
- className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-lg shadow-sm"
95
+ className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-sm shadow-sm"
96
96
  />
97
97
  <InputOTPSlot
98
98
  index={4}
99
- className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-lg shadow-sm"
99
+ className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-sm shadow-sm"
100
100
  />
101
101
  <InputOTPSlot
102
102
  index={5}
103
- className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-lg shadow-sm"
103
+ className="h-12 w-12 text-lg font-semibold border-2 border-border bg-background rounded-sm shadow-sm"
104
104
  />
105
105
  </InputOTPGroup>
106
106
  </InputOTP>
@@ -81,7 +81,7 @@ export function DashboardSidebar() {
81
81
  className={isMobile ? "h-10 w-10 flex-shrink-0" : "h-8 w-8 flex-shrink-0"}
82
82
  />
83
83
  ) : (
84
- <div className={isMobile ? "h-10 w-10 bg-primary rounded-lg flex items-center justify-center flex-shrink-0" : "h-8 w-8 bg-primary rounded-lg flex items-center justify-center flex-shrink-0"}>
84
+ <div className={isMobile ? "h-10 w-10 bg-primary rounded-sm flex items-center justify-center flex-shrink-0" : "h-8 w-8 bg-primary rounded-sm flex items-center justify-center flex-shrink-0"}>
85
85
  <span className="text-primary-foreground font-bold text-sm">
86
86
  {app.name.charAt(0).toUpperCase()}
87
87
  </span>
@@ -61,7 +61,7 @@ export function DesktopUserMenu() {
61
61
  {/* User Dropdown */}
62
62
  <div className="relative">
63
63
  <button
64
- className="flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors text-foreground hover:text-primary hover:bg-accent/50"
64
+ className="flex items-center gap-2 px-3 py-2 rounded-sm text-sm font-medium transition-colors text-foreground hover:text-primary hover:bg-accent/50"
65
65
  onClick={toggleUserMenu}
66
66
  aria-haspopup="true"
67
67
  aria-expanded={userMenuOpen}
@@ -85,15 +85,15 @@ export function DesktopUserMenu() {
85
85
  />
86
86
  {/* Dropdown */}
87
87
  <div
88
- className="absolute top-full right-0 mt-2 w-48 rounded-lg shadow-lg backdrop-blur-xl z-[9996] bg-card/95 border border-border/50"
88
+ className="absolute top-full right-0 mt-2 w-48 rounded-sm shadow-sm backdrop-blur-xl z-[9996] bg-popover border border-border"
89
89
  role="menu"
90
90
  aria-label="User menu"
91
91
  >
92
92
  <div className="p-2">
93
93
  {/* User info */}
94
- <div className="px-3 py-2 text-sm mb-2 border-b text-muted-foreground border-border/30">
95
- Signed in as:
96
- <div className="font-medium truncate text-foreground">
94
+ <div className="px-3 py-2 text-sm mb-2 border-b border-border">
95
+ <div className="text-muted-foreground">Signed in as:</div>
96
+ <div className="font-medium truncate text-popover-foreground mt-1">
97
97
  {user?.email}
98
98
  </div>
99
99
  </div>
@@ -113,7 +113,7 @@ export function DesktopUserMenu() {
113
113
  {/* Logout button */}
114
114
  <button
115
115
  onClick={handleLogout}
116
- className="w-full flex items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors text-destructive hover:bg-destructive/[0.1]"
116
+ className="w-full flex items-center gap-2 px-3 py-2 text-sm rounded-sm transition-colors text-destructive hover:bg-destructive/[0.1]"
117
117
  >
118
118
  <LogOut className="size-4" />
119
119
  <span>Sign out</span>
@@ -139,7 +139,7 @@ export function Footer() {
139
139
  {/* Badge */}
140
140
  {footer.badge && (
141
141
  <div className="pt-2">
142
- <span className="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-primary/10 hover:bg-primary/15 border border-primary/20 text-xs font-medium text-primary transition-colors">
142
+ <span className="inline-flex items-center gap-2 px-3 py-1.5 rounded-sm bg-primary/10 hover:bg-primary/15 border border-primary/20 text-xs font-medium text-primary transition-colors">
143
143
  <footer.badge.icon className="w-3.5 h-3.5" />
144
144
  {footer.badge.text}
145
145
  </span>
@@ -10,12 +10,11 @@
10
10
  import React from 'react';
11
11
  import { createPortal } from 'react-dom';
12
12
  import Link from 'next/link';
13
- import { Crown, LogOut, Settings, User, X } from 'lucide-react';
14
- import { ButtonLink, Card, CardContent } from '@djangocfg/ui/components';
15
- import { ThemeToggle } from '@djangocfg/ui/theme';
13
+ import { X } from 'lucide-react';
16
14
  import { useAppContext } from '../../../context';
17
15
  import { useAuth } from '../../../../../auth';
18
16
  import { useNavigation } from '../../../hooks';
17
+ import { MobileMenuUserCard } from './MobileMenuUserCard';
19
18
 
20
19
  /**
21
20
  * Mobile Menu Component
@@ -37,34 +36,47 @@ export function MobileMenu() {
37
36
 
38
37
  const { app, publicLayout, routes } = config;
39
38
 
40
- // Animation state
41
- const [isAnimating, setIsAnimating] = React.useState(false);
39
+ // Track if we should render (stays true during close animation)
40
+ const [shouldRender, setShouldRender] = React.useState(false);
42
41
 
43
- // Trigger animation when menu opens
42
+ // Track animation state separately
43
+ const [isOpen, setIsOpen] = React.useState(false);
44
+
45
+ // Handle opening
44
46
  React.useEffect(() => {
45
47
  if (mobileMenuOpen) {
46
- // Small delay to trigger animation
47
- setTimeout(() => setIsAnimating(true), 10);
48
+ setShouldRender(true);
49
+ // Trigger animation after render
50
+ requestAnimationFrame(() => {
51
+ requestAnimationFrame(() => {
52
+ setIsOpen(true);
53
+ });
54
+ });
48
55
  } else {
49
- setIsAnimating(false);
56
+ // Start close animation
57
+ setIsOpen(false);
58
+ // Wait for animation to finish before unmounting
59
+ const timer = setTimeout(() => {
60
+ setShouldRender(false);
61
+ }, 300);
62
+ return () => clearTimeout(timer);
50
63
  }
51
64
  }, [mobileMenuOpen]);
52
65
 
53
66
  const handleLogout = () => {
54
67
  logout();
55
- handleClose();
68
+ closeMobileMenu();
56
69
  };
57
70
 
58
71
  const handleClose = () => {
59
- setIsAnimating(false);
60
- setTimeout(closeMobileMenu, 300); // Wait for animation
72
+ closeMobileMenu();
61
73
  };
62
74
 
63
75
  const handleNavigate = () => {
64
- handleClose();
76
+ closeMobileMenu();
65
77
  };
66
78
 
67
- if (!mobileMenuOpen) return null;
79
+ if (!shouldRender) return null;
68
80
 
69
81
  // Portal to body to avoid z-index and positioning issues
70
82
  if (typeof window === 'undefined') return null;
@@ -73,8 +85,8 @@ export function MobileMenu() {
73
85
  <>
74
86
  {/* Backdrop with fade animation */}
75
87
  <div
76
- className={`fixed inset-0 z-[9998] bg-black/50 backdrop-blur-sm transition-opacity duration-300 lg:hidden ${
77
- isAnimating ? 'opacity-100' : 'opacity-0'
88
+ className={`fixed inset-0 z-[150] bg-black/50 backdrop-blur-sm transition-opacity duration-300 ease-in-out lg:hidden ${
89
+ isOpen ? 'opacity-100' : 'opacity-0'
78
90
  }`}
79
91
  onClick={handleClose}
80
92
  aria-hidden="true"
@@ -82,8 +94,8 @@ export function MobileMenu() {
82
94
 
83
95
  {/* Menu Content with slide animation */}
84
96
  <div
85
- className={`fixed top-0 right-0 bottom-0 w-80 z-[9999] bg-card backdrop-blur-xl border-l border-border shadow-2xl transition-transform duration-300 ease-out lg:hidden ${
86
- isAnimating ? 'translate-x-0' : 'translate-x-full'
97
+ className={`fixed top-0 right-0 bottom-0 w-80 z-[200] bg-popover border-l border-border shadow-2xl transition-transform duration-300 ease-in-out lg:hidden ${
98
+ isOpen ? 'translate-x-0' : 'translate-x-full'
87
99
  }`}
88
100
  role="dialog"
89
101
  aria-modal="true"
@@ -104,7 +116,7 @@ export function MobileMenu() {
104
116
  </div>
105
117
  <button
106
118
  onClick={handleClose}
107
- className="p-2 rounded-lg transition-colors hover:bg-accent/50"
119
+ className="p-2 rounded-sm transition-colors hover:bg-accent/50"
108
120
  aria-label="Close menu"
109
121
  >
110
122
  <X className="size-5" />
@@ -113,118 +125,16 @@ export function MobileMenu() {
113
125
 
114
126
  {/* Scrollable Content */}
115
127
  <div className="flex-1 overflow-y-auto p-4 space-y-6">
116
- {/* User Menu Card - Authenticated */}
117
- {isAuthenticated ? (
118
- <Card className="border-primary/20 shadow-lg bg-accent/30">
119
- <CardContent className="p-4">
120
- {/* User Info Header */}
121
- <div className="flex items-center gap-3 mb-4 p-3 rounded-lg border border-border bg-accent/50">
122
- <div className="w-10 h-10 rounded-full flex items-center justify-center bg-primary flex-shrink-0 overflow-hidden">
123
- {user?.avatar ? (
124
- <img
125
- src={user.avatar}
126
- alt={user?.email || 'User'}
127
- className="w-10 h-10 rounded-full object-cover"
128
- />
129
- ) : (
130
- <User className="w-5 h-5 text-primary-foreground" />
131
- )}
132
- </div>
133
- <div className="flex-1 min-w-0">
134
- <p className="text-xs font-medium uppercase tracking-wider text-muted-foreground">
135
- Signed in as
136
- </p>
137
- <p className="text-sm font-semibold truncate text-foreground">
138
- {user?.email}
139
- </p>
140
- </div>
141
- <div className="flex items-center gap-1">
142
- <div className="size-2 rounded-full animate-pulse bg-green-500"></div>
143
- <span className="text-xs font-medium text-green-600 dark:text-green-400">
144
- Active
145
- </span>
146
- </div>
147
- </div>
148
-
149
- {/* Action Buttons */}
150
- <div className="space-y-3">
151
- {/* Dashboard link */}
152
- {publicLayout.userMenu.dashboardPath && (
153
- <ButtonLink
154
- href={publicLayout.userMenu.dashboardPath}
155
- variant="default"
156
- size="sm"
157
- className="w-full justify-center"
158
- onClick={handleNavigate}
159
- >
160
- <Crown className="w-4 h-4 mr-2" />
161
- Dashboard
162
- </ButtonLink>
163
- )}
164
-
165
- {/* Profile link */}
166
- <ButtonLink
167
- href={publicLayout.userMenu.profilePath}
168
- variant="outline"
169
- size="sm"
170
- className="w-full justify-center"
171
- onClick={handleNavigate}
172
- >
173
- <Settings className="w-4 h-4 mr-2" />
174
- Profile Settings
175
- </ButtonLink>
176
-
177
- {/* Logout */}
178
- <button
179
- onClick={handleLogout}
180
- className="w-full flex items-center justify-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors border text-destructive border-destructive/30 hover:bg-destructive/10"
181
- >
182
- <LogOut className="w-4 h-4" />
183
- Sign Out
184
- </button>
185
-
186
- {/* Theme toggle */}
187
- <div className="flex justify-center pt-2 border-t border-border/30">
188
- <ThemeToggle />
189
- </div>
190
- </div>
191
- </CardContent>
192
- </Card>
193
- ) : (
194
- /* Guest Card */
195
- <Card className="border-border bg-accent/30">
196
- <CardContent className="p-4">
197
- <div className="text-center space-y-4">
198
- <div className="w-12 h-12 rounded-full flex items-center justify-center mx-auto bg-muted flex-shrink-0">
199
- <User className="w-6 h-6 text-muted-foreground" />
200
- </div>
201
- <div>
202
- <p className="text-sm font-medium mb-1 text-foreground">
203
- Welcome!
204
- </p>
205
- <p className="text-xs text-muted-foreground">
206
- Sign in to access your dashboard
207
- </p>
208
- </div>
209
- <ButtonLink
210
- href={routes.auth}
211
- variant="default"
212
- size="lg"
213
- className="w-full justify-center"
214
- onClick={handleNavigate}
215
- >
216
- <User className="w-5 h-5 mr-2" />
217
- Sign In
218
- </ButtonLink>
219
-
220
- {/* Theme toggle */}
221
- <div className="flex justify-center pt-2 border-t border-border/30">
222
- <ThemeToggle />
223
- </div>
224
- </div>
225
- </CardContent>
226
- </Card>
227
- )}
128
+ {/* User Menu Card */}
129
+ <MobileMenuUserCard
130
+ isAuthenticated={isAuthenticated}
131
+ user={user}
132
+ dashboardPath={publicLayout.userMenu.dashboardPath}
133
+ profilePath={publicLayout.userMenu.profilePath}
134
+ authPath={routes.auth}
135
+ onLogout={handleLogout}
136
+ onNavigate={handleNavigate}
137
+ />
228
138
 
229
139
  {/* Navigation Sections */}
230
140
  <div className="space-y-6">
@@ -238,7 +148,7 @@ export function MobileMenu() {
238
148
  <div key={section.title} className="space-y-2">
239
149
  <Link
240
150
  href={item.path}
241
- className={`block px-4 py-3 rounded-lg text-base font-medium transition-colors ${
151
+ className={`block px-4 py-3 rounded-sm text-base font-medium transition-colors ${
242
152
  isActive(item.path)
243
153
  ? 'text-primary border border-primary/20 bg-primary/[0.1]'
244
154
  : 'text-muted-foreground hover:text-primary hover:bg-accent/50'
@@ -262,7 +172,7 @@ export function MobileMenu() {
262
172
  <Link
263
173
  key={item.path}
264
174
  href={item.path}
265
- className={`block px-4 py-3 rounded-lg text-base font-medium transition-colors ${
175
+ className={`block px-4 py-3 rounded-sm text-base font-medium transition-colors ${
266
176
  isActive(item.path)
267
177
  ? 'bg-accent text-accent-foreground'
268
178
  : 'text-foreground hover:bg-accent hover:text-accent-foreground'
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Mobile Menu User Card Component
3
+ *
4
+ * Displays user information and action buttons in mobile menu
5
+ * - Authenticated: shows user info with dashboard, profile, and logout
6
+ * - Guest: shows welcome message with sign in button
7
+ */
8
+
9
+ 'use client';
10
+
11
+ import React from 'react';
12
+ import { Crown, LogOut, Settings, User } from 'lucide-react';
13
+ import { Button, ButtonLink, Card, CardContent } from '@djangocfg/ui/components';
14
+ import { ThemeToggle } from '@djangocfg/ui/theme';
15
+
16
+ interface MobileMenuUserCardProps {
17
+ isAuthenticated: boolean;
18
+ user?: {
19
+ email?: string;
20
+ avatar?: string;
21
+ } | null;
22
+ dashboardPath?: string;
23
+ profilePath: string;
24
+ authPath: string;
25
+ onLogout: () => void;
26
+ onNavigate: () => void;
27
+ }
28
+
29
+ export function MobileMenuUserCard({
30
+ isAuthenticated,
31
+ user,
32
+ dashboardPath,
33
+ profilePath,
34
+ authPath,
35
+ onLogout,
36
+ onNavigate,
37
+ }: MobileMenuUserCardProps) {
38
+ if (isAuthenticated) {
39
+ return (
40
+ <Card className="border-primary/20 shadow-lg !bg-accent/50">
41
+ <CardContent className="p-4">
42
+ {/* User Info Header */}
43
+ <div className="flex items-center gap-3 mb-4 p-3 rounded-sm border border-border bg-accent/70">
44
+ <div className="w-10 h-10 rounded-full flex items-center justify-center bg-primary flex-shrink-0 overflow-hidden relative">
45
+ {user?.avatar ? (
46
+ <img
47
+ src={user.avatar}
48
+ alt={user?.email || 'User'}
49
+ className="w-10 h-10 rounded-full object-cover"
50
+ />
51
+ ) : (
52
+ <User className="w-5 h-5 text-primary-foreground" />
53
+ )}
54
+ {/* Active indicator */}
55
+ <div className="absolute -bottom-0.5 -right-0.5 size-3 rounded-full bg-green-500 border-2 border-background" />
56
+ </div>
57
+ <div className="flex-1 min-w-0">
58
+ <p className="text-xs font-medium uppercase tracking-wider text-muted-foreground">
59
+ Signed in as
60
+ </p>
61
+ <p className="text-sm font-semibold truncate text-foreground">
62
+ {user?.email}
63
+ </p>
64
+ </div>
65
+ </div>
66
+
67
+ {/* Action Buttons */}
68
+ <div className="space-y-3">
69
+ {/* Dashboard link */}
70
+ {dashboardPath && (
71
+ <ButtonLink
72
+ href={dashboardPath}
73
+ variant="default"
74
+ size="sm"
75
+ className="w-full h-9"
76
+ onClick={onNavigate}
77
+ >
78
+ <Crown className="w-4 h-4 mr-2" />
79
+ Dashboard
80
+ </ButtonLink>
81
+ )}
82
+
83
+ {/* Quick Actions - Icons only */}
84
+ <div className="flex items-center justify-center gap-2 pt-3 mt-1 border-t border-border/30">
85
+ {/* Profile Settings */}
86
+ <ButtonLink
87
+ href={profilePath}
88
+ variant="ghost"
89
+ size="icon"
90
+ className="h-9 w-9"
91
+ onClick={onNavigate}
92
+ aria-label="Profile Settings"
93
+ >
94
+ <Settings className="h-5 w-5" />
95
+ </ButtonLink>
96
+
97
+ {/* Theme Toggle */}
98
+ <ThemeToggle />
99
+
100
+ {/* Sign Out */}
101
+ <Button
102
+ onClick={onLogout}
103
+ variant="ghost"
104
+ size="icon"
105
+ className="h-9 w-9 text-destructive hover:bg-destructive/10 hover:text-destructive"
106
+ aria-label="Sign Out"
107
+ >
108
+ <LogOut className="h-5 w-5" />
109
+ </Button>
110
+ </div>
111
+ </div>
112
+ </CardContent>
113
+ </Card>
114
+ );
115
+ }
116
+
117
+ // Guest Card
118
+ return (
119
+ <Card className="border-border !bg-accent/50">
120
+ <CardContent className="p-4">
121
+ <div className="text-center space-y-4">
122
+ <div className="w-12 h-12 rounded-full flex items-center justify-center mx-auto bg-muted">
123
+ <User className="w-6 h-6 text-muted-foreground" />
124
+ </div>
125
+ <div>
126
+ <p className="text-sm font-medium mb-1 text-foreground">Welcome!</p>
127
+ <p className="text-xs text-muted-foreground">
128
+ Sign in to access your dashboard
129
+ </p>
130
+ </div>
131
+ <ButtonLink
132
+ href={authPath}
133
+ variant="default"
134
+ size="default"
135
+ className="w-full"
136
+ onClick={onNavigate}
137
+ >
138
+ <User className="w-5 h-5 mr-2" />
139
+ Sign In
140
+ </ButtonLink>
141
+
142
+ {/* Theme toggle */}
143
+ <div className="flex justify-center pt-2 border-t border-border/30">
144
+ <ThemeToggle />
145
+ </div>
146
+ </div>
147
+ </CardContent>
148
+ </Card>
149
+ );
150
+ }
@@ -49,7 +49,7 @@ export function Navigation() {
49
49
  const { app, publicLayout } = config;
50
50
 
51
51
  return (
52
- <nav className="sticky top-0 w-full border-b backdrop-blur-xl z-[9990] bg-surface-gradient border-border/30">
52
+ <nav className="sticky top-0 w-full border-b backdrop-blur-xl z-[100] bg-background/80 border-border/30">
53
53
  <div className="w-full px-4 sm:px-6 lg:px-8">
54
54
  <div className="flex items-center justify-between h-16">
55
55
  {/* Left side - Logo and Navigation Menu */}
@@ -143,7 +143,7 @@ export function Navigation() {
143
143
  {isMobile && (
144
144
  <button
145
145
  onClick={toggleMobileMenu}
146
- className="p-3 rounded-lg border shadow-sm transition-all duration-200 bg-card/50 hover:bg-card border-border/50 hover:border-primary/50 text-foreground hover:text-primary"
146
+ className="p-3 rounded-sm border shadow-sm transition-all duration-200 bg-card/50 hover:bg-card border-border/50 hover:border-primary/50 text-foreground hover:text-primary"
147
147
  aria-label="Toggle mobile menu"
148
148
  >
149
149
  <Menu className="size-5" />
@@ -30,7 +30,7 @@ import { Plus, RefreshCw } from 'lucide-react';
30
30
  import { useForm } from 'react-hook-form';
31
31
  import { zodResolver } from '@hookform/resolvers/zod';
32
32
  import { usePaymentsContext, useRootPaymentsContext } from '@djangocfg/api/cfg/contexts';
33
- import { Schemas, Enums } from '@djangocfg/api/cfg/generated';
33
+ import { Schemas } from '@djangocfg/api/cfg/generated';
34
34
  import { paymentsLogger } from '../../../utils/logger';
35
35
  import { PAYMENTS_DIALOG_EVENTS, closePaymentsDialog } from '../events';
36
36
  import { openPaymentDetails } from './PaymentDetailsDialog';
@@ -39,7 +39,6 @@ import type { ComboboxOption } from '@djangocfg/ui';
39
39
 
40
40
  const { PaymentCreateRequestSchema } = Schemas;
41
41
  type PaymentCreateRequest = Schemas.PaymentCreateRequest;
42
- const { PaymentCreateRequestCurrencyCode, PaymentCreateRequestProvider } = Enums;
43
42
 
44
43
  export const CreatePaymentDialog: React.FC = () => {
45
44
  const [open, setOpen] = useState(false);
@@ -55,33 +54,54 @@ export const CreatePaymentDialog: React.FC = () => {
55
54
  resolver: zodResolver(PaymentCreateRequestSchema),
56
55
  defaultValues: {
57
56
  amount_usd: 10,
58
- currency_code: PaymentCreateRequestCurrencyCode.USDT,
57
+ currency_code: 'USDT', // Default to USDT
59
58
  },
60
59
  });
61
60
 
62
61
  // Group currencies by token and create combobox options
62
+ // Backend automatically selects network, so we group by currency code
63
63
  const currencyOptions = useMemo((): ComboboxOption[] => {
64
64
  if (!providerCurrencies?.results) return [];
65
65
 
66
66
  const enabledCurrencies = providerCurrencies.results.filter(pc => pc.is_enabled);
67
67
 
68
- return enabledCurrencies.map((pc) => {
69
- const networkInfo = pc.network ? ` (${pc.network.name})` : '';
70
- const label = `${pc.currency.code} - ${pc.currency.name}${networkInfo}`;
71
- const description = pc.network ? pc.network.code : 'No network';
68
+ // Group by currency code and collect all networks
69
+ const currencyMap = new Map<string, { pc: ProviderCurrency, networks: string[] }>();
70
+
71
+ for (const pc of enabledCurrencies) {
72
+ const code = pc.currency.code.toUpperCase();
73
+ if (!currencyMap.has(code)) {
74
+ currencyMap.set(code, { pc, networks: [] });
75
+ }
76
+ if (pc.network?.name) {
77
+ currencyMap.get(code)!.networks.push(pc.network.name);
78
+ }
79
+ }
80
+
81
+ return Array.from(currencyMap.values()).map(({ pc, networks }) => {
82
+ // Show network info only if there's exactly one network, otherwise backend will choose
83
+ let label = pc.currency.code;
84
+ let description = pc.currency.name;
85
+
86
+ if (networks.length === 1) {
87
+ label = `${pc.currency.code} (${networks[0]})`;
88
+ } else if (networks.length > 1) {
89
+ description = `${pc.currency.name} • Multiple networks available`;
90
+ }
72
91
 
73
92
  return {
74
- value: pc.provider_currency_code,
93
+ // Use currency code (e.g. "USDT") as value for API
94
+ value: pc.currency.code.toUpperCase(),
75
95
  label,
76
96
  description,
77
97
  };
78
98
  });
79
99
  }, [providerCurrencies]);
80
100
 
81
- // Get ProviderCurrency by currency_code
101
+ // Get first ProviderCurrency by currency code (e.g. "USDT")
82
102
  const getProviderCurrency = (currencyCode: string) => {
83
103
  return providerCurrencies?.results?.find(
84
- pc => pc.provider_currency_code === currencyCode
104
+ pc => pc.is_enabled && pc.currency.code.toUpperCase() === currencyCode.toUpperCase()
85
105
  );
86
106
  };
87
107
 
@@ -127,13 +147,17 @@ export const CreatePaymentDialog: React.FC = () => {
127
147
  try {
128
148
  setIsSubmitting(true);
129
149
 
130
- const payment = await createPayment(data);
150
+ const result = await createPayment(data);
131
151
  handleClose();
132
152
  closePaymentsDialog();
133
153
 
134
- // Open payment details dialog with created payment
135
- if (payment) {
136
- openPaymentDetails(payment);
154
+ // The API returns a wrapped response with { success, message, payment }
155
+ // Extract the payment ID from the result
156
+ const paymentData = result as any;
157
+ const paymentId = paymentData?.payment?.id || paymentData?.id;
158
+
159
+ if (paymentId) {
160
+ openPaymentDetails(String(paymentId));
137
161
  }
138
162
  } catch (error) {
139
163
  paymentsLogger.error('Failed to create payment:', error);
@@ -214,15 +238,13 @@ export const CreatePaymentDialog: React.FC = () => {
214
238
  const pc = getProviderCurrency(option.value);
215
239
  if (!pc) return option.label;
216
240
  return (
217
- <div className="flex items-center gap-2 flex-1">
218
- <TokenIcon symbol={pc.currency.code} size={24} />
219
- <div className="flex flex-col flex-1">
220
- <span className="font-medium">{pc.currency.code} - {pc.currency.name}</span>
221
- {pc.network && (
222
- <span className="text-xs text-muted-foreground">
223
- Network: {pc.network.name} ({pc.network.code})
224
- </span>
225
- )}
241
+ <div className="flex items-center gap-2 flex-1 min-w-0">
242
+ <TokenIcon symbol={pc.currency.code} size={20} className="shrink-0" />
243
+ <div className="flex flex-col flex-1 min-w-0">
244
+ <span className="font-medium truncate">
245
+ {pc.currency.code}
246
+ {pc.network && ` (${pc.network.name})`}
247
+ </span>
226
248
  </div>
227
249
  </div>
228
250
  );
@@ -244,7 +266,7 @@ export const CreatePaymentDialog: React.FC = () => {
244
266
  if (!pc) return null;
245
267
 
246
268
  return (
247
- <div className="rounded-lg bg-muted p-4 space-y-3">
269
+ <div className="rounded-sm bg-muted p-4 space-y-3">
248
270
  {/* Amount to Send in Crypto */}
249
271
  <div className="flex items-center justify-between">
250
272
  <span className="text-sm text-muted-foreground">You will send</span>
@@ -15,34 +15,58 @@ import {
15
15
  DialogTitle,
16
16
  Button,
17
17
  TokenIcon,
18
- useEventListener,
19
18
  } from '@djangocfg/ui';
20
- import { Copy, ExternalLink, CheckCircle2, Clock, XCircle, AlertCircle } from 'lucide-react';
21
- import type { Payment } from '@djangocfg/api/cfg/contexts';
19
+ import { Copy, ExternalLink, CheckCircle2, Clock, XCircle, AlertCircle, RefreshCw } from 'lucide-react';
20
+ import { Hooks, api } from '@djangocfg/api';
21
+ import type { API } from '@djangocfg/api';
22
22
 
23
23
  export const PAYMENT_DETAILS_EVENTS = {
24
24
  OPEN_PAYMENT_DETAILS: 'open-payment-details',
25
25
  CLOSE_PAYMENT_DETAILS: 'close-payment-details',
26
26
  } as const;
27
27
 
28
- export interface PaymentDetailsDialogProps {
29
- payment?: Payment | null;
30
- }
31
-
32
- export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ payment: initialPayment }) => {
28
+ export const PaymentDetailsDialog: React.FC = () => {
33
29
  const [open, setOpen] = useState(false);
34
- const [payment, setPayment] = useState<Payment | null>(initialPayment || null);
30
+ const [paymentId, setPaymentId] = useState<string | null>(null);
35
31
  const [copied, setCopied] = useState(false);
36
32
  const [timeLeft, setTimeLeft] = useState<string>('');
37
33
 
38
- useEventListener(PAYMENT_DETAILS_EVENTS.OPEN_PAYMENT_DETAILS, (event: CustomEvent<{ payment: Payment }>) => {
39
- setPayment(event.detail.payment);
40
- setOpen(true);
41
- });
34
+ // Load payment data by ID using hook
35
+ // Only fetch when dialog is open and paymentId is set
36
+ const shouldFetch = open && !!paymentId;
37
+ const { data: payment, isLoading, error, mutate } = Hooks.usePaymentsPaymentRetrieve(
38
+ shouldFetch ? paymentId : '',
39
+ api as unknown as API
40
+ );
42
41
 
43
- useEventListener(PAYMENT_DETAILS_EVENTS.CLOSE_PAYMENT_DETAILS, () => {
44
- setOpen(false);
45
- });
42
+ // Debug logging
43
+ useEffect(() => {
44
+ if (error) {
45
+ console.error('Payment loading error:', error);
46
+ }
47
+ }, [error]);
48
+
49
+ useEffect(() => {
50
+ const handleOpen = (event: Event) => {
51
+ const customEvent = event as CustomEvent<{ paymentId: string }>;
52
+ console.log('Opening payment details for ID:', customEvent.detail.paymentId);
53
+ setPaymentId(customEvent.detail.paymentId);
54
+ setOpen(true);
55
+ };
56
+
57
+ const handleClose = () => {
58
+ setOpen(false);
59
+ setPaymentId(null);
60
+ };
61
+
62
+ window.addEventListener(PAYMENT_DETAILS_EVENTS.OPEN_PAYMENT_DETAILS, handleOpen);
63
+ window.addEventListener(PAYMENT_DETAILS_EVENTS.CLOSE_PAYMENT_DETAILS, handleClose);
64
+
65
+ return () => {
66
+ window.removeEventListener(PAYMENT_DETAILS_EVENTS.OPEN_PAYMENT_DETAILS, handleOpen);
67
+ window.removeEventListener(PAYMENT_DETAILS_EVENTS.CLOSE_PAYMENT_DETAILS, handleClose);
68
+ };
69
+ }, []);
46
70
 
47
71
  const handleClose = () => {
48
72
  setOpen(false);
@@ -99,7 +123,45 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
99
123
  }
100
124
  };
101
125
 
102
- if (!payment) return null;
126
+ if (!open) return null;
127
+
128
+ // Loading state
129
+ if (isLoading) {
130
+ return (
131
+ <Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
132
+ <DialogContent className="sm:max-w-lg">
133
+ <DialogHeader>
134
+ <DialogTitle>Payment Details</DialogTitle>
135
+ <DialogDescription>Loading payment information...</DialogDescription>
136
+ </DialogHeader>
137
+ <div className="flex items-center justify-center py-12">
138
+ <RefreshCw className="h-8 w-8 animate-spin text-muted-foreground" />
139
+ </div>
140
+ </DialogContent>
141
+ </Dialog>
142
+ );
143
+ }
144
+
145
+ // Error state - only show error if we actually tried to fetch and failed
146
+ if (shouldFetch && !isLoading && (error || !payment)) {
147
+ return (
148
+ <Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
149
+ <DialogContent className="sm:max-w-lg">
150
+ <DialogHeader>
151
+ <DialogTitle>Payment Details</DialogTitle>
152
+ <DialogDescription>Failed to load payment information</DialogDescription>
153
+ </DialogHeader>
154
+ <div className="flex flex-col items-center justify-center py-12 space-y-4">
155
+ <XCircle className="h-12 w-12 text-destructive" />
156
+ <p className="text-sm text-muted-foreground">
157
+ {error ? `Error: ${error}` : 'Payment not found'}
158
+ </p>
159
+ <Button onClick={() => mutate()}>Try Again</Button>
160
+ </div>
161
+ </DialogContent>
162
+ </Dialog>
163
+ );
164
+ }
103
165
 
104
166
  const statusInfo = getStatusInfo();
105
167
  const StatusIcon = statusInfo.icon;
@@ -121,7 +183,7 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
121
183
 
122
184
  <div className="space-y-6">
123
185
  {/* Status Badge */}
124
- <div className={`flex items-center gap-3 p-4 rounded-lg ${statusInfo.bg}`}>
186
+ <div className={`flex items-center gap-3 p-4 rounded-sm ${statusInfo.bg}`}>
125
187
  <StatusIcon className={`h-5 w-5 ${statusInfo.color}`} />
126
188
  <div className="flex-1">
127
189
  <div className="font-semibold capitalize">{payment.status}</div>
@@ -135,10 +197,10 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
135
197
 
136
198
  {/* Amount Information */}
137
199
  <div className="space-y-3">
138
- <div className="flex items-center justify-between p-4 bg-muted rounded-lg">
200
+ <div className="flex items-center justify-between p-4 bg-muted rounded-sm">
139
201
  <span className="text-sm text-muted-foreground">Amount to send</span>
140
202
  <div className="flex items-center gap-2">
141
- <TokenIcon symbol={payment.currency || 'BTC'} size={20} />
203
+ <TokenIcon symbol={String(payment.currency || 'BTC')} size={20} />
142
204
  <span className="font-mono font-bold text-lg">
143
205
  {payment.amount_crypto?.toFixed(8)} {payment.currency}
144
206
  </span>
@@ -150,6 +212,13 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
150
212
  <span className="font-semibold text-lg">${payment.amount_usd?.toFixed(2)} USD</span>
151
213
  </div>
152
214
 
215
+ {payment.provider_payment_id && (
216
+ <div className="flex items-center justify-between px-4">
217
+ <span className="text-sm text-muted-foreground">Payment Order #</span>
218
+ <span className="font-mono font-medium">{payment.provider_payment_id}</span>
219
+ </div>
220
+ )}
221
+
153
222
  {payment.network && (
154
223
  <div className="flex items-center justify-between px-4">
155
224
  <span className="text-sm text-muted-foreground">Network</span>
@@ -160,7 +229,7 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
160
229
 
161
230
  {/* QR Code */}
162
231
  {qrCodeUrl && payment.status === 'pending' && (
163
- <div className="flex justify-center p-6 bg-white rounded-lg">
232
+ <div className="flex justify-center p-6 bg-white rounded-sm">
164
233
  <img src={qrCodeUrl} alt="Payment QR Code" className="w-48 h-48" />
165
234
  </div>
166
235
  )}
@@ -170,7 +239,7 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
170
239
  <div className="space-y-2">
171
240
  <label className="text-sm font-medium">Payment Address</label>
172
241
  <div className="flex items-center gap-2">
173
- <div className="flex-1 p-3 bg-muted rounded-lg font-mono text-sm break-all">
242
+ <div className="flex-1 p-3 bg-muted rounded-sm font-mono text-sm break-all">
174
243
  {payment.pay_address}
175
244
  </div>
176
245
  <Button
@@ -193,7 +262,7 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
193
262
  {payment.transaction_hash && (
194
263
  <div className="space-y-2">
195
264
  <label className="text-sm font-medium">Transaction Hash</label>
196
- <div className="p-3 bg-muted rounded-lg font-mono text-sm break-all">
265
+ <div className="p-3 bg-muted rounded-sm font-mono text-sm break-all">
197
266
  {payment.transaction_hash}
198
267
  </div>
199
268
  </div>
@@ -241,10 +310,10 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
241
310
  };
242
311
 
243
312
  // Helper function to open payment details dialog
244
- export const openPaymentDetails = (payment: Payment) => {
313
+ export const openPaymentDetails = (paymentId: string) => {
245
314
  window.dispatchEvent(
246
315
  new CustomEvent(PAYMENT_DETAILS_EVENTS.OPEN_PAYMENT_DETAILS, {
247
- detail: { payment },
316
+ detail: { paymentId },
248
317
  })
249
318
  );
250
319
  };
@@ -1,13 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import React, { createContext, useContext, useEffect, type ReactNode } from 'react';
4
- import {
5
- api,
6
- usePaymentsCurrenciesList,
7
- usePaymentsProviderCurrenciesList,
8
- usePaymentsNetworksList,
9
- } from '@djangocfg/api/cfg';
10
- import type { API } from '@djangocfg/api/cfg';
4
+ import { api, Hooks } from '@djangocfg/api';
5
+ import type { API } from '@djangocfg/api';
11
6
  import type {
12
7
  Currency,
13
8
  PaginatedCurrencyListList,
@@ -15,7 +10,7 @@ import type {
15
10
  PaginatedProviderCurrencyList,
16
11
  Network,
17
12
  PaginatedNetworkList,
18
- } from '@djangocfg/api/cfg';
13
+ } from '@djangocfg/api';
19
14
 
20
15
  // ─────────────────────────────────────────────────────────────────────────
21
16
  // Context Type
@@ -58,7 +53,7 @@ export function RootPaymentsProvider({ children }: { children: ReactNode }) {
58
53
  error: currenciesError,
59
54
  isLoading: isLoadingCurrencies,
60
55
  mutate: mutateCurrencies,
61
- } = usePaymentsCurrenciesList({}, api as unknown as API);
56
+ } = Hooks.usePaymentsCurrenciesList({}, api as unknown as API);
62
57
 
63
58
  // List all provider currencies
64
59
  const {
@@ -66,7 +61,7 @@ export function RootPaymentsProvider({ children }: { children: ReactNode }) {
66
61
  error: providerCurrenciesError,
67
62
  isLoading: isLoadingProviderCurrencies,
68
63
  mutate: mutateProviderCurrencies,
69
- } = usePaymentsProviderCurrenciesList({}, api as unknown as API);
64
+ } = Hooks.usePaymentsProviderCurrenciesList({}, api as unknown as API);
70
65
 
71
66
  // List all networks
72
67
  const {
@@ -74,7 +69,7 @@ export function RootPaymentsProvider({ children }: { children: ReactNode }) {
74
69
  error: networksError,
75
70
  isLoading: isLoadingNetworks,
76
71
  mutate: mutateNetworks,
77
- } = usePaymentsNetworksList({}, api as unknown as API);
72
+ } = Hooks.usePaymentsNetworksList({}, api as unknown as API);
78
73
 
79
74
  const refreshCurrencies = async () => {
80
75
  await mutateCurrencies();
@@ -88,7 +88,7 @@ export const ApiKeysList: React.FC = () => {
88
88
  <CardContent>
89
89
  <div className="space-y-4">
90
90
  {Array.from({ length: 3 }).map((_, i) => (
91
- <div key={i} className="flex items-center justify-between p-4 border rounded-lg">
91
+ <div key={i} className="flex items-center justify-between p-4 border rounded-sm">
92
92
  <div className="space-y-2 flex-1">
93
93
  <Skeleton className="h-4 w-32" />
94
94
  <Skeleton className="h-3 w-48" />
@@ -131,7 +131,7 @@ export const ApiKeysList: React.FC = () => {
131
131
  {keysList.map((key) => (
132
132
  <div
133
133
  key={key.id}
134
- className="flex items-center justify-between p-4 border rounded-lg hover:bg-accent/50 transition-colors"
134
+ className="flex items-center justify-between p-4 border rounded-sm hover:bg-accent/50 transition-colors"
135
135
  >
136
136
  <div className="flex-1 min-w-0">
137
137
  <div className="flex items-center gap-3 mb-2">
@@ -72,7 +72,7 @@ export const RecentPayments: React.FC = () => {
72
72
  </CardHeader>
73
73
  <CardContent className="space-y-3">
74
74
  {Array.from({ length: 5 }).map((_, i) => (
75
- <div key={i} className="flex items-center justify-between p-3 border rounded-lg">
75
+ <div key={i} className="flex items-center justify-between p-3 border rounded-sm">
76
76
  <div className="space-y-2">
77
77
  <Skeleton className="h-4 w-32" />
78
78
  <Skeleton className="h-3 w-24" />
@@ -112,7 +112,7 @@ export const RecentPayments: React.FC = () => {
112
112
  {payments.map((payment) => (
113
113
  <div
114
114
  key={payment.id}
115
- className="flex items-center justify-between p-3 border rounded-lg hover:bg-accent cursor-pointer transition-colors"
115
+ className="flex items-center justify-between p-3 border rounded-sm hover:bg-accent cursor-pointer transition-colors"
116
116
  onClick={() => openPaymentDetailsDialog(payment.id)}
117
117
  >
118
118
  <div className="flex-1">
@@ -156,7 +156,7 @@ export const PaymentsList: React.FC = () => {
156
156
  {isLoadingPayments ? (
157
157
  <div className="space-y-3">
158
158
  {Array.from({ length: 5 }).map((_, i) => (
159
- <div key={i} className="flex items-center justify-between p-4 border rounded-lg">
159
+ <div key={i} className="flex items-center justify-between p-4 border rounded-sm">
160
160
  <div className="space-y-2">
161
161
  <Skeleton className="h-4 w-32" />
162
162
  <Skeleton className="h-3 w-24" />
@@ -201,7 +201,7 @@ export const PaymentsList: React.FC = () => {
201
201
  <TableRow
202
202
  key={payment.id}
203
203
  className="cursor-pointer hover:bg-accent"
204
- onClick={() => openPaymentDetails(payment)}
204
+ onClick={() => openPaymentDetails(payment.id)}
205
205
  >
206
206
  <TableCell>
207
207
  <div>
@@ -234,7 +234,7 @@ export const PaymentsList: React.FC = () => {
234
234
  size="sm"
235
235
  onClick={(e) => {
236
236
  e.stopPropagation();
237
- openPaymentDetails(payment);
237
+ openPaymentDetails(payment.id);
238
238
  }}
239
239
  >
240
240
  <ExternalLink className="h-4 w-4" />
@@ -4,9 +4,8 @@
4
4
  */
5
5
 
6
6
  import useSWRInfinite from 'swr/infinite';
7
- import { api, getSupportTicketsMessagesList } from '@djangocfg/api';
7
+ import { api, Fetchers, CfgSupportTypes } from '@djangocfg/api';
8
8
  import type { API } from '@djangocfg/api';
9
- import { CfgSupportTypes } from '@djangocfg/api';
10
9
 
11
10
  type PaginatedMessageList = CfgSupportTypes.PaginatedMessageList;
12
11
  type Message = CfgSupportTypes.Message;
@@ -41,7 +40,7 @@ export function useInfiniteMessages(ticketUuid: string | null): UseInfiniteMessa
41
40
  };
42
41
 
43
42
  const fetcher = async ([, ticket_uuid, page, pageSize]: [string, string, number, number]) => {
44
- return getSupportTicketsMessagesList(
43
+ return Fetchers.getSupportTicketsMessagesList(
45
44
  ticket_uuid,
46
45
  { page, page_size: pageSize },
47
46
  api as unknown as API
@@ -4,9 +4,8 @@
4
4
  */
5
5
 
6
6
  import useSWRInfinite from 'swr/infinite';
7
- import { api, getSupportTicketsList } from '@djangocfg/api';
7
+ import { api, Fetchers, CfgSupportTypes } from '@djangocfg/api';
8
8
  import type { API } from '@djangocfg/api';
9
- import { CfgSupportTypes } from '@djangocfg/api';
10
9
 
11
10
  type PaginatedTicketList = CfgSupportTypes.PaginatedTicketList;
12
11
  type Ticket = CfgSupportTypes.Ticket;
@@ -37,7 +36,7 @@ export function useInfiniteTickets(): UseInfiniteTicketsReturn {
37
36
  };
38
37
 
39
38
  const fetcher = async ([, page, pageSize]: [string, number, number]) => {
40
- return getSupportTicketsList(
39
+ return Fetchers.getSupportTicketsList(
41
40
  { page, page_size: pageSize },
42
41
  api as unknown as API
43
42
  );
@@ -102,7 +102,7 @@ export const SessionList: React.FC<SessionListProps> = ({
102
102
  {sessions.map((session, index) => (
103
103
  <div
104
104
  key={session.id}
105
- className="group relative flex items-start gap-3 p-4 border rounded-lg
105
+ className="group relative flex items-start gap-3 p-4 border rounded-sm
106
106
  hover:bg-muted/50 hover:border-primary/50 hover:shadow-md
107
107
  transition-all duration-200 cursor-pointer
108
108
  animate-in fade-in slide-in-from-left-2"
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import useSWRInfinite from 'swr/infinite';
7
- import { api, getKnowbaseAdminSessionsList, CfgKnowbaseTypes } from '@djangocfg/api';
7
+ import { api, Fetchers, CfgKnowbaseTypes } from '@djangocfg/api';
8
8
  import type { API } from '@djangocfg/api';
9
9
 
10
10
  type PaginatedChatSessionList = CfgKnowbaseTypes.PaginatedChatSessionList;
@@ -25,7 +25,7 @@ export function useInfiniteSessions() {
25
25
  };
26
26
 
27
27
  const fetcher = async ([, page, pageSize]: [string, number, number]) => {
28
- return getKnowbaseAdminSessionsList(
28
+ return Fetchers.getKnowbaseAdminSessionsList(
29
29
  { page, page_size: pageSize },
30
30
  api as unknown as API
31
31
  );
@@ -71,7 +71,7 @@ export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(({
71
71
  {/* Video Player */}
72
72
  <div
73
73
  className={cn(
74
- "relative w-full overflow-hidden rounded-lg bg-black",
74
+ "relative w-full overflow-hidden rounded-sm bg-black",
75
75
  theme === 'minimal' && "rounded-none",
76
76
  theme === 'modern' && "rounded-xl shadow-2xl"
77
77
  )}