@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.
- package/README.md +53 -161
- package/package.json +6 -6
- package/src/components/RedirectPage/RedirectPage.tsx +1 -1
- package/src/index.ts +0 -6
- package/src/layouts/AppLayout/AppLayout.tsx +1 -1
- package/src/layouts/AppLayout/BaseApp.tsx +1 -1
- package/src/layouts/AuthLayout/AuthContext.tsx +1 -1
- package/src/layouts/AuthLayout/OAuthCallback.tsx +1 -1
- package/src/layouts/AuthLayout/OAuthProviders.tsx +1 -1
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +1 -1
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +1 -1
- package/src/layouts/ProfileLayout/components/AvatarSection.tsx +2 -2
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +1 -1
- package/src/layouts/_components/UserMenu.tsx +1 -1
- package/src/layouts/index.ts +0 -2
- package/src/snippets/Analytics/useAnalytics.ts +1 -1
- package/src/snippets/index.ts +0 -3
- package/src/auth/README.md +0 -962
- package/src/auth/context/AccountsContext.tsx +0 -240
- package/src/auth/context/AuthContext.tsx +0 -604
- package/src/auth/context/index.ts +0 -4
- package/src/auth/context/types.ts +0 -68
- package/src/auth/hooks/index.ts +0 -17
- package/src/auth/hooks/useAuthForm.ts +0 -332
- package/src/auth/hooks/useAuthGuard.ts +0 -25
- package/src/auth/hooks/useAuthRedirect.ts +0 -51
- package/src/auth/hooks/useAutoAuth.ts +0 -49
- package/src/auth/hooks/useGithubAuth.ts +0 -184
- package/src/auth/hooks/useLocalStorage.ts +0 -214
- package/src/auth/hooks/useProfileCache.ts +0 -146
- package/src/auth/hooks/useSessionStorage.ts +0 -189
- package/src/auth/index.ts +0 -10
- package/src/auth/middlewares/index.ts +0 -1
- package/src/auth/middlewares/proxy.ts +0 -32
- package/src/auth/server.ts +0 -6
- package/src/auth/utils/errors.ts +0 -34
- package/src/auth/utils/index.ts +0 -2
- package/src/auth/utils/validation.ts +0 -14
- package/src/contexts/LeadsContext.tsx +0 -156
- package/src/contexts/NewsletterContext.tsx +0 -263
- package/src/contexts/SupportContext.tsx +0 -256
- package/src/contexts/index.ts +0 -59
- package/src/contexts/knowbase/ChatContext.tsx +0 -174
- package/src/contexts/knowbase/DocumentsContext.tsx +0 -304
- package/src/contexts/knowbase/SessionsContext.tsx +0 -174
- package/src/contexts/knowbase/index.ts +0 -61
- package/src/contexts/payments/BalancesContext.tsx +0 -65
- package/src/contexts/payments/CurrenciesContext.tsx +0 -66
- package/src/contexts/payments/OverviewContext.tsx +0 -174
- package/src/contexts/payments/PaymentsContext.tsx +0 -132
- package/src/contexts/payments/README.md +0 -201
- package/src/contexts/payments/RootPaymentsContext.tsx +0 -68
- package/src/contexts/payments/index.ts +0 -50
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -92
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -291
- package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -290
- package/src/layouts/PaymentsLayout/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/events.ts +0 -47
- package/src/layouts/PaymentsLayout/index.ts +0 -16
- package/src/layouts/PaymentsLayout/types.ts +0 -6
- package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -128
- package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -142
- package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -20
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -276
- package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -17
- package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -273
- package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -17
- package/src/layouts/SupportLayout/README.md +0 -91
- package/src/layouts/SupportLayout/SupportLayout.tsx +0 -179
- package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +0 -155
- package/src/layouts/SupportLayout/components/MessageInput.tsx +0 -92
- package/src/layouts/SupportLayout/components/MessageList.tsx +0 -314
- package/src/layouts/SupportLayout/components/TicketCard.tsx +0 -96
- package/src/layouts/SupportLayout/components/TicketList.tsx +0 -153
- package/src/layouts/SupportLayout/components/index.ts +0 -6
- package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +0 -263
- package/src/layouts/SupportLayout/context/index.ts +0 -2
- package/src/layouts/SupportLayout/events.ts +0 -33
- package/src/layouts/SupportLayout/hooks/index.ts +0 -2
- package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +0 -119
- package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +0 -92
- package/src/layouts/SupportLayout/index.ts +0 -8
- package/src/layouts/SupportLayout/types.ts +0 -21
- package/src/snippets/Chat/ChatUIContext.tsx +0 -110
- package/src/snippets/Chat/ChatWidget.tsx +0 -476
- package/src/snippets/Chat/README.md +0 -122
- package/src/snippets/Chat/components/MessageInput.tsx +0 -124
- package/src/snippets/Chat/components/MessageList.tsx +0 -169
- package/src/snippets/Chat/components/SessionList.tsx +0 -192
- package/src/snippets/Chat/components/index.ts +0 -9
- package/src/snippets/Chat/hooks/index.ts +0 -6
- package/src/snippets/Chat/hooks/useInfiniteSessions.ts +0 -82
- package/src/snippets/Chat/index.tsx +0 -45
- package/src/snippets/Chat/types.ts +0 -80
- package/src/snippets/ContactForm/ContactForm.tsx +0 -346
- package/src/snippets/ContactForm/ContactFormProvider.tsx +0 -153
- package/src/snippets/ContactForm/ContactInfo.tsx +0 -114
- package/src/snippets/ContactForm/ContactPage.tsx +0 -131
- package/src/snippets/ContactForm/dynamic.tsx +0 -55
- package/src/snippets/ContactForm/index.ts +0 -34
- package/src/snippets/ContactForm/types.ts +0 -110
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Balance Card Component (v2.0 - Simplified)
|
|
3
|
-
* Display user balance with quick actions
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import {
|
|
10
|
-
Card,
|
|
11
|
-
CardContent,
|
|
12
|
-
CardHeader,
|
|
13
|
-
CardTitle,
|
|
14
|
-
Button,
|
|
15
|
-
Badge,
|
|
16
|
-
Skeleton,
|
|
17
|
-
} from '@djangocfg/ui-nextjs';
|
|
18
|
-
import { Wallet, RefreshCw, Plus } from 'lucide-react';
|
|
19
|
-
import { useOverviewContext } from '@djangocfg/layouts/contexts';
|
|
20
|
-
import { openCreatePaymentDialog } from '../../../events';
|
|
21
|
-
|
|
22
|
-
export const BalanceCard: React.FC = () => {
|
|
23
|
-
const {
|
|
24
|
-
balance,
|
|
25
|
-
isLoadingBalance,
|
|
26
|
-
refreshBalance
|
|
27
|
-
} = useOverviewContext();
|
|
28
|
-
|
|
29
|
-
const formatCurrency = (amount?: number | null) => {
|
|
30
|
-
if (amount === null || amount === undefined) return '$0.00';
|
|
31
|
-
return new Intl.NumberFormat('en-US', {
|
|
32
|
-
style: 'currency',
|
|
33
|
-
currency: 'USD',
|
|
34
|
-
minimumFractionDigits: 2,
|
|
35
|
-
}).format(amount);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const formatDate = (dateStr?: string) => {
|
|
39
|
-
if (!dateStr) return 'No transactions yet';
|
|
40
|
-
try {
|
|
41
|
-
return new Date(dateStr).toLocaleDateString('en-US', {
|
|
42
|
-
year: 'numeric',
|
|
43
|
-
month: 'short',
|
|
44
|
-
day: 'numeric',
|
|
45
|
-
});
|
|
46
|
-
} catch {
|
|
47
|
-
return 'Invalid date';
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
if (isLoadingBalance) {
|
|
52
|
-
return (
|
|
53
|
-
<Card>
|
|
54
|
-
<CardHeader>
|
|
55
|
-
<CardTitle className="flex items-center justify-between">
|
|
56
|
-
<div className="flex items-center gap-2">
|
|
57
|
-
<Wallet className="h-5 w-5" />
|
|
58
|
-
Account Balance
|
|
59
|
-
</div>
|
|
60
|
-
<Skeleton className="h-8 w-20" />
|
|
61
|
-
</CardTitle>
|
|
62
|
-
</CardHeader>
|
|
63
|
-
<CardContent className="space-y-4">
|
|
64
|
-
<Skeleton className="h-10 w-32" />
|
|
65
|
-
<Skeleton className="h-4 w-48" />
|
|
66
|
-
</CardContent>
|
|
67
|
-
</Card>
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Extract balance data from response: { success, balance: { amount_usd, total_deposited, total_withdrawn, last_transaction_at } }
|
|
72
|
-
const balanceData = balance?.balance || balance;
|
|
73
|
-
const amountUsd = balanceData?.amount_usd ?? 0;
|
|
74
|
-
const totalDeposited = balanceData?.total_deposited ?? 0;
|
|
75
|
-
const totalWithdrawn = balanceData?.total_withdrawn ?? 0;
|
|
76
|
-
const lastTransactionAt = balanceData?.last_transaction_at;
|
|
77
|
-
const isEmpty = amountUsd === 0 && totalDeposited === 0;
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<Card>
|
|
81
|
-
<CardHeader>
|
|
82
|
-
<CardTitle className="flex items-center justify-between">
|
|
83
|
-
<div className="flex items-center gap-2">
|
|
84
|
-
<Wallet className="h-5 w-5" />
|
|
85
|
-
Account Balance
|
|
86
|
-
</div>
|
|
87
|
-
<div className="flex items-center gap-2">
|
|
88
|
-
<Button variant="ghost" size="sm" onClick={refreshBalance}>
|
|
89
|
-
<RefreshCw className="h-4 w-4" />
|
|
90
|
-
</Button>
|
|
91
|
-
<Button size="sm" onClick={() => openCreatePaymentDialog()}>
|
|
92
|
-
<Plus className="h-4 w-4 mr-2" />
|
|
93
|
-
Add Funds
|
|
94
|
-
</Button>
|
|
95
|
-
</div>
|
|
96
|
-
</CardTitle>
|
|
97
|
-
</CardHeader>
|
|
98
|
-
<CardContent className="space-y-4">
|
|
99
|
-
<div>
|
|
100
|
-
<div className="text-4xl font-bold">
|
|
101
|
-
{formatCurrency(amountUsd)}
|
|
102
|
-
</div>
|
|
103
|
-
<p className="text-sm text-muted-foreground mt-1">
|
|
104
|
-
Available balance • Last updated {formatDate(lastTransactionAt)}
|
|
105
|
-
</p>
|
|
106
|
-
</div>
|
|
107
|
-
|
|
108
|
-
<div className="grid grid-cols-2 gap-4 pt-4 border-t">
|
|
109
|
-
<div>
|
|
110
|
-
<p className="text-xs text-muted-foreground">Total Deposited</p>
|
|
111
|
-
<p className="text-lg font-semibold text-green-600">{formatCurrency(totalDeposited)}</p>
|
|
112
|
-
</div>
|
|
113
|
-
<div>
|
|
114
|
-
<p className="text-xs text-muted-foreground">Total Withdrawn</p>
|
|
115
|
-
<p className="text-lg font-semibold text-red-600">{formatCurrency(totalWithdrawn)}</p>
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
<div className="flex items-center gap-2">
|
|
120
|
-
<Badge variant={!isEmpty ? 'default' : 'secondary'}>
|
|
121
|
-
{!isEmpty ? 'Active' : 'New Account'}
|
|
122
|
-
</Badge>
|
|
123
|
-
{isEmpty && <Badge variant="outline">Empty Balance</Badge>}
|
|
124
|
-
</div>
|
|
125
|
-
</CardContent>
|
|
126
|
-
</Card>
|
|
127
|
-
);
|
|
128
|
-
};
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recent Payments Component (v2.0 - Simplified)
|
|
3
|
-
* Display recent payment transactions from payments list
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import {
|
|
10
|
-
Card,
|
|
11
|
-
CardContent,
|
|
12
|
-
CardHeader,
|
|
13
|
-
CardTitle,
|
|
14
|
-
Button,
|
|
15
|
-
Badge,
|
|
16
|
-
Skeleton,
|
|
17
|
-
} from '@djangocfg/ui-nextjs';
|
|
18
|
-
import { History, ExternalLink } from 'lucide-react';
|
|
19
|
-
import { useOverviewContext } from '@djangocfg/layouts/contexts';
|
|
20
|
-
import { openPaymentDetailsDialog } from '../../../events';
|
|
21
|
-
|
|
22
|
-
export const RecentPayments: React.FC = () => {
|
|
23
|
-
const { payments, isLoadingPayments } = useOverviewContext();
|
|
24
|
-
|
|
25
|
-
const formatCurrency = (amount?: number | string | null) => {
|
|
26
|
-
if (amount === null || amount === undefined) return '$0.00';
|
|
27
|
-
const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
|
|
28
|
-
return new Intl.NumberFormat('en-US', {
|
|
29
|
-
style: 'currency',
|
|
30
|
-
currency: 'USD',
|
|
31
|
-
minimumFractionDigits: 2,
|
|
32
|
-
}).format(numAmount);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const getRelativeTime = (date: string | null | undefined): string => {
|
|
36
|
-
if (!date) return 'N/A';
|
|
37
|
-
|
|
38
|
-
const now = new Date();
|
|
39
|
-
const target = new Date(date);
|
|
40
|
-
const diffInSeconds = Math.floor((now.getTime() - target.getTime()) / 1000);
|
|
41
|
-
|
|
42
|
-
if (diffInSeconds < 60) return 'Just now';
|
|
43
|
-
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
44
|
-
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
45
|
-
return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const getStatusVariant = (
|
|
49
|
-
status: string | null | undefined
|
|
50
|
-
): 'default' | 'destructive' | 'outline' | 'secondary' => {
|
|
51
|
-
switch (status?.toLowerCase()) {
|
|
52
|
-
case 'completed':
|
|
53
|
-
case 'success':
|
|
54
|
-
return 'default';
|
|
55
|
-
case 'pending':
|
|
56
|
-
case 'confirming':
|
|
57
|
-
return 'secondary';
|
|
58
|
-
case 'failed':
|
|
59
|
-
case 'error':
|
|
60
|
-
case 'expired':
|
|
61
|
-
return 'destructive';
|
|
62
|
-
default:
|
|
63
|
-
return 'outline';
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
if (isLoadingPayments) {
|
|
68
|
-
return (
|
|
69
|
-
<Card>
|
|
70
|
-
<CardHeader>
|
|
71
|
-
<CardTitle className="flex items-center gap-2">
|
|
72
|
-
<History className="h-5 w-5" />
|
|
73
|
-
Recent Payments
|
|
74
|
-
</CardTitle>
|
|
75
|
-
</CardHeader>
|
|
76
|
-
<CardContent className="space-y-3">
|
|
77
|
-
{Array.from({ length: 5 }).map((_, i) => (
|
|
78
|
-
<div key={i} className="flex items-center justify-between p-3 border rounded-sm">
|
|
79
|
-
<div className="space-y-2">
|
|
80
|
-
<Skeleton className="h-4 w-32" />
|
|
81
|
-
<Skeleton className="h-3 w-24" />
|
|
82
|
-
</div>
|
|
83
|
-
<Skeleton className="h-6 w-16" />
|
|
84
|
-
</div>
|
|
85
|
-
))}
|
|
86
|
-
</CardContent>
|
|
87
|
-
</Card>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Get first 5 payments for recent list
|
|
92
|
-
const recentPaymentsList = payments?.results?.slice(0, 5) || [];
|
|
93
|
-
|
|
94
|
-
return (
|
|
95
|
-
<Card>
|
|
96
|
-
<CardHeader>
|
|
97
|
-
<CardTitle className="flex items-center justify-between">
|
|
98
|
-
<div className="flex items-center gap-2">
|
|
99
|
-
<History className="h-5 w-5" />
|
|
100
|
-
Recent Payments
|
|
101
|
-
</div>
|
|
102
|
-
<Button variant="ghost" size="sm">
|
|
103
|
-
View All
|
|
104
|
-
<ExternalLink className="h-4 w-4 ml-2" />
|
|
105
|
-
</Button>
|
|
106
|
-
</CardTitle>
|
|
107
|
-
</CardHeader>
|
|
108
|
-
<CardContent>
|
|
109
|
-
{recentPaymentsList.length === 0 ? (
|
|
110
|
-
<div className="text-center py-8 text-muted-foreground">
|
|
111
|
-
<History className="h-12 w-12 mx-auto mb-4 opacity-50" />
|
|
112
|
-
<p>No recent payments</p>
|
|
113
|
-
<p className="text-sm mt-2">Create your first payment to get started</p>
|
|
114
|
-
</div>
|
|
115
|
-
) : (
|
|
116
|
-
<div className="space-y-3">
|
|
117
|
-
{recentPaymentsList.map((payment) => (
|
|
118
|
-
<div
|
|
119
|
-
key={payment.id}
|
|
120
|
-
className="flex items-center justify-between p-3 border rounded-sm hover:bg-accent cursor-pointer transition-colors"
|
|
121
|
-
onClick={() => openPaymentDetailsDialog(String(payment.id))}
|
|
122
|
-
>
|
|
123
|
-
<div className="flex-1">
|
|
124
|
-
<div className="flex items-center gap-2">
|
|
125
|
-
<span className="font-medium">{formatCurrency(payment.amount_usd)}</span>
|
|
126
|
-
<Badge variant={getStatusVariant(payment.status)} className="text-xs">
|
|
127
|
-
{payment.status}
|
|
128
|
-
</Badge>
|
|
129
|
-
</div>
|
|
130
|
-
<p className="text-sm text-muted-foreground">
|
|
131
|
-
{getRelativeTime(payment.created_at)} • {payment.currency_code || 'USD'}
|
|
132
|
-
</p>
|
|
133
|
-
</div>
|
|
134
|
-
<ExternalLink className="h-4 w-4 text-muted-foreground" />
|
|
135
|
-
</div>
|
|
136
|
-
))}
|
|
137
|
-
</div>
|
|
138
|
-
)}
|
|
139
|
-
</CardContent>
|
|
140
|
-
</Card>
|
|
141
|
-
);
|
|
142
|
-
};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Overview View (v2.0 - Simplified)
|
|
3
|
-
* Dashboard with balance and recent payments
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import { BalanceCard, RecentPayments } from './components';
|
|
10
|
-
|
|
11
|
-
export const OverviewView: React.FC = () => {
|
|
12
|
-
return (
|
|
13
|
-
<div className="space-y-6">
|
|
14
|
-
<div className="grid gap-6 lg:grid-cols-2">
|
|
15
|
-
<BalanceCard />
|
|
16
|
-
<RecentPayments />
|
|
17
|
-
</div>
|
|
18
|
-
</div>
|
|
19
|
-
);
|
|
20
|
-
};
|
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Payments List Component (v2.0 - Simplified)
|
|
3
|
-
* Display paginated list of payments with filters
|
|
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
|
-
useDRFPagination,
|
|
30
|
-
StaticPagination,
|
|
31
|
-
} from '@djangocfg/ui-nextjs';
|
|
32
|
-
import { Plus, Search, Filter, RefreshCw, ExternalLink } from 'lucide-react';
|
|
33
|
-
import { api, Hooks } from '@djangocfg/api';
|
|
34
|
-
import { openCreatePaymentDialog, openPaymentDetailsDialog } from '../../../events';
|
|
35
|
-
|
|
36
|
-
export const PaymentsList: React.FC = () => {
|
|
37
|
-
// Local pagination state
|
|
38
|
-
const pagination = useDRFPagination(1, 20);
|
|
39
|
-
|
|
40
|
-
// Fetch payments with pagination
|
|
41
|
-
const {
|
|
42
|
-
data: payments,
|
|
43
|
-
error,
|
|
44
|
-
isLoading: isLoadingPayments,
|
|
45
|
-
mutate: refreshPayments,
|
|
46
|
-
} = Hooks.usePaymentsPaymentsList(pagination.params, api as any);
|
|
47
|
-
|
|
48
|
-
const paymentsList = payments?.results || [];
|
|
49
|
-
const totalCount = payments?.count || 0;
|
|
50
|
-
|
|
51
|
-
const [searchTerm, setSearchTerm] = useState('');
|
|
52
|
-
const [statusFilter, setStatusFilter] = useState<string>('all');
|
|
53
|
-
|
|
54
|
-
const formatCurrency = (amount?: number | string | null) => {
|
|
55
|
-
if (amount === null || amount === undefined) return '$0.00';
|
|
56
|
-
const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
|
|
57
|
-
return new Intl.NumberFormat('en-US', {
|
|
58
|
-
style: 'currency',
|
|
59
|
-
currency: 'USD',
|
|
60
|
-
minimumFractionDigits: 2,
|
|
61
|
-
}).format(numAmount);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const getRelativeTime = (date: string | null | undefined): string => {
|
|
65
|
-
if (!date) return 'N/A';
|
|
66
|
-
|
|
67
|
-
const now = new Date();
|
|
68
|
-
const target = new Date(date);
|
|
69
|
-
const diffInSeconds = Math.floor((now.getTime() - target.getTime()) / 1000);
|
|
70
|
-
|
|
71
|
-
if (diffInSeconds < 60) return 'Just now';
|
|
72
|
-
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
73
|
-
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
74
|
-
return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const getStatusVariant = (
|
|
78
|
-
status: string | null | undefined
|
|
79
|
-
): 'default' | 'destructive' | 'outline' | 'secondary' => {
|
|
80
|
-
switch (status?.toLowerCase()) {
|
|
81
|
-
case 'completed':
|
|
82
|
-
case 'success':
|
|
83
|
-
return 'default';
|
|
84
|
-
case 'pending':
|
|
85
|
-
case 'confirming':
|
|
86
|
-
return 'secondary';
|
|
87
|
-
case 'failed':
|
|
88
|
-
case 'error':
|
|
89
|
-
case 'expired':
|
|
90
|
-
return 'destructive';
|
|
91
|
-
default:
|
|
92
|
-
return 'outline';
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const handleSearch = (value: string) => {
|
|
97
|
-
setSearchTerm(value);
|
|
98
|
-
// Client-side filtering only
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const handleStatusFilter = (status: string) => {
|
|
102
|
-
setStatusFilter(status);
|
|
103
|
-
// Client-side filtering only
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
// Filter payments client-side for now (until API supports filtering)
|
|
108
|
-
const filteredPayments = paymentsList.filter((payment) => {
|
|
109
|
-
const matchesSearch = searchTerm
|
|
110
|
-
? payment.id?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
111
|
-
payment.status?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
112
|
-
payment.currency_code?.toLowerCase().includes(searchTerm.toLowerCase())
|
|
113
|
-
: true;
|
|
114
|
-
|
|
115
|
-
const matchesStatus = statusFilter !== 'all'
|
|
116
|
-
? payment.status?.toLowerCase() === statusFilter.toLowerCase()
|
|
117
|
-
: true;
|
|
118
|
-
|
|
119
|
-
return matchesSearch && matchesStatus;
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
return (
|
|
123
|
-
<Card>
|
|
124
|
-
<CardHeader>
|
|
125
|
-
<CardTitle className="flex items-center justify-between">
|
|
126
|
-
<span>Payment History</span>
|
|
127
|
-
<div className="flex items-center gap-2">
|
|
128
|
-
<Button variant="outline" size="sm" onClick={() => refreshPayments()} disabled={isLoadingPayments}>
|
|
129
|
-
<RefreshCw className={`h-4 w-4 mr-2 ${isLoadingPayments ? 'animate-spin' : ''}`} />
|
|
130
|
-
Refresh
|
|
131
|
-
</Button>
|
|
132
|
-
<Button size="sm" onClick={() => openCreatePaymentDialog()}>
|
|
133
|
-
<Plus className="h-4 w-4 mr-2" />
|
|
134
|
-
New Payment
|
|
135
|
-
</Button>
|
|
136
|
-
</div>
|
|
137
|
-
</CardTitle>
|
|
138
|
-
</CardHeader>
|
|
139
|
-
|
|
140
|
-
<CardContent className="space-y-4">
|
|
141
|
-
{/* Filters */}
|
|
142
|
-
<div className="flex flex-col sm:flex-row gap-4">
|
|
143
|
-
<div className="relative flex-1">
|
|
144
|
-
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
145
|
-
<Input
|
|
146
|
-
placeholder="Search by ID, status, or currency..."
|
|
147
|
-
value={searchTerm}
|
|
148
|
-
onChange={(e) => handleSearch(e.target.value)}
|
|
149
|
-
className="pl-10"
|
|
150
|
-
/>
|
|
151
|
-
</div>
|
|
152
|
-
|
|
153
|
-
<Select value={statusFilter} onValueChange={handleStatusFilter}>
|
|
154
|
-
<SelectTrigger className="w-full sm:w-48">
|
|
155
|
-
<Filter className="h-4 w-4 mr-2" />
|
|
156
|
-
<SelectValue placeholder="Filter by status" />
|
|
157
|
-
</SelectTrigger>
|
|
158
|
-
<SelectContent>
|
|
159
|
-
<SelectItem value="all">All Statuses</SelectItem>
|
|
160
|
-
<SelectItem value="completed">Completed</SelectItem>
|
|
161
|
-
<SelectItem value="pending">Pending</SelectItem>
|
|
162
|
-
<SelectItem value="confirming">Confirming</SelectItem>
|
|
163
|
-
<SelectItem value="failed">Failed</SelectItem>
|
|
164
|
-
<SelectItem value="expired">Expired</SelectItem>
|
|
165
|
-
</SelectContent>
|
|
166
|
-
</Select>
|
|
167
|
-
</div>
|
|
168
|
-
|
|
169
|
-
{/* Payments Table */}
|
|
170
|
-
{isLoadingPayments ? (
|
|
171
|
-
<div className="space-y-3">
|
|
172
|
-
{Array.from({ length: 5 }).map((_, i) => (
|
|
173
|
-
<div key={i} className="flex items-center justify-between p-4 border rounded-sm">
|
|
174
|
-
<div className="space-y-2">
|
|
175
|
-
<Skeleton className="h-4 w-32" />
|
|
176
|
-
<Skeleton className="h-3 w-24" />
|
|
177
|
-
</div>
|
|
178
|
-
<Skeleton className="h-6 w-16" />
|
|
179
|
-
</div>
|
|
180
|
-
))}
|
|
181
|
-
</div>
|
|
182
|
-
) : filteredPayments.length === 0 ? (
|
|
183
|
-
<div className="text-center py-12">
|
|
184
|
-
<div className="w-16 h-16 mx-auto mb-4 bg-muted rounded-full flex items-center justify-center">
|
|
185
|
-
<Search className="w-8 h-8 text-muted-foreground" />
|
|
186
|
-
</div>
|
|
187
|
-
<h3 className="text-lg font-semibold mb-2">No Payments Found</h3>
|
|
188
|
-
<p className="text-muted-foreground mb-4">
|
|
189
|
-
{searchTerm || statusFilter !== 'all'
|
|
190
|
-
? 'No payments match your current filters'
|
|
191
|
-
: "You haven't made any payments yet"}
|
|
192
|
-
</p>
|
|
193
|
-
<Button onClick={() => openCreatePaymentDialog()}>
|
|
194
|
-
<Plus className="h-4 w-4 mr-2" />
|
|
195
|
-
Create Payment
|
|
196
|
-
</Button>
|
|
197
|
-
</div>
|
|
198
|
-
) : (
|
|
199
|
-
<>
|
|
200
|
-
<div className="rounded-md border">
|
|
201
|
-
<Table>
|
|
202
|
-
<TableHeader>
|
|
203
|
-
<TableRow>
|
|
204
|
-
<TableHead>Date</TableHead>
|
|
205
|
-
<TableHead>Amount</TableHead>
|
|
206
|
-
<TableHead>Currency</TableHead>
|
|
207
|
-
<TableHead>Status</TableHead>
|
|
208
|
-
<TableHead>Provider</TableHead>
|
|
209
|
-
<TableHead>Payment ID</TableHead>
|
|
210
|
-
<TableHead className="text-right">Actions</TableHead>
|
|
211
|
-
</TableRow>
|
|
212
|
-
</TableHeader>
|
|
213
|
-
<TableBody>
|
|
214
|
-
{filteredPayments.map((payment) => (
|
|
215
|
-
<TableRow
|
|
216
|
-
key={payment.id}
|
|
217
|
-
className="cursor-pointer hover:bg-accent"
|
|
218
|
-
onClick={() => openPaymentDetailsDialog(String(payment.id))}
|
|
219
|
-
>
|
|
220
|
-
<TableCell>
|
|
221
|
-
<div>
|
|
222
|
-
<div className="font-medium">
|
|
223
|
-
{payment.created_at
|
|
224
|
-
? new Date(payment.created_at).toLocaleDateString()
|
|
225
|
-
: 'N/A'}
|
|
226
|
-
</div>
|
|
227
|
-
<div className="text-sm text-muted-foreground">
|
|
228
|
-
{getRelativeTime(payment.created_at)}
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
</TableCell>
|
|
232
|
-
<TableCell className="font-mono font-semibold">
|
|
233
|
-
{formatCurrency(payment.amount_usd)}
|
|
234
|
-
</TableCell>
|
|
235
|
-
<TableCell>
|
|
236
|
-
<Badge variant="outline">{payment.currency_code || 'USD'}</Badge>
|
|
237
|
-
</TableCell>
|
|
238
|
-
<TableCell>
|
|
239
|
-
<Badge variant={getStatusVariant(payment.status)}>{payment.status}</Badge>
|
|
240
|
-
</TableCell>
|
|
241
|
-
<TableCell className="text-sm text-muted-foreground">
|
|
242
|
-
NowPayments
|
|
243
|
-
</TableCell>
|
|
244
|
-
<TableCell className="font-mono text-sm text-muted-foreground">
|
|
245
|
-
{payment.id ? `${payment.id.toString().slice(0, 8)}...` : 'N/A'}
|
|
246
|
-
</TableCell>
|
|
247
|
-
<TableCell className="text-right">
|
|
248
|
-
<Button
|
|
249
|
-
variant="ghost"
|
|
250
|
-
size="sm"
|
|
251
|
-
onClick={(e) => {
|
|
252
|
-
e.stopPropagation();
|
|
253
|
-
openPaymentDetailsDialog(String(payment.id));
|
|
254
|
-
}}
|
|
255
|
-
>
|
|
256
|
-
<ExternalLink className="h-4 w-4" />
|
|
257
|
-
</Button>
|
|
258
|
-
</TableCell>
|
|
259
|
-
</TableRow>
|
|
260
|
-
))}
|
|
261
|
-
</TableBody>
|
|
262
|
-
</Table>
|
|
263
|
-
</div>
|
|
264
|
-
|
|
265
|
-
{/* DRF Pagination */}
|
|
266
|
-
<StaticPagination
|
|
267
|
-
data={payments}
|
|
268
|
-
onPageChange={pagination.setPage}
|
|
269
|
-
className="mt-4"
|
|
270
|
-
/>
|
|
271
|
-
</>
|
|
272
|
-
)}
|
|
273
|
-
</CardContent>
|
|
274
|
-
</Card>
|
|
275
|
-
);
|
|
276
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { PaymentsList } from './PaymentsList';
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Payments View (v2.0 - Simplified)
|
|
3
|
-
* List and manage payment transactions
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import { PaymentsList } from './components';
|
|
10
|
-
|
|
11
|
-
export const PaymentsView: React.FC = () => {
|
|
12
|
-
return (
|
|
13
|
-
<div className="space-y-6">
|
|
14
|
-
<PaymentsList />
|
|
15
|
-
</div>
|
|
16
|
-
);
|
|
17
|
-
};
|