@djangocfg/ext-payments 1.0.4 → 1.0.7
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/dist/config.cjs +3 -2
- package/dist/config.js +3 -2
- package/dist/hooks.cjs +559 -543
- package/dist/hooks.js +558 -543
- package/dist/index.cjs +559 -543
- package/dist/index.js +558 -543
- package/package.json +9 -8
- package/src/api/generated/ext_payments/CLAUDE.md +76 -0
- package/src/api/generated/ext_payments/_utils/fetchers/ext_payments__payments.ts +1 -0
- package/src/api/generated/ext_payments/_utils/fetchers/index.ts +1 -0
- package/src/api/generated/ext_payments/_utils/hooks/ext_payments__payments.ts +1 -0
- package/src/api/generated/ext_payments/_utils/hooks/index.ts +1 -0
- package/src/api/generated/ext_payments/_utils/schemas/index.ts +1 -0
- package/src/api/generated/ext_payments/api-instance.ts +1 -0
- package/src/api/generated/ext_payments/enums.ts +1 -0
- package/src/api/generated/ext_payments/errors.ts +1 -0
- package/src/api/generated/ext_payments/ext_payments__payments/index.ts +1 -0
- package/src/api/generated/ext_payments/ext_payments__payments/models.ts +1 -0
- package/src/api/generated/ext_payments/http.ts +1 -0
- package/src/api/generated/ext_payments/index.ts +1 -0
- package/src/api/generated/ext_payments/logger.ts +1 -0
- package/src/api/generated/ext_payments/retry.ts +1 -0
- package/src/api/generated/ext_payments/storage.ts +1 -0
- package/src/api/generated/ext_payments/validation-events.ts +1 -0
- package/src/api/index.ts +2 -1
- package/src/config.ts +1 -0
- package/src/contexts/BalancesContext.tsx +2 -1
- package/src/contexts/CurrenciesContext.tsx +2 -1
- package/src/contexts/OverviewContext.tsx +5 -5
- package/src/contexts/PaymentsContext.tsx +6 -5
- package/src/contexts/PaymentsExtensionProvider.tsx +3 -2
- package/src/contexts/RootPaymentsContext.tsx +2 -1
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +5 -7
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +10 -27
- package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +15 -18
- package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +6 -13
- package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +8 -11
- package/src/layouts/PaymentsLayout/views/overview/index.tsx +1 -0
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +43 -42
- package/src/layouts/PaymentsLayout/views/payments/index.tsx +1 -0
- package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +71 -84
- package/src/layouts/PaymentsLayout/views/transactions/index.tsx +1 -0
package/dist/hooks.js
CHANGED
|
@@ -2,13 +2,14 @@ import { createConsola, consola } from 'consola';
|
|
|
2
2
|
import pRetry, { AbortError } from 'p-retry';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
5
|
+
import { RefreshCw, Plus, XCircle, ExternalLink, Wallet, CreditCard, History, Clock, AlertCircle, CheckCircle2, Search, Filter, ArrowDownLeft, ArrowUpRight } from 'lucide-react';
|
|
6
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Form, FormField, FormItem, FormLabel, FormControl, Input, FormDescription, FormMessage, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, TokenIcon, DialogFooter, Button, CopyButton, Tabs, TabsList, TabsTrigger, TabsContent, Card, CardHeader, CardTitle, Skeleton, CardContent, Badge, useDRFPagination, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, StaticPagination } from '@djangocfg/ui-nextjs';
|
|
5
7
|
import { createContext, useContext, useState, useMemo, useEffect } from 'react';
|
|
6
8
|
import useSWR, { useSWRConfig, SWRConfig } from 'swr';
|
|
7
9
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
-
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Form, FormField, FormItem, FormLabel, FormControl, Input, FormDescription, FormMessage, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, TokenIcon, DialogFooter, Button, CopyButton, Tabs, TabsList, TabsTrigger, TabsContent, Card, CardHeader, CardTitle, Skeleton, CardContent, Badge, useDRFPagination, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, StaticPagination } from '@djangocfg/ui-nextjs';
|
|
9
|
-
import { RefreshCw, Plus, XCircle, ExternalLink, Wallet, CreditCard, History, Clock, AlertCircle, CheckCircle2, Search, Filter, ArrowDownLeft, ArrowUpRight } from 'lucide-react';
|
|
10
10
|
import { useForm } from 'react-hook-form';
|
|
11
11
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
12
|
+
import moment from 'moment';
|
|
12
13
|
import { createExtensionConfig } from '@djangocfg/ext-base';
|
|
13
14
|
|
|
14
15
|
var __defProp = Object.defineProperty;
|
|
@@ -1639,6 +1640,11 @@ function useRootPaymentsContext() {
|
|
|
1639
1640
|
}
|
|
1640
1641
|
return context;
|
|
1641
1642
|
}
|
|
1643
|
+
var isDevelopment = process.env.NODE_ENV === "development";
|
|
1644
|
+
var logger = createConsola({
|
|
1645
|
+
level: isDevelopment ? 4 : 1
|
|
1646
|
+
}).withTag("ext-payments");
|
|
1647
|
+
var paymentsLogger = logger;
|
|
1642
1648
|
|
|
1643
1649
|
// src/layouts/PaymentsLayout/events.ts
|
|
1644
1650
|
var PAYMENT_EVENTS = {
|
|
@@ -1659,156 +1665,526 @@ var openPaymentDetailsDialog = (id) => {
|
|
|
1659
1665
|
var closePaymentsDialog = () => {
|
|
1660
1666
|
window.dispatchEvent(new Event(PAYMENT_EVENTS.CLOSE_DIALOG));
|
|
1661
1667
|
};
|
|
1662
|
-
var
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
const
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1668
|
+
var PaymentCreateSchema = z.object({
|
|
1669
|
+
amount_usd: z.number().min(0.01, "Amount must be at least $0.01"),
|
|
1670
|
+
currency_code: z.string().min(1, "Please select a currency")
|
|
1671
|
+
});
|
|
1672
|
+
var CreatePaymentDialog = () => {
|
|
1673
|
+
const [open, setOpen] = useState(false);
|
|
1674
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
1675
|
+
const { createPayment } = usePaymentsContext();
|
|
1676
|
+
const { currencies, isLoadingCurrencies } = useRootPaymentsContext();
|
|
1677
|
+
const form = useForm({
|
|
1678
|
+
resolver: zodResolver(PaymentCreateSchema),
|
|
1679
|
+
defaultValues: {
|
|
1680
|
+
amount_usd: 10,
|
|
1681
|
+
currency_code: "USDT"
|
|
1682
|
+
}
|
|
1683
|
+
});
|
|
1684
|
+
const currenciesList = useMemo(() => {
|
|
1685
|
+
const data = currencies?.currencies || currencies?.results || currencies || [];
|
|
1686
|
+
return Array.isArray(data) ? data : [];
|
|
1687
|
+
}, [currencies]);
|
|
1688
|
+
const currencyOptions = useMemo(() => {
|
|
1689
|
+
return currenciesList.filter((curr) => curr.is_enabled !== false).map((curr) => ({
|
|
1690
|
+
code: curr.code || curr.currency_code || curr.symbol,
|
|
1691
|
+
name: curr.name || curr.code || curr.currency_code,
|
|
1692
|
+
usd_rate: curr.usd_rate || curr.rate || 1,
|
|
1693
|
+
network: curr.network || null
|
|
1694
|
+
}));
|
|
1695
|
+
}, [currenciesList]);
|
|
1696
|
+
const calculateCryptoAmount = useMemo(() => {
|
|
1697
|
+
const amountUsd = form.watch("amount_usd");
|
|
1698
|
+
const currencyCode = form.watch("currency_code");
|
|
1699
|
+
const currency = currencyOptions.find((c) => c.code === currencyCode);
|
|
1700
|
+
if (!currency || !currency.usd_rate || !amountUsd) {
|
|
1701
|
+
return null;
|
|
1702
|
+
}
|
|
1703
|
+
const cryptoAmount = amountUsd / currency.usd_rate;
|
|
1704
|
+
return {
|
|
1705
|
+
amount: cryptoAmount,
|
|
1706
|
+
currency: currency.code,
|
|
1707
|
+
rate: currency.usd_rate,
|
|
1708
|
+
network: currency.network
|
|
1709
|
+
};
|
|
1710
|
+
}, [form.watch("amount_usd"), form.watch("currency_code"), currencyOptions]);
|
|
1711
|
+
useEffect(() => {
|
|
1712
|
+
const handleOpen = () => setOpen(true);
|
|
1713
|
+
const handleClose2 = () => setOpen(false);
|
|
1714
|
+
window.addEventListener(PAYMENT_EVENTS.OPEN_CREATE_PAYMENT_DIALOG, handleOpen);
|
|
1715
|
+
window.addEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose2);
|
|
1716
|
+
return () => {
|
|
1717
|
+
window.removeEventListener(PAYMENT_EVENTS.OPEN_CREATE_PAYMENT_DIALOG, handleOpen);
|
|
1718
|
+
window.removeEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose2);
|
|
1719
|
+
};
|
|
1720
|
+
}, []);
|
|
1721
|
+
const handleClose = () => {
|
|
1722
|
+
setOpen(false);
|
|
1723
|
+
form.reset();
|
|
1675
1724
|
};
|
|
1676
|
-
|
|
1677
|
-
if (
|
|
1725
|
+
useEffect(() => {
|
|
1726
|
+
if (currencyOptions.length > 0 && !form.getValues("currency_code")) {
|
|
1727
|
+
form.setValue("currency_code", currencyOptions[0].code);
|
|
1728
|
+
}
|
|
1729
|
+
}, [currencyOptions, form]);
|
|
1730
|
+
const handleSubmit = async (data) => {
|
|
1678
1731
|
try {
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1732
|
+
setIsSubmitting(true);
|
|
1733
|
+
const result = await createPayment();
|
|
1734
|
+
handleClose();
|
|
1735
|
+
closePaymentsDialog();
|
|
1736
|
+
const paymentData = result;
|
|
1737
|
+
const paymentId = paymentData?.payment?.id || paymentData?.id;
|
|
1738
|
+
if (paymentId) {
|
|
1739
|
+
openPaymentDetailsDialog(String(paymentId));
|
|
1740
|
+
}
|
|
1741
|
+
} catch (error) {
|
|
1742
|
+
paymentsLogger.error("Failed to create payment:", error);
|
|
1743
|
+
} finally {
|
|
1744
|
+
setIsSubmitting(false);
|
|
1686
1745
|
}
|
|
1687
1746
|
};
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
/* @__PURE__ */ jsx(
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1747
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (isOpen) => !isOpen && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
|
|
1748
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
1749
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Create Payment" }),
|
|
1750
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Create a new payment to add funds to your account." })
|
|
1751
|
+
] }),
|
|
1752
|
+
/* @__PURE__ */ jsx(Form, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit: form.handleSubmit(handleSubmit), className: "space-y-4", children: [
|
|
1753
|
+
/* @__PURE__ */ jsx(
|
|
1754
|
+
FormField,
|
|
1755
|
+
{
|
|
1756
|
+
control: form.control,
|
|
1757
|
+
name: "amount_usd",
|
|
1758
|
+
render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { children: [
|
|
1759
|
+
/* @__PURE__ */ jsx(FormLabel, { children: "Amount (USD)" }),
|
|
1760
|
+
/* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
|
|
1761
|
+
Input,
|
|
1762
|
+
{
|
|
1763
|
+
type: "number",
|
|
1764
|
+
step: "0.01",
|
|
1765
|
+
min: "0.01",
|
|
1766
|
+
placeholder: "10.00",
|
|
1767
|
+
...field,
|
|
1768
|
+
onChange: (e) => field.onChange(parseFloat(e.target.value) || 0)
|
|
1769
|
+
}
|
|
1770
|
+
) }),
|
|
1771
|
+
/* @__PURE__ */ jsx(FormDescription, { children: "The amount you want to pay in USD." }),
|
|
1772
|
+
/* @__PURE__ */ jsx(FormMessage, {})
|
|
1773
|
+
] })
|
|
1774
|
+
}
|
|
1775
|
+
),
|
|
1776
|
+
/* @__PURE__ */ jsx(
|
|
1777
|
+
FormField,
|
|
1778
|
+
{
|
|
1779
|
+
control: form.control,
|
|
1780
|
+
name: "currency_code",
|
|
1781
|
+
render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { children: [
|
|
1782
|
+
/* @__PURE__ */ jsx(FormLabel, { children: "Currency" }),
|
|
1783
|
+
/* @__PURE__ */ jsxs(
|
|
1784
|
+
Select,
|
|
1785
|
+
{
|
|
1786
|
+
onValueChange: field.onChange,
|
|
1787
|
+
defaultValue: field.value,
|
|
1788
|
+
disabled: isLoadingCurrencies,
|
|
1789
|
+
children: [
|
|
1790
|
+
/* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select currency..." }) }) }),
|
|
1791
|
+
/* @__PURE__ */ jsx(SelectContent, { children: currencyOptions.map((curr) => /* @__PURE__ */ jsx(SelectItem, { value: curr.code, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1792
|
+
/* @__PURE__ */ jsx(TokenIcon, { symbol: curr.code, size: 16 }),
|
|
1793
|
+
/* @__PURE__ */ jsx("span", { children: curr.code }),
|
|
1794
|
+
curr.network && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
1795
|
+
"(",
|
|
1796
|
+
curr.network,
|
|
1797
|
+
")"
|
|
1798
|
+
] })
|
|
1799
|
+
] }) }, curr.code)) })
|
|
1800
|
+
]
|
|
1801
|
+
}
|
|
1802
|
+
),
|
|
1803
|
+
/* @__PURE__ */ jsx(FormDescription, { children: "The cryptocurrency to use for payment." }),
|
|
1804
|
+
/* @__PURE__ */ jsx(FormMessage, {})
|
|
1805
|
+
] })
|
|
1806
|
+
}
|
|
1807
|
+
),
|
|
1808
|
+
calculateCryptoAmount && /* @__PURE__ */ jsxs("div", { className: "rounded-sm bg-muted p-4 space-y-3", children: [
|
|
1809
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1810
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "You will send" }),
|
|
1811
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1812
|
+
/* @__PURE__ */ jsx(TokenIcon, { symbol: calculateCryptoAmount.currency, size: 16 }),
|
|
1813
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono font-semibold", children: [
|
|
1814
|
+
calculateCryptoAmount.amount.toFixed(8),
|
|
1815
|
+
" ",
|
|
1816
|
+
calculateCryptoAmount.currency
|
|
1817
|
+
] })
|
|
1818
|
+
] })
|
|
1694
1819
|
] }),
|
|
1695
|
-
/* @__PURE__ */
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
}
|
|
1703
|
-
const balanceData = balance?.balance || balance;
|
|
1704
|
-
const amountUsd = balanceData?.amount_usd ?? 0;
|
|
1705
|
-
const totalDeposited = balanceData?.total_deposited ?? 0;
|
|
1706
|
-
const totalWithdrawn = balanceData?.total_withdrawn ?? 0;
|
|
1707
|
-
const lastTransactionAt = balanceData?.last_transaction_at;
|
|
1708
|
-
const isEmpty = amountUsd === 0 && totalDeposited === 0;
|
|
1709
|
-
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
1710
|
-
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center justify-between", children: [
|
|
1711
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1712
|
-
/* @__PURE__ */ jsx(Wallet, { className: "h-5 w-5" }),
|
|
1713
|
-
"Account Balance"
|
|
1714
|
-
] }),
|
|
1715
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1716
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: refreshBalance, children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" }) }),
|
|
1717
|
-
/* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => openCreatePaymentDialog(), children: [
|
|
1718
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-2" }),
|
|
1719
|
-
"Add Funds"
|
|
1720
|
-
] })
|
|
1721
|
-
] })
|
|
1722
|
-
] }) }),
|
|
1723
|
-
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
|
|
1724
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1725
|
-
/* @__PURE__ */ jsx("div", { className: "text-4xl font-bold", children: formatCurrency(amountUsd) }),
|
|
1726
|
-
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground mt-1", children: [
|
|
1727
|
-
"Available balance \u2022 Last updated ",
|
|
1728
|
-
formatDate(lastTransactionAt)
|
|
1729
|
-
] })
|
|
1730
|
-
] }),
|
|
1731
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4 pt-4 border-t", children: [
|
|
1732
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1733
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Total Deposited" }),
|
|
1734
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold text-green-600", children: formatCurrency(totalDeposited) })
|
|
1820
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1821
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "You will receive" }),
|
|
1822
|
+
/* @__PURE__ */ jsxs("span", { className: "text-lg font-bold", children: [
|
|
1823
|
+
"$",
|
|
1824
|
+
form.watch("amount_usd")?.toFixed(2),
|
|
1825
|
+
" USD"
|
|
1826
|
+
] })
|
|
1735
1827
|
] }),
|
|
1736
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1737
|
-
/* @__PURE__ */ jsx("
|
|
1738
|
-
/* @__PURE__ */
|
|
1739
|
-
|
|
1828
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
1829
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Rate" }),
|
|
1830
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
1831
|
+
"1 ",
|
|
1832
|
+
calculateCryptoAmount.currency,
|
|
1833
|
+
" = $",
|
|
1834
|
+
calculateCryptoAmount.rate?.toFixed(2)
|
|
1835
|
+
] })
|
|
1836
|
+
] }),
|
|
1837
|
+
calculateCryptoAmount.network && /* @__PURE__ */ jsx("div", { className: "border-t pt-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1838
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Network" }),
|
|
1839
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: calculateCryptoAmount.network })
|
|
1840
|
+
] }) })
|
|
1740
1841
|
] }),
|
|
1741
|
-
/* @__PURE__ */ jsxs(
|
|
1742
|
-
/* @__PURE__ */ jsx(
|
|
1743
|
-
|
|
1842
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
1843
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: handleClose, disabled: isSubmitting, children: "Cancel" }),
|
|
1844
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: isSubmitting || currencyOptions.length === 0, children: isSubmitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1845
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4 mr-2 animate-spin" }),
|
|
1846
|
+
"Creating..."
|
|
1847
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1848
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-2" }),
|
|
1849
|
+
"Create Payment"
|
|
1850
|
+
] }) })
|
|
1851
|
+
] })
|
|
1852
|
+
] }) })
|
|
1853
|
+
] }) });
|
|
1854
|
+
};
|
|
1855
|
+
var PaymentDetailsDialog = () => {
|
|
1856
|
+
const [open, setOpen] = useState(false);
|
|
1857
|
+
const [paymentId, setPaymentId] = useState(null);
|
|
1858
|
+
const [timeLeft, setTimeLeft] = useState("");
|
|
1859
|
+
const shouldFetch = open && !!paymentId;
|
|
1860
|
+
const { data: payment, isLoading, error, mutate } = usePaymentsPaymentsRetrieve(
|
|
1861
|
+
shouldFetch ? paymentId : "",
|
|
1862
|
+
apiPayments
|
|
1863
|
+
);
|
|
1864
|
+
useEffect(() => {
|
|
1865
|
+
const handleOpen = (event) => {
|
|
1866
|
+
const customEvent = event;
|
|
1867
|
+
setPaymentId(customEvent.detail.id);
|
|
1868
|
+
setOpen(true);
|
|
1869
|
+
};
|
|
1870
|
+
const handleClose2 = () => {
|
|
1871
|
+
setOpen(false);
|
|
1872
|
+
setPaymentId(null);
|
|
1873
|
+
};
|
|
1874
|
+
window.addEventListener(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, handleOpen);
|
|
1875
|
+
window.addEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose2);
|
|
1876
|
+
return () => {
|
|
1877
|
+
window.removeEventListener(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, handleOpen);
|
|
1878
|
+
window.removeEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose2);
|
|
1879
|
+
};
|
|
1880
|
+
}, []);
|
|
1881
|
+
const handleClose = () => {
|
|
1882
|
+
setOpen(false);
|
|
1883
|
+
setPaymentId(null);
|
|
1884
|
+
};
|
|
1885
|
+
useEffect(() => {
|
|
1886
|
+
if (!payment?.expires_at) return;
|
|
1887
|
+
const updateTimeLeft = () => {
|
|
1888
|
+
const now = moment();
|
|
1889
|
+
const expires = moment.utc(payment.expires_at);
|
|
1890
|
+
const diff = expires.diff(now);
|
|
1891
|
+
if (diff <= 0) {
|
|
1892
|
+
setTimeLeft("Expired");
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
const duration = moment.duration(diff);
|
|
1896
|
+
const hours = Math.floor(duration.asHours());
|
|
1897
|
+
const minutes = duration.minutes();
|
|
1898
|
+
const seconds = duration.seconds();
|
|
1899
|
+
setTimeLeft(`${hours}h ${minutes}m ${seconds}s`);
|
|
1900
|
+
};
|
|
1901
|
+
updateTimeLeft();
|
|
1902
|
+
const interval = setInterval(updateTimeLeft, 1e3);
|
|
1903
|
+
return () => clearInterval(interval);
|
|
1904
|
+
}, [payment?.expires_at]);
|
|
1905
|
+
const getStatusInfo = () => {
|
|
1906
|
+
switch (payment?.status?.toLowerCase()) {
|
|
1907
|
+
case "pending":
|
|
1908
|
+
return { icon: Clock, color: "text-yellow-500", bg: "bg-yellow-500/10" };
|
|
1909
|
+
case "completed":
|
|
1910
|
+
case "success":
|
|
1911
|
+
return { icon: CheckCircle2, color: "text-green-500", bg: "bg-green-500/10" };
|
|
1912
|
+
case "failed":
|
|
1913
|
+
case "error":
|
|
1914
|
+
return { icon: XCircle, color: "text-red-500", bg: "bg-red-500/10" };
|
|
1915
|
+
case "expired":
|
|
1916
|
+
return { icon: AlertCircle, color: "text-gray-500", bg: "bg-gray-500/10" };
|
|
1917
|
+
case "confirming":
|
|
1918
|
+
return { icon: RefreshCw, color: "text-blue-500", bg: "bg-blue-500/10" };
|
|
1919
|
+
default:
|
|
1920
|
+
return { icon: Clock, color: "text-gray-500", bg: "bg-gray-500/10" };
|
|
1921
|
+
}
|
|
1922
|
+
};
|
|
1923
|
+
if (!open) return null;
|
|
1924
|
+
if (isLoading) {
|
|
1925
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (isOpen) => !isOpen && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-lg", children: [
|
|
1926
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
1927
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Payment Details" }),
|
|
1928
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Loading payment information..." })
|
|
1929
|
+
] }),
|
|
1930
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-8 w-8 animate-spin text-muted-foreground" }) })
|
|
1931
|
+
] }) });
|
|
1932
|
+
}
|
|
1933
|
+
if (shouldFetch && !isLoading && (error || !payment)) {
|
|
1934
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (isOpen) => !isOpen && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-lg", children: [
|
|
1935
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
1936
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Payment Details" }),
|
|
1937
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Failed to load payment information" })
|
|
1938
|
+
] }),
|
|
1939
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 space-y-4", children: [
|
|
1940
|
+
/* @__PURE__ */ jsx(XCircle, { className: "h-12 w-12 text-destructive" }),
|
|
1941
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: error ? `Error: ${error}` : "Payment not found" }),
|
|
1942
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => mutate(), children: "Try Again" })
|
|
1943
|
+
] })
|
|
1944
|
+
] }) });
|
|
1945
|
+
}
|
|
1946
|
+
const statusInfo = getStatusInfo();
|
|
1947
|
+
const StatusIcon = statusInfo.icon;
|
|
1948
|
+
const qrCodeUrl = payment.pay_address ? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(payment.pay_address)}` : null;
|
|
1949
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (isOpen) => !isOpen && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-lg", children: [
|
|
1950
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
1951
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Payment Details" }),
|
|
1952
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Send cryptocurrency to complete your payment" })
|
|
1953
|
+
] }),
|
|
1954
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
1955
|
+
/* @__PURE__ */ jsxs("div", { className: `flex items-center gap-3 p-4 rounded-sm ${statusInfo.bg}`, children: [
|
|
1956
|
+
/* @__PURE__ */ jsx(StatusIcon, { className: `h-5 w-5 ${statusInfo.color}` }),
|
|
1957
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
1958
|
+
/* @__PURE__ */ jsx("div", { className: "font-semibold capitalize", children: payment.status }),
|
|
1959
|
+
payment.status === "pending" && timeLeft && /* @__PURE__ */ jsxs("div", { className: "text-sm text-muted-foreground", children: [
|
|
1960
|
+
"Expires in ",
|
|
1961
|
+
timeLeft
|
|
1962
|
+
] })
|
|
1963
|
+
] })
|
|
1964
|
+
] }),
|
|
1965
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1966
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 bg-muted rounded-sm", children: [
|
|
1967
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Amount to send" }),
|
|
1968
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1969
|
+
/* @__PURE__ */ jsx(TokenIcon, { symbol: String(payment.currency_code || "BTC"), size: 20 }),
|
|
1970
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono font-bold text-lg", children: [
|
|
1971
|
+
payment.pay_amount || "0.00000000",
|
|
1972
|
+
" ",
|
|
1973
|
+
payment.currency_code
|
|
1974
|
+
] })
|
|
1975
|
+
] })
|
|
1976
|
+
] }),
|
|
1977
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4", children: [
|
|
1978
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Equivalent to" }),
|
|
1979
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold text-lg", children: [
|
|
1980
|
+
"$",
|
|
1981
|
+
parseFloat(payment.amount_usd || "0").toFixed(2),
|
|
1982
|
+
" USD"
|
|
1983
|
+
] })
|
|
1984
|
+
] }),
|
|
1985
|
+
payment.internal_payment_id && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4", children: [
|
|
1986
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Payment Order #" }),
|
|
1987
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: payment.internal_payment_id })
|
|
1988
|
+
] }),
|
|
1989
|
+
payment.currency_network && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4", children: [
|
|
1990
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Network" }),
|
|
1991
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: payment.currency_network })
|
|
1992
|
+
] })
|
|
1993
|
+
] }),
|
|
1994
|
+
qrCodeUrl && payment.status === "pending" && /* @__PURE__ */ jsx("div", { className: "flex justify-center p-6 bg-white rounded-sm", children: /* @__PURE__ */ jsx("img", { src: qrCodeUrl, alt: "Payment QR Code", className: "w-48 h-48" }) }),
|
|
1995
|
+
payment.pay_address && payment.status === "pending" && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1996
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Payment Address" }),
|
|
1997
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1998
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 p-3 bg-muted rounded-sm font-mono text-sm break-all", children: payment.pay_address }),
|
|
1999
|
+
/* @__PURE__ */ jsx(CopyButton, { value: payment.pay_address, variant: "outline" })
|
|
2000
|
+
] })
|
|
2001
|
+
] }),
|
|
2002
|
+
payment.transaction_hash && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2003
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Transaction Hash" }),
|
|
2004
|
+
/* @__PURE__ */ jsx("div", { className: "p-3 bg-muted rounded-sm font-mono text-sm break-all", children: payment.transaction_hash })
|
|
2005
|
+
] }),
|
|
2006
|
+
payment.payment_url && payment.status === "pending" && /* @__PURE__ */ jsxs(
|
|
2007
|
+
Button,
|
|
2008
|
+
{
|
|
2009
|
+
variant: "outline",
|
|
2010
|
+
className: "w-full",
|
|
2011
|
+
onClick: () => window.open(payment.payment_url, "_blank"),
|
|
2012
|
+
children: [
|
|
2013
|
+
/* @__PURE__ */ jsx(ExternalLink, { className: "h-4 w-4 mr-2" }),
|
|
2014
|
+
"Open in Payment Provider"
|
|
2015
|
+
]
|
|
2016
|
+
}
|
|
2017
|
+
),
|
|
2018
|
+
/* @__PURE__ */ jsxs("div", { className: "pt-4 border-t space-y-2 text-xs text-muted-foreground", children: [
|
|
2019
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
2020
|
+
/* @__PURE__ */ jsx("span", { children: "Payment ID" }),
|
|
2021
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: payment.id })
|
|
2022
|
+
] }),
|
|
2023
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
2024
|
+
/* @__PURE__ */ jsx("span", { children: "Created" }),
|
|
2025
|
+
/* @__PURE__ */ jsx("span", { children: moment.utc(payment.created_at).local().format("MMM D, YYYY HH:mm") })
|
|
2026
|
+
] }),
|
|
2027
|
+
payment.confirmations_count !== void 0 && /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
2028
|
+
/* @__PURE__ */ jsx("span", { children: "Confirmations" }),
|
|
2029
|
+
/* @__PURE__ */ jsx("span", { children: payment.confirmations_count })
|
|
2030
|
+
] })
|
|
2031
|
+
] })
|
|
2032
|
+
] }),
|
|
2033
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
2034
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleClose, children: "Close" }),
|
|
2035
|
+
/* @__PURE__ */ jsxs(Button, { onClick: () => mutate(), variant: "ghost", size: "sm", children: [
|
|
2036
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4 mr-2" }),
|
|
2037
|
+
"Refresh"
|
|
1744
2038
|
] })
|
|
1745
2039
|
] })
|
|
1746
|
-
] });
|
|
2040
|
+
] }) });
|
|
1747
2041
|
};
|
|
1748
|
-
var
|
|
1749
|
-
const {
|
|
2042
|
+
var BalanceCard = () => {
|
|
2043
|
+
const {
|
|
2044
|
+
balance,
|
|
2045
|
+
isLoadingBalance,
|
|
2046
|
+
refreshBalance
|
|
2047
|
+
} = useOverviewContext();
|
|
1750
2048
|
const formatCurrency = (amount) => {
|
|
1751
2049
|
if (amount === null || amount === void 0) return "$0.00";
|
|
1752
|
-
const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
|
|
1753
2050
|
return new Intl.NumberFormat("en-US", {
|
|
1754
2051
|
style: "currency",
|
|
1755
2052
|
currency: "USD",
|
|
1756
2053
|
minimumFractionDigits: 2
|
|
1757
|
-
}).format(
|
|
1758
|
-
};
|
|
1759
|
-
const getRelativeTime = (date) => {
|
|
1760
|
-
if (!date) return "N/A";
|
|
1761
|
-
const now = /* @__PURE__ */ new Date();
|
|
1762
|
-
const target = new Date(date);
|
|
1763
|
-
const diffInSeconds = Math.floor((now.getTime() - target.getTime()) / 1e3);
|
|
1764
|
-
if (diffInSeconds < 60) return "Just now";
|
|
1765
|
-
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
1766
|
-
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
1767
|
-
return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
2054
|
+
}).format(amount);
|
|
1768
2055
|
};
|
|
1769
|
-
const
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
case "confirming":
|
|
1776
|
-
return "secondary";
|
|
1777
|
-
case "failed":
|
|
1778
|
-
case "error":
|
|
1779
|
-
case "expired":
|
|
1780
|
-
return "destructive";
|
|
1781
|
-
default:
|
|
1782
|
-
return "outline";
|
|
2056
|
+
const formatDate = (dateStr) => {
|
|
2057
|
+
if (!dateStr) return "No transactions yet";
|
|
2058
|
+
try {
|
|
2059
|
+
return moment.utc(dateStr).local().format("MMM D, YYYY");
|
|
2060
|
+
} catch {
|
|
2061
|
+
return "Invalid date";
|
|
1783
2062
|
}
|
|
1784
2063
|
};
|
|
1785
|
-
if (
|
|
2064
|
+
if (isLoadingBalance) {
|
|
1786
2065
|
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
1787
|
-
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center
|
|
1788
|
-
/* @__PURE__ */
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
/* @__PURE__ */ jsx(CardContent, { className: "space-y-3", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-3 border rounded-sm", children: [
|
|
1792
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1793
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-32" }),
|
|
1794
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" })
|
|
2066
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center justify-between", children: [
|
|
2067
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2068
|
+
/* @__PURE__ */ jsx(Wallet, { className: "h-5 w-5" }),
|
|
2069
|
+
"Account Balance"
|
|
1795
2070
|
] }),
|
|
1796
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-
|
|
1797
|
-
] }
|
|
2071
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-20" })
|
|
2072
|
+
] }) }),
|
|
2073
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
|
|
2074
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-32" }),
|
|
2075
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-48" })
|
|
2076
|
+
] })
|
|
1798
2077
|
] });
|
|
1799
2078
|
}
|
|
1800
|
-
const
|
|
2079
|
+
const balanceData = balance?.balance || balance;
|
|
2080
|
+
const amountUsd = balanceData?.amount_usd ?? 0;
|
|
2081
|
+
const totalDeposited = balanceData?.total_deposited ?? 0;
|
|
2082
|
+
const totalWithdrawn = balanceData?.total_withdrawn ?? 0;
|
|
2083
|
+
const lastTransactionAt = balanceData?.last_transaction_at;
|
|
2084
|
+
const isEmpty = amountUsd === 0 && totalDeposited === 0;
|
|
1801
2085
|
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
1802
2086
|
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center justify-between", children: [
|
|
1803
2087
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1804
|
-
/* @__PURE__ */ jsx(
|
|
1805
|
-
"
|
|
2088
|
+
/* @__PURE__ */ jsx(Wallet, { className: "h-5 w-5" }),
|
|
2089
|
+
"Account Balance"
|
|
1806
2090
|
] }),
|
|
1807
|
-
/* @__PURE__ */ jsxs(
|
|
1808
|
-
"
|
|
1809
|
-
/* @__PURE__ */
|
|
1810
|
-
|
|
1811
|
-
|
|
2091
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2092
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: refreshBalance, children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" }) }),
|
|
2093
|
+
/* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => openCreatePaymentDialog(), children: [
|
|
2094
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-2" }),
|
|
2095
|
+
"Add Funds"
|
|
2096
|
+
] })
|
|
2097
|
+
] })
|
|
2098
|
+
] }) }),
|
|
2099
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
|
|
2100
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2101
|
+
/* @__PURE__ */ jsx("div", { className: "text-4xl font-bold", children: formatCurrency(amountUsd) }),
|
|
2102
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground mt-1", children: [
|
|
2103
|
+
"Available balance \u2022 Last updated ",
|
|
2104
|
+
formatDate(lastTransactionAt)
|
|
2105
|
+
] })
|
|
2106
|
+
] }),
|
|
2107
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4 pt-4 border-t", children: [
|
|
2108
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2109
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Total Deposited" }),
|
|
2110
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold text-green-600", children: formatCurrency(totalDeposited) })
|
|
2111
|
+
] }),
|
|
2112
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2113
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Total Withdrawn" }),
|
|
2114
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold text-red-600", children: formatCurrency(totalWithdrawn) })
|
|
2115
|
+
] })
|
|
2116
|
+
] }),
|
|
2117
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2118
|
+
/* @__PURE__ */ jsx(Badge, { variant: !isEmpty ? "default" : "secondary", children: !isEmpty ? "Active" : "New Account" }),
|
|
2119
|
+
isEmpty && /* @__PURE__ */ jsx(Badge, { variant: "outline", children: "Empty Balance" })
|
|
2120
|
+
] })
|
|
2121
|
+
] })
|
|
2122
|
+
] });
|
|
2123
|
+
};
|
|
2124
|
+
var RecentPayments = () => {
|
|
2125
|
+
const { payments, isLoadingPayments } = useOverviewContext();
|
|
2126
|
+
const formatCurrency = (amount) => {
|
|
2127
|
+
if (amount === null || amount === void 0) return "$0.00";
|
|
2128
|
+
const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
|
|
2129
|
+
return new Intl.NumberFormat("en-US", {
|
|
2130
|
+
style: "currency",
|
|
2131
|
+
currency: "USD",
|
|
2132
|
+
minimumFractionDigits: 2
|
|
2133
|
+
}).format(numAmount);
|
|
2134
|
+
};
|
|
2135
|
+
const getRelativeTime = (date) => {
|
|
2136
|
+
if (!date) return "N/A";
|
|
2137
|
+
const m = moment.utc(date).local();
|
|
2138
|
+
const now = moment();
|
|
2139
|
+
const diffInSeconds = now.diff(m, "seconds");
|
|
2140
|
+
if (diffInSeconds < 60) return "Just now";
|
|
2141
|
+
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
2142
|
+
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
2143
|
+
return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
2144
|
+
};
|
|
2145
|
+
const getStatusVariant = (status) => {
|
|
2146
|
+
switch (status?.toLowerCase()) {
|
|
2147
|
+
case "completed":
|
|
2148
|
+
case "success":
|
|
2149
|
+
return "default";
|
|
2150
|
+
case "pending":
|
|
2151
|
+
case "confirming":
|
|
2152
|
+
return "secondary";
|
|
2153
|
+
case "failed":
|
|
2154
|
+
case "error":
|
|
2155
|
+
case "expired":
|
|
2156
|
+
return "destructive";
|
|
2157
|
+
default:
|
|
2158
|
+
return "outline";
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
2161
|
+
if (isLoadingPayments) {
|
|
2162
|
+
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
2163
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2", children: [
|
|
2164
|
+
/* @__PURE__ */ jsx(History, { className: "h-5 w-5" }),
|
|
2165
|
+
"Recent Payments"
|
|
2166
|
+
] }) }),
|
|
2167
|
+
/* @__PURE__ */ jsx(CardContent, { className: "space-y-3", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-3 border rounded-sm", children: [
|
|
2168
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2169
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-32" }),
|
|
2170
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" })
|
|
2171
|
+
] }),
|
|
2172
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-16" })
|
|
2173
|
+
] }, i)) })
|
|
2174
|
+
] });
|
|
2175
|
+
}
|
|
2176
|
+
const recentPaymentsList = payments?.results?.slice(0, 5) || [];
|
|
2177
|
+
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
2178
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center justify-between", children: [
|
|
2179
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2180
|
+
/* @__PURE__ */ jsx(History, { className: "h-5 w-5" }),
|
|
2181
|
+
"Recent Payments"
|
|
2182
|
+
] }),
|
|
2183
|
+
/* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", children: [
|
|
2184
|
+
"View All",
|
|
2185
|
+
/* @__PURE__ */ jsx(ExternalLink, { className: "h-4 w-4 ml-2" })
|
|
2186
|
+
] })
|
|
2187
|
+
] }) }),
|
|
1812
2188
|
/* @__PURE__ */ jsx(CardContent, { children: recentPaymentsList.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-center py-8 text-muted-foreground", children: [
|
|
1813
2189
|
/* @__PURE__ */ jsx(History, { className: "h-12 w-12 mx-auto mb-4 opacity-50" }),
|
|
1814
2190
|
/* @__PURE__ */ jsx("p", { children: "No recent payments" }),
|
|
@@ -1866,14 +2242,18 @@ var PaymentsList = () => {
|
|
|
1866
2242
|
};
|
|
1867
2243
|
const getRelativeTime = (date) => {
|
|
1868
2244
|
if (!date) return "N/A";
|
|
1869
|
-
const
|
|
1870
|
-
const
|
|
1871
|
-
const diffInSeconds =
|
|
2245
|
+
const m = moment.utc(date).local();
|
|
2246
|
+
const now = moment();
|
|
2247
|
+
const diffInSeconds = now.diff(m, "seconds");
|
|
1872
2248
|
if (diffInSeconds < 60) return "Just now";
|
|
1873
2249
|
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
1874
2250
|
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
1875
2251
|
return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
1876
2252
|
};
|
|
2253
|
+
const formatDate = (date) => {
|
|
2254
|
+
if (!date) return "N/A";
|
|
2255
|
+
return moment.utc(date).local().format("MMM D, YYYY");
|
|
2256
|
+
};
|
|
1877
2257
|
const getStatusVariant = (status) => {
|
|
1878
2258
|
switch (status?.toLowerCase()) {
|
|
1879
2259
|
case "completed":
|
|
@@ -1896,11 +2276,21 @@ var PaymentsList = () => {
|
|
|
1896
2276
|
const handleStatusFilter = (status) => {
|
|
1897
2277
|
setStatusFilter(status);
|
|
1898
2278
|
};
|
|
2279
|
+
const truncateId = (id) => {
|
|
2280
|
+
if (!id) return "N/A";
|
|
2281
|
+
const str = id.toString();
|
|
2282
|
+
return str.length > 8 ? `${str.slice(0, 8)}...` : str;
|
|
2283
|
+
};
|
|
1899
2284
|
const filteredPayments = paymentsList.filter((payment) => {
|
|
1900
2285
|
const matchesSearch = searchTerm ? payment.id?.toLowerCase().includes(searchTerm.toLowerCase()) || payment.status?.toLowerCase().includes(searchTerm.toLowerCase()) || payment.currency_code?.toLowerCase().includes(searchTerm.toLowerCase()) : true;
|
|
1901
2286
|
const matchesStatus = statusFilter !== "all" ? payment.status?.toLowerCase() === statusFilter.toLowerCase() : true;
|
|
1902
2287
|
return matchesSearch && matchesStatus;
|
|
1903
|
-
})
|
|
2288
|
+
}).map((payment) => ({
|
|
2289
|
+
...payment,
|
|
2290
|
+
formattedDate: formatDate(payment.created_at),
|
|
2291
|
+
relativeTime: getRelativeTime(payment.created_at),
|
|
2292
|
+
truncatedId: truncateId(payment.id)
|
|
2293
|
+
}));
|
|
1904
2294
|
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
1905
2295
|
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center justify-between", children: [
|
|
1906
2296
|
/* @__PURE__ */ jsx("span", { children: "Payment History" }),
|
|
@@ -1976,14 +2366,14 @@ var PaymentsList = () => {
|
|
|
1976
2366
|
onClick: () => openPaymentDetailsDialog(String(payment.id)),
|
|
1977
2367
|
children: [
|
|
1978
2368
|
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("div", { children: [
|
|
1979
|
-
/* @__PURE__ */ jsx("div", { className: "font-medium", children: payment.
|
|
1980
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children:
|
|
2369
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium", children: payment.formattedDate }),
|
|
2370
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: payment.relativeTime })
|
|
1981
2371
|
] }) }),
|
|
1982
2372
|
/* @__PURE__ */ jsx(TableCell, { className: "font-mono font-semibold", children: formatCurrency(payment.amount_usd) }),
|
|
1983
2373
|
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Badge, { variant: "outline", children: payment.currency_code || "USD" }) }),
|
|
1984
2374
|
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Badge, { variant: getStatusVariant(payment.status), children: payment.status }) }),
|
|
1985
2375
|
/* @__PURE__ */ jsx(TableCell, { className: "text-sm text-muted-foreground", children: "NowPayments" }),
|
|
1986
|
-
/* @__PURE__ */ jsx(TableCell, { className: "font-mono text-sm text-muted-foreground", children: payment.
|
|
2376
|
+
/* @__PURE__ */ jsx(TableCell, { className: "font-mono text-sm text-muted-foreground", children: payment.truncatedId }),
|
|
1987
2377
|
/* @__PURE__ */ jsx(TableCell, { className: "text-right", children: /* @__PURE__ */ jsx(
|
|
1988
2378
|
Button,
|
|
1989
2379
|
{
|
|
@@ -2036,22 +2426,16 @@ var TransactionsList = () => {
|
|
|
2036
2426
|
const formatDate = (date) => {
|
|
2037
2427
|
if (!date) return "N/A";
|
|
2038
2428
|
try {
|
|
2039
|
-
return
|
|
2040
|
-
year: "numeric",
|
|
2041
|
-
month: "short",
|
|
2042
|
-
day: "numeric",
|
|
2043
|
-
hour: "2-digit",
|
|
2044
|
-
minute: "2-digit"
|
|
2045
|
-
});
|
|
2429
|
+
return moment.utc(date).local().format("MMM D, YYYY hh:mm A");
|
|
2046
2430
|
} catch {
|
|
2047
2431
|
return "Invalid date";
|
|
2048
2432
|
}
|
|
2049
2433
|
};
|
|
2050
2434
|
const getRelativeTime = (date) => {
|
|
2051
2435
|
if (!date) return "N/A";
|
|
2052
|
-
const
|
|
2053
|
-
const
|
|
2054
|
-
const diffInSeconds =
|
|
2436
|
+
const m = moment.utc(date).local();
|
|
2437
|
+
const now = moment();
|
|
2438
|
+
const diffInSeconds = now.diff(m, "seconds");
|
|
2055
2439
|
if (diffInSeconds < 60) return "Just now";
|
|
2056
2440
|
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
2057
2441
|
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
@@ -2081,11 +2465,22 @@ var TransactionsList = () => {
|
|
|
2081
2465
|
setTypeFilter(type);
|
|
2082
2466
|
await refreshTransactions();
|
|
2083
2467
|
};
|
|
2468
|
+
const truncateId = (id) => {
|
|
2469
|
+
if (!id) return "N/A";
|
|
2470
|
+
const str = id.toString();
|
|
2471
|
+
return str.length > 8 ? `${str.slice(0, 8)}...` : str;
|
|
2472
|
+
};
|
|
2084
2473
|
const filteredTransactions = transactionsList.filter((transaction) => {
|
|
2085
2474
|
const matchesSearch = searchTerm ? transaction.id?.toString().toLowerCase().includes(searchTerm.toLowerCase()) || transaction.description?.toLowerCase().includes(searchTerm.toLowerCase()) || transaction.type?.toLowerCase().includes(searchTerm.toLowerCase()) : true;
|
|
2086
2475
|
const matchesType = typeFilter !== "all" ? transaction.type?.toLowerCase() === typeFilter.toLowerCase() : true;
|
|
2087
2476
|
return matchesSearch && matchesType;
|
|
2088
|
-
})
|
|
2477
|
+
}).map((transaction) => ({
|
|
2478
|
+
...transaction,
|
|
2479
|
+
isDeposit: transaction.type?.toLowerCase() === "deposit" || transaction.type?.toLowerCase() === "credit",
|
|
2480
|
+
formattedDate: formatDate(transaction.created_at || transaction.timestamp),
|
|
2481
|
+
relativeTime: getRelativeTime(transaction.created_at || transaction.timestamp),
|
|
2482
|
+
truncatedRef: truncateId(transaction.reference || transaction.payment_id)
|
|
2483
|
+
}));
|
|
2089
2484
|
if (isLoadingTransactions) {
|
|
2090
2485
|
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
2091
2486
|
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2", children: [
|
|
@@ -2151,26 +2546,23 @@ var TransactionsList = () => {
|
|
|
2151
2546
|
/* @__PURE__ */ jsx(TableHead, { children: "Description" }),
|
|
2152
2547
|
/* @__PURE__ */ jsx(TableHead, { children: "Reference" })
|
|
2153
2548
|
] }) }),
|
|
2154
|
-
/* @__PURE__ */ jsx(TableBody, { children: filteredTransactions.map((transaction, index) => {
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
/* @__PURE__ */ jsx(
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
/* @__PURE__ */ jsx(
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
/* @__PURE__ */ jsx(TableCell, { className: "font-mono text-sm text-muted-foreground", children: transaction.reference || transaction.payment_id ? `${(transaction.reference || transaction.payment_id).toString().slice(0, 8)}...` : "N/A" })
|
|
2172
|
-
] }, transaction.id || index);
|
|
2173
|
-
}) })
|
|
2549
|
+
/* @__PURE__ */ jsx(TableBody, { children: filteredTransactions.map((transaction, index) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
2550
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("div", { children: [
|
|
2551
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium", children: transaction.formattedDate }),
|
|
2552
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: transaction.relativeTime })
|
|
2553
|
+
] }) }),
|
|
2554
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2555
|
+
getTypeIcon(transaction.type),
|
|
2556
|
+
/* @__PURE__ */ jsx(Badge, { variant: getTypeVariant(transaction.type), children: transaction.type || "Unknown" })
|
|
2557
|
+
] }) }),
|
|
2558
|
+
/* @__PURE__ */ jsx(TableCell, { className: "font-mono font-semibold", children: /* @__PURE__ */ jsxs("span", { className: transaction.isDeposit ? "text-green-600" : "text-red-600", children: [
|
|
2559
|
+
transaction.isDeposit ? "+" : "-",
|
|
2560
|
+
formatCurrency(Math.abs(transaction.amount || transaction.amount_usd || 0))
|
|
2561
|
+
] }) }),
|
|
2562
|
+
/* @__PURE__ */ jsx(TableCell, { className: "font-mono", children: formatCurrency(transaction.balance_after || 0) }),
|
|
2563
|
+
/* @__PURE__ */ jsx(TableCell, { className: "text-sm", children: transaction.description || transaction.note || "No description" }),
|
|
2564
|
+
/* @__PURE__ */ jsx(TableCell, { className: "font-mono text-sm text-muted-foreground", children: transaction.truncatedRef })
|
|
2565
|
+
] }, transaction.id || index)) })
|
|
2174
2566
|
] }) })
|
|
2175
2567
|
] })
|
|
2176
2568
|
] });
|
|
@@ -2178,384 +2570,6 @@ var TransactionsList = () => {
|
|
|
2178
2570
|
var TransactionsView = () => {
|
|
2179
2571
|
return /* @__PURE__ */ jsx("div", { className: "space-y-6", children: /* @__PURE__ */ jsx(TransactionsList, {}) });
|
|
2180
2572
|
};
|
|
2181
|
-
var isDevelopment = process.env.NODE_ENV === "development";
|
|
2182
|
-
var logger = createConsola({
|
|
2183
|
-
level: isDevelopment ? 4 : 1
|
|
2184
|
-
}).withTag("ext-payments");
|
|
2185
|
-
var paymentsLogger = logger;
|
|
2186
|
-
var PaymentCreateSchema = z.object({
|
|
2187
|
-
amount_usd: z.number().min(0.01, "Amount must be at least $0.01"),
|
|
2188
|
-
currency_code: z.string().min(1, "Please select a currency")
|
|
2189
|
-
});
|
|
2190
|
-
var CreatePaymentDialog = () => {
|
|
2191
|
-
const [open, setOpen] = useState(false);
|
|
2192
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
2193
|
-
const { createPayment } = usePaymentsContext();
|
|
2194
|
-
const { currencies, isLoadingCurrencies } = useRootPaymentsContext();
|
|
2195
|
-
const form = useForm({
|
|
2196
|
-
resolver: zodResolver(PaymentCreateSchema),
|
|
2197
|
-
defaultValues: {
|
|
2198
|
-
amount_usd: 10,
|
|
2199
|
-
currency_code: "USDT"
|
|
2200
|
-
}
|
|
2201
|
-
});
|
|
2202
|
-
const currenciesList = useMemo(() => {
|
|
2203
|
-
const data = currencies?.currencies || currencies?.results || currencies || [];
|
|
2204
|
-
return Array.isArray(data) ? data : [];
|
|
2205
|
-
}, [currencies]);
|
|
2206
|
-
const currencyOptions = useMemo(() => {
|
|
2207
|
-
return currenciesList.filter((curr) => curr.is_enabled !== false).map((curr) => ({
|
|
2208
|
-
code: curr.code || curr.currency_code || curr.symbol,
|
|
2209
|
-
name: curr.name || curr.code || curr.currency_code,
|
|
2210
|
-
usd_rate: curr.usd_rate || curr.rate || 1,
|
|
2211
|
-
network: curr.network || null
|
|
2212
|
-
}));
|
|
2213
|
-
}, [currenciesList]);
|
|
2214
|
-
const calculateCryptoAmount = useMemo(() => {
|
|
2215
|
-
const amountUsd = form.watch("amount_usd");
|
|
2216
|
-
const currencyCode = form.watch("currency_code");
|
|
2217
|
-
const currency = currencyOptions.find((c) => c.code === currencyCode);
|
|
2218
|
-
if (!currency || !currency.usd_rate || !amountUsd) {
|
|
2219
|
-
return null;
|
|
2220
|
-
}
|
|
2221
|
-
const cryptoAmount = amountUsd / currency.usd_rate;
|
|
2222
|
-
return {
|
|
2223
|
-
amount: cryptoAmount,
|
|
2224
|
-
currency: currency.code,
|
|
2225
|
-
rate: currency.usd_rate,
|
|
2226
|
-
network: currency.network
|
|
2227
|
-
};
|
|
2228
|
-
}, [form.watch("amount_usd"), form.watch("currency_code"), currencyOptions]);
|
|
2229
|
-
useEffect(() => {
|
|
2230
|
-
const handleOpen = () => setOpen(true);
|
|
2231
|
-
const handleClose2 = () => setOpen(false);
|
|
2232
|
-
window.addEventListener(PAYMENT_EVENTS.OPEN_CREATE_PAYMENT_DIALOG, handleOpen);
|
|
2233
|
-
window.addEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose2);
|
|
2234
|
-
return () => {
|
|
2235
|
-
window.removeEventListener(PAYMENT_EVENTS.OPEN_CREATE_PAYMENT_DIALOG, handleOpen);
|
|
2236
|
-
window.removeEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose2);
|
|
2237
|
-
};
|
|
2238
|
-
}, []);
|
|
2239
|
-
const handleClose = () => {
|
|
2240
|
-
setOpen(false);
|
|
2241
|
-
form.reset();
|
|
2242
|
-
};
|
|
2243
|
-
useEffect(() => {
|
|
2244
|
-
if (currencyOptions.length > 0 && !form.getValues("currency_code")) {
|
|
2245
|
-
form.setValue("currency_code", currencyOptions[0].code);
|
|
2246
|
-
}
|
|
2247
|
-
}, [currencyOptions, form]);
|
|
2248
|
-
const handleSubmit = async (data) => {
|
|
2249
|
-
try {
|
|
2250
|
-
setIsSubmitting(true);
|
|
2251
|
-
const result = await createPayment();
|
|
2252
|
-
handleClose();
|
|
2253
|
-
closePaymentsDialog();
|
|
2254
|
-
const paymentData = result;
|
|
2255
|
-
const paymentId = paymentData?.payment?.id || paymentData?.id;
|
|
2256
|
-
if (paymentId) {
|
|
2257
|
-
openPaymentDetailsDialog(String(paymentId));
|
|
2258
|
-
}
|
|
2259
|
-
} catch (error) {
|
|
2260
|
-
paymentsLogger.error("Failed to create payment:", error);
|
|
2261
|
-
} finally {
|
|
2262
|
-
setIsSubmitting(false);
|
|
2263
|
-
}
|
|
2264
|
-
};
|
|
2265
|
-
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (isOpen) => !isOpen && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
|
|
2266
|
-
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
2267
|
-
/* @__PURE__ */ jsx(DialogTitle, { children: "Create Payment" }),
|
|
2268
|
-
/* @__PURE__ */ jsx(DialogDescription, { children: "Create a new payment to add funds to your account." })
|
|
2269
|
-
] }),
|
|
2270
|
-
/* @__PURE__ */ jsx(Form, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit: form.handleSubmit(handleSubmit), className: "space-y-4", children: [
|
|
2271
|
-
/* @__PURE__ */ jsx(
|
|
2272
|
-
FormField,
|
|
2273
|
-
{
|
|
2274
|
-
control: form.control,
|
|
2275
|
-
name: "amount_usd",
|
|
2276
|
-
render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { children: [
|
|
2277
|
-
/* @__PURE__ */ jsx(FormLabel, { children: "Amount (USD)" }),
|
|
2278
|
-
/* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
|
|
2279
|
-
Input,
|
|
2280
|
-
{
|
|
2281
|
-
type: "number",
|
|
2282
|
-
step: "0.01",
|
|
2283
|
-
min: "0.01",
|
|
2284
|
-
placeholder: "10.00",
|
|
2285
|
-
...field,
|
|
2286
|
-
onChange: (e) => field.onChange(parseFloat(e.target.value) || 0)
|
|
2287
|
-
}
|
|
2288
|
-
) }),
|
|
2289
|
-
/* @__PURE__ */ jsx(FormDescription, { children: "The amount you want to pay in USD." }),
|
|
2290
|
-
/* @__PURE__ */ jsx(FormMessage, {})
|
|
2291
|
-
] })
|
|
2292
|
-
}
|
|
2293
|
-
),
|
|
2294
|
-
/* @__PURE__ */ jsx(
|
|
2295
|
-
FormField,
|
|
2296
|
-
{
|
|
2297
|
-
control: form.control,
|
|
2298
|
-
name: "currency_code",
|
|
2299
|
-
render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { children: [
|
|
2300
|
-
/* @__PURE__ */ jsx(FormLabel, { children: "Currency" }),
|
|
2301
|
-
/* @__PURE__ */ jsxs(
|
|
2302
|
-
Select,
|
|
2303
|
-
{
|
|
2304
|
-
onValueChange: field.onChange,
|
|
2305
|
-
defaultValue: field.value,
|
|
2306
|
-
disabled: isLoadingCurrencies,
|
|
2307
|
-
children: [
|
|
2308
|
-
/* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select currency..." }) }) }),
|
|
2309
|
-
/* @__PURE__ */ jsx(SelectContent, { children: currencyOptions.map((curr) => /* @__PURE__ */ jsx(SelectItem, { value: curr.code, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2310
|
-
/* @__PURE__ */ jsx(TokenIcon, { symbol: curr.code, size: 16 }),
|
|
2311
|
-
/* @__PURE__ */ jsx("span", { children: curr.code }),
|
|
2312
|
-
curr.network && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
2313
|
-
"(",
|
|
2314
|
-
curr.network,
|
|
2315
|
-
")"
|
|
2316
|
-
] })
|
|
2317
|
-
] }) }, curr.code)) })
|
|
2318
|
-
]
|
|
2319
|
-
}
|
|
2320
|
-
),
|
|
2321
|
-
/* @__PURE__ */ jsx(FormDescription, { children: "The cryptocurrency to use for payment." }),
|
|
2322
|
-
/* @__PURE__ */ jsx(FormMessage, {})
|
|
2323
|
-
] })
|
|
2324
|
-
}
|
|
2325
|
-
),
|
|
2326
|
-
calculateCryptoAmount && /* @__PURE__ */ jsxs("div", { className: "rounded-sm bg-muted p-4 space-y-3", children: [
|
|
2327
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2328
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "You will send" }),
|
|
2329
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2330
|
-
/* @__PURE__ */ jsx(TokenIcon, { symbol: calculateCryptoAmount.currency, size: 16 }),
|
|
2331
|
-
/* @__PURE__ */ jsxs("span", { className: "font-mono font-semibold", children: [
|
|
2332
|
-
calculateCryptoAmount.amount.toFixed(8),
|
|
2333
|
-
" ",
|
|
2334
|
-
calculateCryptoAmount.currency
|
|
2335
|
-
] })
|
|
2336
|
-
] })
|
|
2337
|
-
] }),
|
|
2338
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2339
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "You will receive" }),
|
|
2340
|
-
/* @__PURE__ */ jsxs("span", { className: "text-lg font-bold", children: [
|
|
2341
|
-
"$",
|
|
2342
|
-
form.watch("amount_usd")?.toFixed(2),
|
|
2343
|
-
" USD"
|
|
2344
|
-
] })
|
|
2345
|
-
] }),
|
|
2346
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
2347
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Rate" }),
|
|
2348
|
-
/* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
2349
|
-
"1 ",
|
|
2350
|
-
calculateCryptoAmount.currency,
|
|
2351
|
-
" = $",
|
|
2352
|
-
calculateCryptoAmount.rate?.toFixed(2)
|
|
2353
|
-
] })
|
|
2354
|
-
] }),
|
|
2355
|
-
calculateCryptoAmount.network && /* @__PURE__ */ jsx("div", { className: "border-t pt-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2356
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Network" }),
|
|
2357
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: calculateCryptoAmount.network })
|
|
2358
|
-
] }) })
|
|
2359
|
-
] }),
|
|
2360
|
-
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
2361
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: handleClose, disabled: isSubmitting, children: "Cancel" }),
|
|
2362
|
-
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: isSubmitting || currencyOptions.length === 0, children: isSubmitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2363
|
-
/* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4 mr-2 animate-spin" }),
|
|
2364
|
-
"Creating..."
|
|
2365
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2366
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-2" }),
|
|
2367
|
-
"Create Payment"
|
|
2368
|
-
] }) })
|
|
2369
|
-
] })
|
|
2370
|
-
] }) })
|
|
2371
|
-
] }) });
|
|
2372
|
-
};
|
|
2373
|
-
var PaymentDetailsDialog = () => {
|
|
2374
|
-
const [open, setOpen] = useState(false);
|
|
2375
|
-
const [paymentId, setPaymentId] = useState(null);
|
|
2376
|
-
const [timeLeft, setTimeLeft] = useState("");
|
|
2377
|
-
const shouldFetch = open && !!paymentId;
|
|
2378
|
-
const { data: payment, isLoading, error, mutate } = usePaymentsPaymentsRetrieve(
|
|
2379
|
-
shouldFetch ? paymentId : "",
|
|
2380
|
-
apiPayments
|
|
2381
|
-
);
|
|
2382
|
-
useEffect(() => {
|
|
2383
|
-
const handleOpen = (event) => {
|
|
2384
|
-
const customEvent = event;
|
|
2385
|
-
setPaymentId(customEvent.detail.id);
|
|
2386
|
-
setOpen(true);
|
|
2387
|
-
};
|
|
2388
|
-
const handleClose2 = () => {
|
|
2389
|
-
setOpen(false);
|
|
2390
|
-
setPaymentId(null);
|
|
2391
|
-
};
|
|
2392
|
-
window.addEventListener(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, handleOpen);
|
|
2393
|
-
window.addEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose2);
|
|
2394
|
-
return () => {
|
|
2395
|
-
window.removeEventListener(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, handleOpen);
|
|
2396
|
-
window.removeEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose2);
|
|
2397
|
-
};
|
|
2398
|
-
}, []);
|
|
2399
|
-
const handleClose = () => {
|
|
2400
|
-
setOpen(false);
|
|
2401
|
-
setPaymentId(null);
|
|
2402
|
-
};
|
|
2403
|
-
useEffect(() => {
|
|
2404
|
-
if (!payment?.expires_at) return;
|
|
2405
|
-
const updateTimeLeft = () => {
|
|
2406
|
-
const now = (/* @__PURE__ */ new Date()).getTime();
|
|
2407
|
-
const expires = new Date(payment.expires_at).getTime();
|
|
2408
|
-
const diff = expires - now;
|
|
2409
|
-
if (diff <= 0) {
|
|
2410
|
-
setTimeLeft("Expired");
|
|
2411
|
-
return;
|
|
2412
|
-
}
|
|
2413
|
-
const hours = Math.floor(diff / (1e3 * 60 * 60));
|
|
2414
|
-
const minutes = Math.floor(diff % (1e3 * 60 * 60) / (1e3 * 60));
|
|
2415
|
-
const seconds = Math.floor(diff % (1e3 * 60) / 1e3);
|
|
2416
|
-
setTimeLeft(`${hours}h ${minutes}m ${seconds}s`);
|
|
2417
|
-
};
|
|
2418
|
-
updateTimeLeft();
|
|
2419
|
-
const interval = setInterval(updateTimeLeft, 1e3);
|
|
2420
|
-
return () => clearInterval(interval);
|
|
2421
|
-
}, [payment?.expires_at]);
|
|
2422
|
-
const getStatusInfo = () => {
|
|
2423
|
-
switch (payment?.status?.toLowerCase()) {
|
|
2424
|
-
case "pending":
|
|
2425
|
-
return { icon: Clock, color: "text-yellow-500", bg: "bg-yellow-500/10" };
|
|
2426
|
-
case "completed":
|
|
2427
|
-
case "success":
|
|
2428
|
-
return { icon: CheckCircle2, color: "text-green-500", bg: "bg-green-500/10" };
|
|
2429
|
-
case "failed":
|
|
2430
|
-
case "error":
|
|
2431
|
-
return { icon: XCircle, color: "text-red-500", bg: "bg-red-500/10" };
|
|
2432
|
-
case "expired":
|
|
2433
|
-
return { icon: AlertCircle, color: "text-gray-500", bg: "bg-gray-500/10" };
|
|
2434
|
-
case "confirming":
|
|
2435
|
-
return { icon: RefreshCw, color: "text-blue-500", bg: "bg-blue-500/10" };
|
|
2436
|
-
default:
|
|
2437
|
-
return { icon: Clock, color: "text-gray-500", bg: "bg-gray-500/10" };
|
|
2438
|
-
}
|
|
2439
|
-
};
|
|
2440
|
-
if (!open) return null;
|
|
2441
|
-
if (isLoading) {
|
|
2442
|
-
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (isOpen) => !isOpen && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-lg", children: [
|
|
2443
|
-
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
2444
|
-
/* @__PURE__ */ jsx(DialogTitle, { children: "Payment Details" }),
|
|
2445
|
-
/* @__PURE__ */ jsx(DialogDescription, { children: "Loading payment information..." })
|
|
2446
|
-
] }),
|
|
2447
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-8 w-8 animate-spin text-muted-foreground" }) })
|
|
2448
|
-
] }) });
|
|
2449
|
-
}
|
|
2450
|
-
if (shouldFetch && !isLoading && (error || !payment)) {
|
|
2451
|
-
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (isOpen) => !isOpen && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-lg", children: [
|
|
2452
|
-
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
2453
|
-
/* @__PURE__ */ jsx(DialogTitle, { children: "Payment Details" }),
|
|
2454
|
-
/* @__PURE__ */ jsx(DialogDescription, { children: "Failed to load payment information" })
|
|
2455
|
-
] }),
|
|
2456
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 space-y-4", children: [
|
|
2457
|
-
/* @__PURE__ */ jsx(XCircle, { className: "h-12 w-12 text-destructive" }),
|
|
2458
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: error ? `Error: ${error}` : "Payment not found" }),
|
|
2459
|
-
/* @__PURE__ */ jsx(Button, { onClick: () => mutate(), children: "Try Again" })
|
|
2460
|
-
] })
|
|
2461
|
-
] }) });
|
|
2462
|
-
}
|
|
2463
|
-
const statusInfo = getStatusInfo();
|
|
2464
|
-
const StatusIcon = statusInfo.icon;
|
|
2465
|
-
const qrCodeUrl = payment.pay_address ? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(payment.pay_address)}` : null;
|
|
2466
|
-
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (isOpen) => !isOpen && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-lg", children: [
|
|
2467
|
-
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
2468
|
-
/* @__PURE__ */ jsx(DialogTitle, { children: "Payment Details" }),
|
|
2469
|
-
/* @__PURE__ */ jsx(DialogDescription, { children: "Send cryptocurrency to complete your payment" })
|
|
2470
|
-
] }),
|
|
2471
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
2472
|
-
/* @__PURE__ */ jsxs("div", { className: `flex items-center gap-3 p-4 rounded-sm ${statusInfo.bg}`, children: [
|
|
2473
|
-
/* @__PURE__ */ jsx(StatusIcon, { className: `h-5 w-5 ${statusInfo.color}` }),
|
|
2474
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
2475
|
-
/* @__PURE__ */ jsx("div", { className: "font-semibold capitalize", children: payment.status }),
|
|
2476
|
-
payment.status === "pending" && timeLeft && /* @__PURE__ */ jsxs("div", { className: "text-sm text-muted-foreground", children: [
|
|
2477
|
-
"Expires in ",
|
|
2478
|
-
timeLeft
|
|
2479
|
-
] })
|
|
2480
|
-
] })
|
|
2481
|
-
] }),
|
|
2482
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2483
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 bg-muted rounded-sm", children: [
|
|
2484
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Amount to send" }),
|
|
2485
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2486
|
-
/* @__PURE__ */ jsx(TokenIcon, { symbol: String(payment.currency_code || "BTC"), size: 20 }),
|
|
2487
|
-
/* @__PURE__ */ jsxs("span", { className: "font-mono font-bold text-lg", children: [
|
|
2488
|
-
payment.pay_amount || "0.00000000",
|
|
2489
|
-
" ",
|
|
2490
|
-
payment.currency_code
|
|
2491
|
-
] })
|
|
2492
|
-
] })
|
|
2493
|
-
] }),
|
|
2494
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4", children: [
|
|
2495
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Equivalent to" }),
|
|
2496
|
-
/* @__PURE__ */ jsxs("span", { className: "font-semibold text-lg", children: [
|
|
2497
|
-
"$",
|
|
2498
|
-
parseFloat(payment.amount_usd || "0").toFixed(2),
|
|
2499
|
-
" USD"
|
|
2500
|
-
] })
|
|
2501
|
-
] }),
|
|
2502
|
-
payment.internal_payment_id && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4", children: [
|
|
2503
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Payment Order #" }),
|
|
2504
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: payment.internal_payment_id })
|
|
2505
|
-
] }),
|
|
2506
|
-
payment.currency_network && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4", children: [
|
|
2507
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Network" }),
|
|
2508
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium", children: payment.currency_network })
|
|
2509
|
-
] })
|
|
2510
|
-
] }),
|
|
2511
|
-
qrCodeUrl && payment.status === "pending" && /* @__PURE__ */ jsx("div", { className: "flex justify-center p-6 bg-white rounded-sm", children: /* @__PURE__ */ jsx("img", { src: qrCodeUrl, alt: "Payment QR Code", className: "w-48 h-48" }) }),
|
|
2512
|
-
payment.pay_address && payment.status === "pending" && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2513
|
-
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Payment Address" }),
|
|
2514
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2515
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 p-3 bg-muted rounded-sm font-mono text-sm break-all", children: payment.pay_address }),
|
|
2516
|
-
/* @__PURE__ */ jsx(CopyButton, { value: payment.pay_address, variant: "outline" })
|
|
2517
|
-
] })
|
|
2518
|
-
] }),
|
|
2519
|
-
payment.transaction_hash && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2520
|
-
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Transaction Hash" }),
|
|
2521
|
-
/* @__PURE__ */ jsx("div", { className: "p-3 bg-muted rounded-sm font-mono text-sm break-all", children: payment.transaction_hash })
|
|
2522
|
-
] }),
|
|
2523
|
-
payment.payment_url && payment.status === "pending" && /* @__PURE__ */ jsxs(
|
|
2524
|
-
Button,
|
|
2525
|
-
{
|
|
2526
|
-
variant: "outline",
|
|
2527
|
-
className: "w-full",
|
|
2528
|
-
onClick: () => window.open(payment.payment_url, "_blank"),
|
|
2529
|
-
children: [
|
|
2530
|
-
/* @__PURE__ */ jsx(ExternalLink, { className: "h-4 w-4 mr-2" }),
|
|
2531
|
-
"Open in Payment Provider"
|
|
2532
|
-
]
|
|
2533
|
-
}
|
|
2534
|
-
),
|
|
2535
|
-
/* @__PURE__ */ jsxs("div", { className: "pt-4 border-t space-y-2 text-xs text-muted-foreground", children: [
|
|
2536
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
2537
|
-
/* @__PURE__ */ jsx("span", { children: "Payment ID" }),
|
|
2538
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: payment.id })
|
|
2539
|
-
] }),
|
|
2540
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
2541
|
-
/* @__PURE__ */ jsx("span", { children: "Created" }),
|
|
2542
|
-
/* @__PURE__ */ jsx("span", { children: new Date(payment.created_at).toLocaleString() })
|
|
2543
|
-
] }),
|
|
2544
|
-
payment.confirmations_count !== void 0 && /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
2545
|
-
/* @__PURE__ */ jsx("span", { children: "Confirmations" }),
|
|
2546
|
-
/* @__PURE__ */ jsx("span", { children: payment.confirmations_count })
|
|
2547
|
-
] })
|
|
2548
|
-
] })
|
|
2549
|
-
] }),
|
|
2550
|
-
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
2551
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleClose, children: "Close" }),
|
|
2552
|
-
/* @__PURE__ */ jsxs(Button, { onClick: () => mutate(), variant: "ghost", size: "sm", children: [
|
|
2553
|
-
/* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4 mr-2" }),
|
|
2554
|
-
"Refresh"
|
|
2555
|
-
] })
|
|
2556
|
-
] })
|
|
2557
|
-
] }) });
|
|
2558
|
-
};
|
|
2559
2573
|
var PaymentsLayout = () => {
|
|
2560
2574
|
return /* @__PURE__ */ jsx(RootPaymentsProvider, { children: /* @__PURE__ */ jsxs("div", { className: "h-full p-6 space-y-6", children: [
|
|
2561
2575
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -2594,7 +2608,7 @@ var PaymentsLayout = () => {
|
|
|
2594
2608
|
// package.json
|
|
2595
2609
|
var package_default = {
|
|
2596
2610
|
name: "@djangocfg/ext-payments",
|
|
2597
|
-
version: "1.0.
|
|
2611
|
+
version: "1.0.6",
|
|
2598
2612
|
description: "Payments system extension for DjangoCFG",
|
|
2599
2613
|
keywords: [
|
|
2600
2614
|
"django",
|
|
@@ -2662,7 +2676,8 @@ var package_default = {
|
|
|
2662
2676
|
"p-retry": "^7.0.0",
|
|
2663
2677
|
react: "^18 || ^19",
|
|
2664
2678
|
swr: "^2.3.7",
|
|
2665
|
-
zod: "^4.1.13"
|
|
2679
|
+
zod: "^4.1.13",
|
|
2680
|
+
moment: "^2.30.1"
|
|
2666
2681
|
},
|
|
2667
2682
|
devDependencies: {
|
|
2668
2683
|
"@djangocfg/api": "workspace:*",
|