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