@djangocfg/ext-payments 1.0.17 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/config.cjs +1 -1
  2. package/dist/config.js +1 -1
  3. package/dist/index.cjs +1175 -290
  4. package/dist/index.d.cts +226 -80
  5. package/dist/index.d.ts +226 -80
  6. package/dist/index.js +1157 -255
  7. package/package.json +9 -9
  8. package/src/WalletPage.tsx +100 -0
  9. package/src/api/generated/ext_payments/CLAUDE.md +6 -4
  10. package/src/api/generated/ext_payments/_utils/fetchers/ext_payments__payments.ts +37 -6
  11. package/src/api/generated/ext_payments/_utils/hooks/ext_payments__payments.ts +34 -3
  12. package/src/api/generated/ext_payments/_utils/schemas/Balance.schema.ts +1 -1
  13. package/src/api/generated/ext_payments/_utils/schemas/PaymentCreateResponse.schema.ts +22 -0
  14. package/src/api/generated/ext_payments/_utils/schemas/PaymentDetail.schema.ts +3 -3
  15. package/src/api/generated/ext_payments/_utils/schemas/PaymentList.schema.ts +2 -2
  16. package/src/api/generated/ext_payments/_utils/schemas/Transaction.schema.ts +1 -1
  17. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCancelResponse.schema.ts +22 -0
  18. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCreateResponse.schema.ts +22 -0
  19. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalDetail.schema.ts +5 -5
  20. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalList.schema.ts +2 -2
  21. package/src/api/generated/ext_payments/_utils/schemas/index.ts +3 -0
  22. package/src/api/generated/ext_payments/client.ts +1 -1
  23. package/src/api/generated/ext_payments/ext_payments__payments/client.ts +49 -4
  24. package/src/api/generated/ext_payments/ext_payments__payments/models.ts +33 -14
  25. package/src/api/generated/ext_payments/index.ts +1 -1
  26. package/src/api/generated/ext_payments/schema.json +167 -33
  27. package/src/components/AddFundsSheet.tsx +157 -73
  28. package/src/components/CurrencyCombobox.tsx +49 -0
  29. package/src/components/PaymentSheet.tsx +94 -32
  30. package/src/components/WithdrawSheet.tsx +121 -95
  31. package/src/components/WithdrawalSheet.tsx +332 -0
  32. package/src/components/index.ts +1 -8
  33. package/src/config.ts +1 -0
  34. package/src/contexts/WalletContext.tsx +10 -9
  35. package/src/contexts/index.ts +5 -1
  36. package/src/contexts/types.ts +46 -0
  37. package/src/hooks/index.ts +3 -0
  38. package/src/hooks/useCurrencyOptions.ts +79 -0
  39. package/src/hooks/useEstimate.ts +113 -0
  40. package/src/hooks/useWithdrawalEstimate.ts +117 -0
  41. package/src/index.ts +3 -0
  42. package/src/types/index.ts +78 -0
  43. package/src/utils/errors.ts +36 -0
  44. package/src/utils/format.ts +65 -0
  45. package/src/utils/index.ts +3 -0
  46. package/src/components/ResponsiveSheet.tsx +0 -151
package/dist/index.cjs CHANGED
@@ -1,44 +1,25 @@
1
1
  'use strict';
2
2
 
3
+ var react = require('react');
4
+ var api = require('@djangocfg/ext-base/api');
3
5
  var consola = require('consola');
4
6
  var pRetry = require('p-retry');
5
7
  var zod = require('zod');
6
- var api = require('@djangocfg/ext-base/api');
8
+ var useSWR = require('swr');
9
+ var jsxRuntime = require('react/jsx-runtime');
7
10
  var lucideReact = require('lucide-react');
8
11
  var uiCore = require('@djangocfg/ui-core');
9
12
  var lib = require('@djangocfg/ui-core/lib');
10
- var React2 = require('react');
11
- var useSWR2 = require('swr');
12
- var jsxRuntime = require('react/jsx-runtime');
13
- var moment2 = require('moment');
13
+ var moment3 = require('moment');
14
14
  var reactHookForm = require('react-hook-form');
15
15
  var zod$1 = require('@hookform/resolvers/zod');
16
16
  var extBase = require('@djangocfg/ext-base');
17
17
 
18
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
19
 
20
- function _interopNamespace(e) {
21
- if (e && e.__esModule) return e;
22
- var n = Object.create(null);
23
- if (e) {
24
- Object.keys(e).forEach(function (k) {
25
- if (k !== 'default') {
26
- var d = Object.getOwnPropertyDescriptor(e, k);
27
- Object.defineProperty(n, k, d.get ? d : {
28
- enumerable: true,
29
- get: function () { return e[k]; }
30
- });
31
- }
32
- });
33
- }
34
- n.default = e;
35
- return Object.freeze(n);
36
- }
37
-
38
20
  var pRetry__default = /*#__PURE__*/_interopDefault(pRetry);
39
- var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
40
- var useSWR2__default = /*#__PURE__*/_interopDefault(useSWR2);
41
- var moment2__default = /*#__PURE__*/_interopDefault(moment2);
21
+ var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
22
+ var moment3__default = /*#__PURE__*/_interopDefault(moment3);
42
23
 
43
24
  var __defProp = Object.defineProperty;
44
25
  var __export = (target, all) => {
@@ -64,12 +45,47 @@ var ExtPaymentsPayments = class {
64
45
  /**
65
46
  * Get available currencies
66
47
  *
67
- * Returns list of available currencies with token+network info
48
+ * Returns list of available currencies with token+network info, popular
49
+ * first
68
50
  */
69
51
  async currenciesList() {
70
52
  const response = await this.client.request("GET", "/cfg/payments/currencies/");
71
53
  return response;
72
54
  }
55
+ /**
56
+ * Get currency estimate
57
+ *
58
+ * Get estimated crypto amount for a given USD amount, including min amount
59
+ */
60
+ async currenciesEstimateRetrieve(...args) {
61
+ const code = args[0];
62
+ const isParamsObject = args.length === 2 && typeof args[1] === "object" && args[1] !== null && !Array.isArray(args[1]);
63
+ let params;
64
+ if (isParamsObject) {
65
+ params = args[1];
66
+ } else {
67
+ params = { amount: args[1] };
68
+ }
69
+ const response = await this.client.request("GET", `/cfg/payments/currencies/${code}/estimate/`, { params });
70
+ return response;
71
+ }
72
+ /**
73
+ * Get withdrawal estimate
74
+ *
75
+ * Get estimated crypto amount for withdrawal with fee breakdown
76
+ */
77
+ async currenciesWithdrawalEstimateRetrieve(...args) {
78
+ const code = args[0];
79
+ const isParamsObject = args.length === 2 && typeof args[1] === "object" && args[1] !== null && !Array.isArray(args[1]);
80
+ let params;
81
+ if (isParamsObject) {
82
+ params = args[1];
83
+ } else {
84
+ params = { amount: args[1] };
85
+ }
86
+ const response = await this.client.request("GET", `/cfg/payments/currencies/${code}/withdrawal-estimate/`, { params });
87
+ return response;
88
+ }
73
89
  /**
74
90
  * ViewSet for payment operations. Endpoints: - GET /payments/ - List
75
91
  * user's payments - GET /payments/{id}/ - Get payment details - POST
@@ -747,8 +763,8 @@ var APIClient = class {
747
763
  // src/api/generated/ext_payments/storage.ts
748
764
  var LocalStorageAdapter = class {
749
765
  logger;
750
- constructor(logger) {
751
- this.logger = logger;
766
+ constructor(logger2) {
767
+ this.logger = logger2;
752
768
  }
753
769
  getItem(key) {
754
770
  try {
@@ -790,8 +806,8 @@ var LocalStorageAdapter = class {
790
806
  };
791
807
  var CookieStorageAdapter = class {
792
808
  logger;
793
- constructor(logger) {
794
- this.logger = logger;
809
+ constructor(logger2) {
810
+ this.logger = logger2;
795
811
  }
796
812
  getItem(key) {
797
813
  try {
@@ -840,8 +856,8 @@ var CookieStorageAdapter = class {
840
856
  var MemoryStorageAdapter = class {
841
857
  storage = /* @__PURE__ */ new Map();
842
858
  logger;
843
- constructor(logger) {
844
- this.logger = logger;
859
+ constructor(logger2) {
860
+ this.logger = logger2;
845
861
  }
846
862
  getItem(key) {
847
863
  const value = this.storage.get(key) || null;
@@ -926,10 +942,13 @@ __export(schemas_exports, {
926
942
  PaginatedPaymentListListSchema: () => PaginatedPaymentListListSchema,
927
943
  PaginatedWithdrawalListListSchema: () => PaginatedWithdrawalListListSchema,
928
944
  PaymentCreateRequestSchema: () => PaymentCreateRequestSchema,
945
+ PaymentCreateResponseSchema: () => PaymentCreateResponseSchema,
929
946
  PaymentDetailSchema: () => PaymentDetailSchema,
930
947
  PaymentListSchema: () => PaymentListSchema,
931
948
  TransactionSchema: () => TransactionSchema,
949
+ WithdrawalCancelResponseSchema: () => WithdrawalCancelResponseSchema,
932
950
  WithdrawalCreateRequestSchema: () => WithdrawalCreateRequestSchema,
951
+ WithdrawalCreateResponseSchema: () => WithdrawalCreateResponseSchema,
933
952
  WithdrawalDetailSchema: () => WithdrawalDetailSchema,
934
953
  WithdrawalListSchema: () => WithdrawalListSchema
935
954
  });
@@ -938,7 +957,7 @@ var BalanceSchema = zod.z.object({
938
957
  balance_display: zod.z.string(),
939
958
  total_deposited: zod.z.string(),
940
959
  total_withdrawn: zod.z.string(),
941
- last_transaction_at: zod.z.iso.datetime().nullable()
960
+ last_transaction_at: zod.z.string().datetime({ offset: true }).nullable()
942
961
  });
943
962
  var CurrencySchema = zod.z.object({
944
963
  code: zod.z.string(),
@@ -960,8 +979,8 @@ var PaymentListSchema = zod.z.object({
960
979
  currency_token: zod.z.string(),
961
980
  status: zod.z.nativeEnum(PaymentListStatus),
962
981
  status_display: zod.z.string(),
963
- created_at: zod.z.iso.datetime(),
964
- completed_at: zod.z.iso.datetime().nullable()
982
+ created_at: zod.z.string().datetime({ offset: true }),
983
+ completed_at: zod.z.string().datetime({ offset: true }).nullable()
965
984
  });
966
985
 
967
986
  // src/api/generated/ext_payments/_utils/schemas/PaginatedPaymentListList.schema.ts
@@ -985,8 +1004,8 @@ var WithdrawalListSchema = zod.z.object({
985
1004
  currency_token: zod.z.string(),
986
1005
  status: zod.z.nativeEnum(WithdrawalListStatus),
987
1006
  status_display: zod.z.string(),
988
- created_at: zod.z.iso.datetime(),
989
- completed_at: zod.z.iso.datetime().nullable()
1007
+ created_at: zod.z.string().datetime({ offset: true }),
1008
+ completed_at: zod.z.string().datetime({ offset: true }).nullable()
990
1009
  });
991
1010
 
992
1011
  // src/api/generated/ext_payments/_utils/schemas/PaginatedWithdrawalListList.schema.ts
@@ -1025,14 +1044,21 @@ var PaymentDetailSchema = zod.z.object({
1025
1044
  transaction_hash: zod.z.string().nullable(),
1026
1045
  explorer_link: zod.z.string().nullable(),
1027
1046
  confirmations_count: zod.z.int(),
1028
- expires_at: zod.z.iso.datetime().nullable(),
1029
- completed_at: zod.z.iso.datetime().nullable(),
1030
- created_at: zod.z.iso.datetime(),
1047
+ expires_at: zod.z.string().datetime({ offset: true }).nullable(),
1048
+ completed_at: zod.z.string().datetime({ offset: true }).nullable(),
1049
+ created_at: zod.z.string().datetime({ offset: true }),
1031
1050
  is_completed: zod.z.boolean(),
1032
1051
  is_failed: zod.z.boolean(),
1033
1052
  is_expired: zod.z.boolean(),
1034
1053
  description: zod.z.string()
1035
1054
  });
1055
+
1056
+ // src/api/generated/ext_payments/_utils/schemas/PaymentCreateResponse.schema.ts
1057
+ var PaymentCreateResponseSchema = zod.z.object({
1058
+ success: zod.z.boolean(),
1059
+ payment: PaymentDetailSchema,
1060
+ qr_code_url: zod.z.union([zod.z.url(), zod.z.literal("")]).nullable()
1061
+ });
1036
1062
  var TransactionSchema = zod.z.object({
1037
1063
  id: zod.z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
1038
1064
  transaction_type: zod.z.nativeEnum(TransactionTransactionType),
@@ -1042,12 +1068,7 @@ var TransactionSchema = zod.z.object({
1042
1068
  balance_after: zod.z.string(),
1043
1069
  payment_id: zod.z.string().nullable(),
1044
1070
  description: zod.z.string(),
1045
- created_at: zod.z.iso.datetime()
1046
- });
1047
- var WithdrawalCreateRequestSchema = zod.z.object({
1048
- amount_usd: zod.z.string(),
1049
- currency_code: zod.z.string().min(1).max(20),
1050
- wallet_address: zod.z.string().min(1).max(255)
1071
+ created_at: zod.z.string().datetime({ offset: true })
1051
1072
  });
1052
1073
  var WithdrawalDetailSchema = zod.z.object({
1053
1074
  id: zod.z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
@@ -1068,11 +1089,28 @@ var WithdrawalDetailSchema = zod.z.object({
1068
1089
  transaction_hash: zod.z.string().nullable(),
1069
1090
  explorer_link: zod.z.string().nullable(),
1070
1091
  admin_notes: zod.z.string(),
1071
- created_at: zod.z.iso.datetime(),
1072
- approved_at: zod.z.iso.datetime().nullable(),
1073
- completed_at: zod.z.iso.datetime().nullable(),
1074
- rejected_at: zod.z.iso.datetime().nullable(),
1075
- cancelled_at: zod.z.iso.datetime().nullable()
1092
+ created_at: zod.z.string().datetime({ offset: true }),
1093
+ approved_at: zod.z.string().datetime({ offset: true }).nullable(),
1094
+ completed_at: zod.z.string().datetime({ offset: true }).nullable(),
1095
+ rejected_at: zod.z.string().datetime({ offset: true }).nullable(),
1096
+ cancelled_at: zod.z.string().datetime({ offset: true }).nullable()
1097
+ });
1098
+
1099
+ // src/api/generated/ext_payments/_utils/schemas/WithdrawalCancelResponse.schema.ts
1100
+ var WithdrawalCancelResponseSchema = zod.z.object({
1101
+ success: zod.z.boolean(),
1102
+ withdrawal: WithdrawalDetailSchema,
1103
+ message: zod.z.string()
1104
+ });
1105
+ var WithdrawalCreateRequestSchema = zod.z.object({
1106
+ amount_usd: zod.z.string(),
1107
+ currency_code: zod.z.string().min(1).max(20),
1108
+ wallet_address: zod.z.string().min(1).max(255)
1109
+ });
1110
+ var WithdrawalCreateResponseSchema = zod.z.object({
1111
+ success: zod.z.boolean(),
1112
+ withdrawal: WithdrawalDetailSchema,
1113
+ message: zod.z.string()
1076
1114
  });
1077
1115
 
1078
1116
  // src/api/generated/ext_payments/validation-events.ts
@@ -1129,7 +1167,9 @@ __export(fetchers_exports, {
1129
1167
  createPaymentsWithdrawalsCancelCreate: () => createPaymentsWithdrawalsCancelCreate,
1130
1168
  createPaymentsWithdrawalsCreateCreate: () => createPaymentsWithdrawalsCreateCreate,
1131
1169
  getPaymentsBalanceRetrieve: () => getPaymentsBalanceRetrieve,
1170
+ getPaymentsCurrenciesEstimateRetrieve: () => getPaymentsCurrenciesEstimateRetrieve,
1132
1171
  getPaymentsCurrenciesList: () => getPaymentsCurrenciesList,
1172
+ getPaymentsCurrenciesWithdrawalEstimateRetrieve: () => getPaymentsCurrenciesWithdrawalEstimateRetrieve,
1133
1173
  getPaymentsPaymentsList: () => getPaymentsPaymentsList,
1134
1174
  getPaymentsPaymentsRetrieve: () => getPaymentsPaymentsRetrieve,
1135
1175
  getPaymentsPaymentsStatusRetrieve: () => getPaymentsPaymentsStatusRetrieve,
@@ -1240,6 +1280,16 @@ async function getPaymentsCurrenciesList(client) {
1240
1280
  const response = await api.ext_payments_payments.currenciesList();
1241
1281
  return response;
1242
1282
  }
1283
+ async function getPaymentsCurrenciesEstimateRetrieve(code, params, client) {
1284
+ const api = client || getAPIInstance();
1285
+ const response = await api.ext_payments_payments.currenciesEstimateRetrieve(code, params?.amount);
1286
+ return response;
1287
+ }
1288
+ async function getPaymentsCurrenciesWithdrawalEstimateRetrieve(code, params, client) {
1289
+ const api = client || getAPIInstance();
1290
+ const response = await api.ext_payments_payments.currenciesWithdrawalEstimateRetrieve(code, params?.amount);
1291
+ return response;
1292
+ }
1243
1293
  async function getPaymentsPaymentsList(params, client) {
1244
1294
  const api = client || getAPIInstance();
1245
1295
  const response = await api.ext_payments_payments.paymentsList(params?.page, params?.page_size);
@@ -1412,7 +1462,7 @@ async function createPaymentsPaymentsCreateCreate(data, client) {
1412
1462
  const api = client || getAPIInstance();
1413
1463
  const response = await api.ext_payments_payments.paymentsCreateCreate(data);
1414
1464
  try {
1415
- return PaymentDetailSchema.parse(response);
1465
+ return PaymentCreateResponseSchema.parse(response);
1416
1466
  } catch (error) {
1417
1467
  consola.consola.error("\u274C Zod Validation Failed");
1418
1468
  consola.consola.box(`createPaymentsPaymentsCreateCreate
@@ -1543,7 +1593,7 @@ async function createPaymentsWithdrawalsCancelCreate(id, client) {
1543
1593
  const api = client || getAPIInstance();
1544
1594
  const response = await api.ext_payments_payments.withdrawalsCancelCreate(id);
1545
1595
  try {
1546
- return WithdrawalDetailSchema.parse(response);
1596
+ return WithdrawalCancelResponseSchema.parse(response);
1547
1597
  } catch (error) {
1548
1598
  consola.consola.error("\u274C Zod Validation Failed");
1549
1599
  consola.consola.box(`createPaymentsWithdrawalsCancelCreate
@@ -1585,7 +1635,7 @@ async function createPaymentsWithdrawalsCreateCreate(data, client) {
1585
1635
  const api = client || getAPIInstance();
1586
1636
  const response = await api.ext_payments_payments.withdrawalsCreateCreate(data);
1587
1637
  try {
1588
- return WithdrawalDetailSchema.parse(response);
1638
+ return WithdrawalCreateResponseSchema.parse(response);
1589
1639
  } catch (error) {
1590
1640
  consola.consola.error("\u274C Zod Validation Failed");
1591
1641
  consola.consola.box(`createPaymentsWithdrawalsCreateCreate
@@ -1639,8 +1689,8 @@ var API = class {
1639
1689
  constructor(baseUrl, options) {
1640
1690
  this.baseUrl = baseUrl;
1641
1691
  this.options = options;
1642
- const logger = options?.loggerConfig ? new APILogger(options.loggerConfig) : void 0;
1643
- this.storage = options?.storage || new LocalStorageAdapter(logger);
1692
+ const logger2 = options?.loggerConfig ? new APILogger(options.loggerConfig) : void 0;
1693
+ this.storage = options?.storage || new LocalStorageAdapter(logger2);
1644
1694
  this._loadTokensFromStorage();
1645
1695
  this._client = new APIClient(this.baseUrl, {
1646
1696
  retryConfig: this.options?.retryConfig,
@@ -1749,16 +1799,260 @@ var API = class {
1749
1799
  return "./schema.json";
1750
1800
  }
1751
1801
  };
1802
+
1803
+ // src/api/index.ts
1752
1804
  api.initializeExtensionAPI(configureAPI);
1753
1805
  var apiPayments = api.createExtensionAPI(API);
1754
- var WalletContext = React2.createContext(void 0);
1806
+ function usePaymentsBalanceRetrieve(client) {
1807
+ return useSWR__default.default(
1808
+ "cfg-payments-balance",
1809
+ () => getPaymentsBalanceRetrieve(client)
1810
+ );
1811
+ }
1812
+ function usePaymentsCurrenciesList(client) {
1813
+ return useSWR__default.default(
1814
+ "cfg-payments-currencies",
1815
+ () => getPaymentsCurrenciesList(client)
1816
+ );
1817
+ }
1818
+ function usePaymentsPaymentsList(params, client) {
1819
+ return useSWR__default.default(
1820
+ params ? ["cfg-payments-payments", params] : "cfg-payments-payments",
1821
+ () => getPaymentsPaymentsList(params, client)
1822
+ );
1823
+ }
1824
+ function useCreatePaymentsPaymentsCreateCreate() {
1825
+ const { mutate } = useSWR.useSWRConfig();
1826
+ return async (data, client) => {
1827
+ const result = await createPaymentsPaymentsCreateCreate(data, client);
1828
+ mutate("cfg-payments-payments");
1829
+ return result;
1830
+ };
1831
+ }
1832
+ function usePaymentsTransactionsList(params, client) {
1833
+ return useSWR__default.default(
1834
+ params ? ["cfg-payments-transactions", params] : "cfg-payments-transactions",
1835
+ () => getPaymentsTransactionsList(params, client)
1836
+ );
1837
+ }
1838
+ function usePaymentsWithdrawalsList(params, client) {
1839
+ return useSWR__default.default(
1840
+ params ? ["cfg-payments-withdrawals", params] : "cfg-payments-withdrawals",
1841
+ () => getPaymentsWithdrawalsList(params, client)
1842
+ );
1843
+ }
1844
+ function useCreatePaymentsWithdrawalsCancelCreate() {
1845
+ const { mutate } = useSWR.useSWRConfig();
1846
+ return async (id, client) => {
1847
+ const result = await createPaymentsWithdrawalsCancelCreate(id, client);
1848
+ mutate("cfg-payments-withdrawals-cancel");
1849
+ return result;
1850
+ };
1851
+ }
1852
+ function useCreatePaymentsWithdrawalsCreateCreate() {
1853
+ const { mutate } = useSWR.useSWRConfig();
1854
+ return async (data, client) => {
1855
+ const result = await createPaymentsWithdrawalsCreateCreate(data, client);
1856
+ mutate("cfg-payments-withdrawals");
1857
+ return result;
1858
+ };
1859
+ }
1860
+ var WalletContext = react.createContext(void 0);
1861
+ function WalletProvider({ children }) {
1862
+ const {
1863
+ data: balance,
1864
+ isLoading: isLoadingBalance,
1865
+ mutate: mutateBalance
1866
+ } = usePaymentsBalanceRetrieve(apiPayments);
1867
+ const {
1868
+ data: paymentsData,
1869
+ isLoading: isLoadingPayments,
1870
+ mutate: mutatePayments
1871
+ } = usePaymentsPaymentsList({ page: 1, page_size: 20 }, apiPayments);
1872
+ const {
1873
+ data: transactionsData,
1874
+ isLoading: isLoadingTransactions,
1875
+ mutate: mutateTransactions
1876
+ } = usePaymentsTransactionsList({ limit: 20 }, apiPayments);
1877
+ const {
1878
+ data: currenciesData,
1879
+ isLoading: isLoadingCurrencies,
1880
+ mutate: mutateCurrencies
1881
+ } = usePaymentsCurrenciesList(apiPayments);
1882
+ const {
1883
+ data: withdrawalsData,
1884
+ isLoading: isLoadingWithdrawals,
1885
+ mutate: mutateWithdrawals
1886
+ } = usePaymentsWithdrawalsList({ page: 1, page_size: 20 }, apiPayments);
1887
+ const createPaymentMutation = useCreatePaymentsPaymentsCreateCreate();
1888
+ const createWithdrawalMutation = useCreatePaymentsWithdrawalsCreateCreate();
1889
+ const cancelWithdrawalMutation = useCreatePaymentsWithdrawalsCancelCreate();
1890
+ const balanceAmount = react.useMemo(() => {
1891
+ if (!balance?.balance_usd) return 0;
1892
+ return parseFloat(balance.balance_usd) || 0;
1893
+ }, [balance]);
1894
+ const activity = react.useMemo(() => {
1895
+ const items = [];
1896
+ const payments = paymentsData?.results || [];
1897
+ for (const payment of payments) {
1898
+ items.push({
1899
+ id: `payment-${payment.id}`,
1900
+ type: "payment",
1901
+ amount: payment.amount_usd,
1902
+ amountDisplay: `+$${parseFloat(payment.amount_usd).toFixed(2)}`,
1903
+ currency: payment.currency_code,
1904
+ status: mapPaymentStatus(payment.status),
1905
+ statusDisplay: payment.status_display,
1906
+ description: `${payment.currency_code} payment`,
1907
+ createdAt: payment.created_at,
1908
+ payment
1909
+ });
1910
+ }
1911
+ const withdrawals = withdrawalsData?.results || [];
1912
+ for (const withdrawal of withdrawals) {
1913
+ items.push({
1914
+ id: `withdrawal-${withdrawal.id}`,
1915
+ type: "withdrawal",
1916
+ amount: withdrawal.amount_usd,
1917
+ amountDisplay: `-$${parseFloat(withdrawal.amount_usd).toFixed(2)}`,
1918
+ currency: withdrawal.currency_code,
1919
+ status: mapWithdrawalStatus(withdrawal.status),
1920
+ statusDisplay: withdrawal.status_display,
1921
+ description: `${withdrawal.currency_code} withdrawal`,
1922
+ createdAt: withdrawal.created_at,
1923
+ withdrawal
1924
+ });
1925
+ }
1926
+ const transactions = transactionsData?.results || transactionsData || [];
1927
+ if (Array.isArray(transactions)) {
1928
+ for (const tx of transactions) {
1929
+ if (tx.payment_id && payments.some((p) => p.id === tx.payment_id)) {
1930
+ continue;
1931
+ }
1932
+ const isDeposit = tx.transaction_type === "deposit";
1933
+ items.push({
1934
+ id: `tx-${tx.id}`,
1935
+ type: isDeposit ? "deposit" : "withdrawal",
1936
+ amount: tx.amount_usd,
1937
+ amountDisplay: `${isDeposit ? "+" : "-"}$${Math.abs(parseFloat(tx.amount_usd)).toFixed(2)}`,
1938
+ status: "completed",
1939
+ statusDisplay: "Completed",
1940
+ description: tx.description || tx.type_display,
1941
+ createdAt: tx.created_at,
1942
+ transaction: tx
1943
+ });
1944
+ }
1945
+ }
1946
+ items.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
1947
+ return items;
1948
+ }, [paymentsData, withdrawalsData, transactionsData]);
1949
+ const currencies = react.useMemo(() => {
1950
+ const data = currenciesData?.currencies || currenciesData?.results || currenciesData || [];
1951
+ if (!Array.isArray(data)) return [];
1952
+ return data.filter((c) => c.is_enabled !== false).map((c) => ({
1953
+ code: c.code || c.currency_code || c.symbol,
1954
+ name: c.name || c.code,
1955
+ token: c.token || c.code,
1956
+ // Token symbol (e.g., USDT, WBTC) with fallback to code
1957
+ network: c.network || void 0,
1958
+ enabled: c.is_enabled !== false
1959
+ }));
1960
+ }, [currenciesData]);
1961
+ const addFunds = react.useCallback(async (data) => {
1962
+ const response = await createPaymentMutation(data, apiPayments);
1963
+ await Promise.all([mutateBalance(), mutatePayments(), mutateTransactions()]);
1964
+ return response.payment;
1965
+ }, [createPaymentMutation, mutateBalance, mutatePayments, mutateTransactions]);
1966
+ const withdraw = react.useCallback(async (data) => {
1967
+ const response = await createWithdrawalMutation(data, apiPayments);
1968
+ await Promise.all([mutateBalance(), mutateWithdrawals(), mutateTransactions()]);
1969
+ return response.withdrawal;
1970
+ }, [createWithdrawalMutation, mutateBalance, mutateWithdrawals, mutateTransactions]);
1971
+ const cancelWithdrawal = react.useCallback(async (id) => {
1972
+ await cancelWithdrawalMutation(id, apiPayments);
1973
+ await Promise.all([mutateBalance(), mutateWithdrawals(), mutateTransactions()]);
1974
+ }, [cancelWithdrawalMutation, mutateBalance, mutateWithdrawals, mutateTransactions]);
1975
+ const getPaymentDetails = react.useCallback(async (id) => {
1976
+ return apiPayments.ext_payments_payments.paymentsRetrieve(id);
1977
+ }, []);
1978
+ const getWithdrawalDetails = react.useCallback(async (id) => {
1979
+ return apiPayments.ext_payments_payments.withdrawalsRetrieve(id);
1980
+ }, []);
1981
+ const refreshWallet = react.useCallback(async () => {
1982
+ await Promise.all([
1983
+ mutateBalance(),
1984
+ mutatePayments(),
1985
+ mutateWithdrawals(),
1986
+ mutateTransactions(),
1987
+ mutateCurrencies()
1988
+ ]);
1989
+ }, [mutateBalance, mutatePayments, mutateWithdrawals, mutateTransactions, mutateCurrencies]);
1990
+ const isLoading = isLoadingBalance || isLoadingPayments || isLoadingWithdrawals || isLoadingTransactions;
1991
+ const isLoadingActivity = isLoadingPayments || isLoadingWithdrawals || isLoadingTransactions;
1992
+ const value = {
1993
+ balance,
1994
+ balanceAmount,
1995
+ isLoadingBalance,
1996
+ activity,
1997
+ isLoadingActivity,
1998
+ hasMoreActivity: (paymentsData?.count || 0) > 20 || (withdrawalsData?.count || 0) > 20,
1999
+ currencies,
2000
+ isLoadingCurrencies,
2001
+ addFunds,
2002
+ withdraw,
2003
+ cancelWithdrawal,
2004
+ getPaymentDetails,
2005
+ getWithdrawalDetails,
2006
+ refreshWallet,
2007
+ isLoading
2008
+ };
2009
+ return /* @__PURE__ */ jsxRuntime.jsx(WalletContext.Provider, { value, children });
2010
+ }
1755
2011
  function useWallet() {
1756
- const context = React2.useContext(WalletContext);
2012
+ const context = react.useContext(WalletContext);
1757
2013
  if (!context) {
1758
2014
  throw new Error("useWallet must be used within WalletProvider");
1759
2015
  }
1760
2016
  return context;
1761
2017
  }
2018
+ function mapPaymentStatus(status) {
2019
+ switch (status?.toLowerCase()) {
2020
+ case "completed":
2021
+ case "success":
2022
+ case "finished":
2023
+ return "completed";
2024
+ case "pending":
2025
+ case "waiting":
2026
+ return "pending";
2027
+ case "confirming":
2028
+ case "partially_paid":
2029
+ return "confirming";
2030
+ case "expired":
2031
+ return "expired";
2032
+ case "failed":
2033
+ case "error":
2034
+ case "cancelled":
2035
+ return "failed";
2036
+ default:
2037
+ return "pending";
2038
+ }
2039
+ }
2040
+ function mapWithdrawalStatus(status) {
2041
+ switch (status?.toLowerCase()) {
2042
+ case "completed":
2043
+ return "completed";
2044
+ case "pending":
2045
+ case "approved":
2046
+ return "pending";
2047
+ case "processing":
2048
+ return "confirming";
2049
+ case "rejected":
2050
+ case "cancelled":
2051
+ return "failed";
2052
+ default:
2053
+ return "pending";
2054
+ }
2055
+ }
1762
2056
  function BalanceHero({ onAddFunds, onWithdraw, className }) {
1763
2057
  const { balance, balanceAmount, isLoadingBalance, refreshWallet } = useWallet();
1764
2058
  const formattedBalance = new Intl.NumberFormat("en-US", {
@@ -1864,7 +2158,7 @@ function ActivityItem({ item, onClick }) {
1864
2158
  const StatusIcon = config.icon;
1865
2159
  const isPositive = item.type === "payment" || item.type === "deposit";
1866
2160
  const DirectionIcon = isPositive ? lucideReact.ArrowDownLeft : lucideReact.ArrowUpRight;
1867
- const relativeTime = moment2__default.default(item.createdAt).fromNow();
2161
+ const relativeTime = moment3__default.default(item.createdAt).fromNow();
1868
2162
  return /* @__PURE__ */ jsxRuntime.jsxs(
1869
2163
  "button",
1870
2164
  {
@@ -1953,42 +2247,300 @@ function ActivityList({
1953
2247
  )) })
1954
2248
  ] });
1955
2249
  }
2250
+ function useEstimate({
2251
+ currencyCode,
2252
+ amountUsd,
2253
+ minAmount = 0,
2254
+ debounceMs = 300,
2255
+ skip = false
2256
+ }) {
2257
+ const [estimate, setEstimate] = react.useState(null);
2258
+ const [isLoading, setIsLoading] = react.useState(false);
2259
+ const [error, setError] = react.useState(null);
2260
+ react.useEffect(() => {
2261
+ if (skip || !currencyCode || amountUsd < minAmount) {
2262
+ setEstimate(null);
2263
+ setError(null);
2264
+ return;
2265
+ }
2266
+ const fetchEstimate = async () => {
2267
+ setIsLoading(true);
2268
+ setError(null);
2269
+ try {
2270
+ const response = await apiPayments.ext_payments_payments.currenciesEstimateRetrieve(
2271
+ currencyCode,
2272
+ { amount: amountUsd }
2273
+ );
2274
+ if (response?.success && response?.estimated_amount) {
2275
+ setEstimate({
2276
+ estimatedAmount: parseFloat(response.estimated_amount),
2277
+ usdRate: parseFloat(response.usd_rate) || 0,
2278
+ minAmountUsd: response.min_amount_usd ? parseFloat(response.min_amount_usd) : null,
2279
+ isStablecoin: response.is_stablecoin || false,
2280
+ // New fee fields
2281
+ amountToReceive: parseFloat(response.amount_to_receive) || amountUsd,
2282
+ serviceFeeUsd: parseFloat(response.service_fee_usd) || 0,
2283
+ serviceFeePercent: parseFloat(response.service_fee_percent) || 0,
2284
+ totalToPayUsd: parseFloat(response.total_to_pay_usd) || amountUsd
2285
+ });
2286
+ } else {
2287
+ setEstimate(null);
2288
+ }
2289
+ } catch (err) {
2290
+ console.error("Failed to fetch estimate:", err);
2291
+ setEstimate(null);
2292
+ setError(err instanceof Error ? err : new Error("Failed to fetch estimate"));
2293
+ } finally {
2294
+ setIsLoading(false);
2295
+ }
2296
+ };
2297
+ const timeoutId = setTimeout(fetchEstimate, debounceMs);
2298
+ return () => clearTimeout(timeoutId);
2299
+ }, [currencyCode, amountUsd, minAmount, debounceMs, skip]);
2300
+ return { estimate, isLoading, error };
2301
+ }
2302
+ function useWithdrawalEstimate({
2303
+ currencyCode,
2304
+ amountUsd,
2305
+ minAmount = 10,
2306
+ debounceMs = 300,
2307
+ skip = false
2308
+ }) {
2309
+ const [estimate, setEstimate] = react.useState(null);
2310
+ const [isLoading, setIsLoading] = react.useState(false);
2311
+ const [error, setError] = react.useState(null);
2312
+ react.useEffect(() => {
2313
+ if (skip || !currencyCode || amountUsd < minAmount) {
2314
+ setEstimate(null);
2315
+ setError(null);
2316
+ return;
2317
+ }
2318
+ const fetchEstimate = async () => {
2319
+ setIsLoading(true);
2320
+ setError(null);
2321
+ try {
2322
+ const response = await apiPayments.ext_payments_payments.currenciesWithdrawalEstimateRetrieve(
2323
+ currencyCode,
2324
+ { amount: amountUsd }
2325
+ );
2326
+ if (response?.success && response?.estimated_amount) {
2327
+ setEstimate({
2328
+ estimatedAmount: parseFloat(response.estimated_amount),
2329
+ usdRate: parseFloat(response.usd_rate) || 0,
2330
+ minAmountUsd: response.min_amount_usd ? parseFloat(response.min_amount_usd) : null,
2331
+ isStablecoin: response.is_stablecoin || false,
2332
+ // Withdrawal-specific fields
2333
+ amountRequested: parseFloat(response.amount_requested) || amountUsd,
2334
+ serviceFeeUsd: parseFloat(response.service_fee_usd) || 0,
2335
+ serviceFeePercent: parseFloat(response.service_fee_percent) || 0,
2336
+ networkFeeUsd: parseFloat(response.network_fee_usd) || 0,
2337
+ totalFeesUsd: parseFloat(response.total_fees_usd) || 0,
2338
+ amountToReceive: parseFloat(response.amount_to_receive) || 0
2339
+ });
2340
+ } else {
2341
+ setEstimate(null);
2342
+ }
2343
+ } catch (err) {
2344
+ console.error("Failed to fetch withdrawal estimate:", err);
2345
+ setEstimate(null);
2346
+ setError(err instanceof Error ? err : new Error("Failed to fetch withdrawal estimate"));
2347
+ } finally {
2348
+ setIsLoading(false);
2349
+ }
2350
+ };
2351
+ const timeoutId = setTimeout(fetchEstimate, debounceMs);
2352
+ return () => clearTimeout(timeoutId);
2353
+ }, [currencyCode, amountUsd, minAmount, debounceMs, skip]);
2354
+ return { estimate, isLoading, error };
2355
+ }
2356
+ function useCurrencyOptions(currencies) {
2357
+ return react.useMemo(() => {
2358
+ return currencies.map((c) => ({
2359
+ value: c.code,
2360
+ label: c.name,
2361
+ token: c.token,
2362
+ network: c.network
2363
+ }));
2364
+ }, [currencies]);
2365
+ }
2366
+ function useDefaultCurrency({
2367
+ currencyOptions,
2368
+ savedCurrency,
2369
+ currentValue,
2370
+ setValue
2371
+ }) {
2372
+ react.useEffect(() => {
2373
+ if (currencyOptions.length > 0 && !currentValue) {
2374
+ const savedOption = savedCurrency && currencyOptions.find((c) => c.value === savedCurrency);
2375
+ if (savedOption) {
2376
+ setValue(savedOption.value);
2377
+ } else {
2378
+ const usdt = currencyOptions.find((c) => c.value.includes("USDT"));
2379
+ setValue(usdt?.value || currencyOptions[0].value);
2380
+ }
2381
+ }
2382
+ }, [currencyOptions, savedCurrency, currentValue, setValue]);
2383
+ }
2384
+ function useAutoSave(value, save, validate) {
2385
+ const saveRef = react.useRef(save);
2386
+ const validateRef = react.useRef(validate);
2387
+ react.useEffect(() => {
2388
+ saveRef.current = save;
2389
+ validateRef.current = validate;
2390
+ });
2391
+ react.useEffect(() => {
2392
+ const isValid = validateRef.current ? validateRef.current(value) : Boolean(value);
2393
+ if (isValid) {
2394
+ saveRef.current(value);
2395
+ }
2396
+ }, [value]);
2397
+ }
2398
+
2399
+ // src/utils/format.ts
2400
+ function formatUsdRate(rate) {
2401
+ if (rate >= 1) {
2402
+ return rate.toLocaleString(void 0, {
2403
+ minimumFractionDigits: 2,
2404
+ maximumFractionDigits: 2
2405
+ });
2406
+ } else if (rate >= 0.01) {
2407
+ return rate.toLocaleString(void 0, {
2408
+ minimumFractionDigits: 2,
2409
+ maximumFractionDigits: 4
2410
+ });
2411
+ } else if (rate >= 1e-4) {
2412
+ return rate.toLocaleString(void 0, {
2413
+ minimumFractionDigits: 4,
2414
+ maximumFractionDigits: 6
2415
+ });
2416
+ } else {
2417
+ return rate.toLocaleString(void 0, {
2418
+ minimumFractionDigits: 6,
2419
+ maximumFractionDigits: 8
2420
+ });
2421
+ }
2422
+ }
2423
+ function formatCryptoAmount(amount, isStablecoin) {
2424
+ const decimals = isStablecoin ? 2 : 8;
2425
+ return amount.toFixed(decimals);
2426
+ }
2427
+ function formatUsdAmount(amount, alwaysShowCents = false) {
2428
+ if (alwaysShowCents) {
2429
+ return amount.toFixed(2);
2430
+ }
2431
+ const hasCents = amount % 1 !== 0;
2432
+ if (hasCents) {
2433
+ return amount.toFixed(2);
2434
+ }
2435
+ return amount.toFixed(0);
2436
+ }
2437
+
2438
+ // src/utils/errors.ts
2439
+ function extractErrorMessage(err, fallback = "An error occurred") {
2440
+ if (!err) return fallback;
2441
+ const error = err;
2442
+ const responseData = error.response?.data;
2443
+ const response = responseData || error.response;
2444
+ if (response) {
2445
+ if (typeof response.error === "string") return response.error;
2446
+ if (typeof response.message === "string") return response.message;
2447
+ if (typeof response.detail === "string") return response.detail;
2448
+ }
2449
+ if (typeof error.errorMessage === "string") return error.errorMessage;
2450
+ if (typeof error.message === "string") return error.message;
2451
+ return fallback;
2452
+ }
2453
+ var isDevelopment = process.env.NODE_ENV === "development";
2454
+ consola.createConsola({
2455
+ level: isDevelopment ? 4 : 1
2456
+ }).withTag("ext-payments");
2457
+ function CurrencyCombobox({
2458
+ options,
2459
+ value,
2460
+ onChange,
2461
+ disabled,
2462
+ placeholder = "Select currency..."
2463
+ }) {
2464
+ return /* @__PURE__ */ jsxRuntime.jsx(
2465
+ uiCore.Combobox,
2466
+ {
2467
+ options,
2468
+ value,
2469
+ onValueChange: onChange,
2470
+ placeholder,
2471
+ searchPlaceholder: "Search...",
2472
+ disabled,
2473
+ className: "h-14",
2474
+ renderOption: (option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 flex-1", children: [
2475
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: option.value, size: 24 }),
2476
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: option.label })
2477
+ ] }),
2478
+ renderValue: (option) => option && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2479
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: option.value, size: 24 }),
2480
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: option.label })
2481
+ ] })
2482
+ }
2483
+ );
2484
+ }
1956
2485
  var AddFundsSchema = zod.z.object({
1957
- amount: zod.z.number().min(1, "Minimum $1.00"),
2486
+ amount: zod.z.number().min(1, "Minimum $1"),
1958
2487
  currency: zod.z.string().min(1, "Select a currency")
1959
2488
  });
2489
+ var STORAGE_KEY = "payments:addFunds";
1960
2490
  function AddFundsSheet({ open, onOpenChange, onSuccess }) {
1961
2491
  const { currencies, isLoadingCurrencies, addFunds } = useWallet();
1962
- const [isSubmitting, setIsSubmitting] = React2.useState(false);
1963
- const [error, setError] = React2.useState(null);
2492
+ const [isSubmitting, setIsSubmitting] = react.useState(false);
2493
+ const [error, setError] = react.useState(null);
2494
+ const [saved, setSaved] = uiCore.useLocalStorage(STORAGE_KEY, {
2495
+ currency: "",
2496
+ amount: 100
2497
+ });
1964
2498
  const form = reactHookForm.useForm({
1965
2499
  resolver: zod$1.zodResolver(AddFundsSchema),
1966
2500
  defaultValues: {
1967
- amount: 100,
1968
- currency: ""
2501
+ amount: saved.amount,
2502
+ currency: saved.currency
1969
2503
  }
1970
2504
  });
1971
- const currencyOptions = React2.useMemo(() => {
1972
- return currencies.map((c) => ({
1973
- value: c.code,
1974
- label: c.network ? `${c.code} (${c.network})` : c.code,
1975
- rate: c.rate,
1976
- network: c.network
1977
- }));
1978
- }, [currencies]);
1979
- React2.useMemo(() => {
1980
- if (currencyOptions.length > 0 && !form.getValues("currency")) {
1981
- const usdt = currencyOptions.find((c) => c.value.includes("USDT"));
1982
- form.setValue("currency", usdt?.value || currencyOptions[0].value);
1983
- }
1984
- }, [currencyOptions, form]);
1985
- const selectedCurrency = currencyOptions.find((c) => c.value === form.watch("currency"));
1986
- const cryptoAmount = React2.useMemo(() => {
1987
- const amount = form.watch("amount");
1988
- if (!selectedCurrency?.rate || !amount) return null;
1989
- return amount / selectedCurrency.rate;
1990
- }, [form.watch("amount"), selectedCurrency]);
1991
- const handleSubmit = React2.useCallback(async (data) => {
2505
+ const watchedAmount = form.watch("amount");
2506
+ const watchedCurrency = form.watch("currency");
2507
+ useAutoSave(watchedAmount, (v) => setSaved((prev) => ({ ...prev, amount: v })), (v) => v > 0);
2508
+ useAutoSave(watchedCurrency, (v) => setSaved((prev) => ({ ...prev, currency: v })));
2509
+ const currencyOptions = useCurrencyOptions(currencies);
2510
+ useDefaultCurrency({
2511
+ currencyOptions,
2512
+ savedCurrency: saved.currency,
2513
+ currentValue: watchedCurrency,
2514
+ setValue: (v) => form.setValue("currency", v)
2515
+ });
2516
+ const { estimate, isLoading: isLoadingEstimate } = useEstimate({
2517
+ currencyCode: watchedCurrency,
2518
+ amountUsd: watchedAmount,
2519
+ minAmount: 1
2520
+ });
2521
+ const selectedCurrency = currencyOptions.find((c) => c.value === watchedCurrency);
2522
+ const displayData = react.useMemo(() => {
2523
+ if (!selectedCurrency || !estimate) return null;
2524
+ const token = selectedCurrency.token;
2525
+ const cryptoAmount = formatCryptoAmount(estimate.estimatedAmount, estimate.isStablecoin);
2526
+ const rate = formatUsdRate(estimate.usdRate);
2527
+ const belowMinimum = estimate.minAmountUsd ? watchedAmount < estimate.minAmountUsd : false;
2528
+ const minAmount = estimate.minAmountUsd ? formatUsdAmount(estimate.minAmountUsd) : void 0;
2529
+ return {
2530
+ token,
2531
+ cryptoAmount,
2532
+ rate,
2533
+ showRate: !estimate.isStablecoin && estimate.usdRate > 0,
2534
+ belowMinimum,
2535
+ minAmount,
2536
+ // Fee breakdown from API
2537
+ amountToReceive: estimate.amountToReceive,
2538
+ serviceFeeUsd: estimate.serviceFeeUsd,
2539
+ serviceFeePercent: estimate.serviceFeePercent,
2540
+ totalToPayUsd: estimate.totalToPayUsd
2541
+ };
2542
+ }, [selectedCurrency, estimate, watchedAmount]);
2543
+ const handleSubmit = react.useCallback(async (data) => {
1992
2544
  try {
1993
2545
  setIsSubmitting(true);
1994
2546
  setError(null);
@@ -2000,19 +2552,22 @@ function AddFundsSheet({ open, onOpenChange, onSuccess }) {
2000
2552
  onOpenChange(false);
2001
2553
  onSuccess?.(result);
2002
2554
  } catch (err) {
2003
- const message = err?.response?.data?.message || err?.response?.data?.detail || err?.message || "Failed to create payment";
2004
- setError(message);
2555
+ setError(extractErrorMessage(err, "Failed to create payment"));
2005
2556
  } finally {
2006
2557
  setIsSubmitting(false);
2007
2558
  }
2008
2559
  }, [addFunds, form, onOpenChange, onSuccess]);
2009
- const handleOpenChange = React2.useCallback((open2) => {
2010
- if (!open2) {
2560
+ const handleOpenChange = react.useCallback((open2) => {
2561
+ if (open2) {
2562
+ form.reset({
2563
+ amount: saved.amount,
2564
+ currency: saved.currency
2565
+ });
2566
+ } else {
2011
2567
  setError(null);
2012
- form.reset();
2013
2568
  }
2014
2569
  onOpenChange(open2);
2015
- }, [form, onOpenChange]);
2570
+ }, [form, onOpenChange, saved]);
2016
2571
  return /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheet, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetContent, { className: "sm:max-w-md", children: [
2017
2572
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetHeader, { children: [
2018
2573
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetTitle, { children: "Add Funds" }),
@@ -2034,7 +2589,7 @@ function AddFundsSheet({ open, onOpenChange, onSuccess }) {
2034
2589
  type: "number",
2035
2590
  step: "0.01",
2036
2591
  min: "1",
2037
- placeholder: "100.00",
2592
+ placeholder: "100",
2038
2593
  className: "pl-8 text-2xl h-14 font-semibold",
2039
2594
  ...field,
2040
2595
  onChange: (e) => field.onChange(parseFloat(e.target.value) || 0)
@@ -2053,51 +2608,78 @@ function AddFundsSheet({ open, onOpenChange, onSuccess }) {
2053
2608
  render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(uiCore.FormItem, { children: [
2054
2609
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormLabel, { children: "Pay with" }),
2055
2610
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsx(
2056
- uiCore.Combobox,
2611
+ CurrencyCombobox,
2057
2612
  {
2058
2613
  options: currencyOptions,
2059
2614
  value: field.value,
2060
- onValueChange: field.onChange,
2061
- placeholder: "Select currency...",
2062
- searchPlaceholder: "Search...",
2063
- disabled: isLoadingCurrencies,
2064
- className: "h-14",
2065
- renderOption: (option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 flex-1", children: [
2066
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: option.value, size: 24 }),
2067
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: option.label })
2068
- ] }),
2069
- renderValue: (option) => option && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2070
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: option.value, size: 24 }),
2071
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: option.label })
2072
- ] })
2615
+ onChange: field.onChange,
2616
+ disabled: isLoadingCurrencies
2073
2617
  }
2074
2618
  ) }),
2075
2619
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormMessage, {})
2076
2620
  ] })
2077
2621
  }
2078
2622
  ),
2079
- cryptoAmount !== null && selectedCurrency && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-muted rounded-xl p-4 space-y-2", children: [
2080
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2081
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "You will send" }),
2082
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2083
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: selectedCurrency.value, size: 20 }),
2084
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono font-semibold", children: [
2085
- cryptoAmount.toFixed(8),
2086
- " ",
2087
- selectedCurrency.value
2088
- ] })
2623
+ selectedCurrency && watchedAmount >= 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-muted rounded-xl p-4 space-y-2", children: isLoadingEstimate ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center py-2", children: [
2624
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-5 w-5 animate-spin text-muted-foreground" }),
2625
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 text-sm text-muted-foreground", children: "Getting rate..." })
2626
+ ] }) : displayData ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2627
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
2628
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Amount" }),
2629
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2630
+ "$",
2631
+ formatUsdAmount(displayData.amountToReceive)
2089
2632
  ] })
2090
2633
  ] }),
2091
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm text-muted-foreground", children: [
2634
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
2635
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
2636
+ "Service fee (",
2637
+ displayData.serviceFeePercent,
2638
+ "%)"
2639
+ ] }),
2640
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2641
+ "+$",
2642
+ formatUsdAmount(displayData.serviceFeeUsd)
2643
+ ] })
2644
+ ] }),
2645
+ displayData.showRate && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm text-muted-foreground", children: [
2092
2646
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Rate" }),
2093
2647
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2094
2648
  "1 ",
2095
- selectedCurrency.value,
2649
+ displayData.token,
2096
2650
  " = $",
2097
- selectedCurrency.rate.toFixed(2)
2651
+ displayData.rate
2652
+ ] })
2653
+ ] }),
2654
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t pt-2 mt-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2655
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "You will send" }),
2656
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
2657
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 justify-end", children: [
2658
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: displayData.token, size: 20 }),
2659
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono font-semibold text-lg", children: [
2660
+ displayData.cryptoAmount,
2661
+ " ",
2662
+ displayData.token
2663
+ ] })
2664
+ ] }),
2665
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-muted-foreground", children: [
2666
+ "~$",
2667
+ formatUsdAmount(displayData.totalToPayUsd)
2668
+ ] })
2098
2669
  ] })
2670
+ ] }) }),
2671
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm pt-2", children: [
2672
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "You will receive" }),
2673
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
2674
+ "$",
2675
+ formatUsdAmount(displayData.amountToReceive)
2676
+ ] })
2677
+ ] }),
2678
+ displayData.belowMinimum && displayData.minAmount && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-destructive mt-2 pt-2 border-t border-destructive/20", children: [
2679
+ "Minimum amount: $",
2680
+ displayData.minAmount
2099
2681
  ] })
2100
- ] }),
2682
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center text-sm text-muted-foreground py-2", children: "Enter amount to see conversion" }) }),
2101
2683
  error && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Alert, { variant: "destructive", children: /* @__PURE__ */ jsxRuntime.jsx(uiCore.AlertDescription, { children: error }) }),
2102
2684
  /* @__PURE__ */ jsxRuntime.jsx(
2103
2685
  uiCore.Button,
@@ -2105,7 +2687,7 @@ function AddFundsSheet({ open, onOpenChange, onSuccess }) {
2105
2687
  type: "submit",
2106
2688
  size: "lg",
2107
2689
  className: "w-full h-14 text-lg rounded-xl",
2108
- disabled: isSubmitting || currencyOptions.length === 0,
2690
+ disabled: isSubmitting || currencyOptions.length === 0 || isLoadingEstimate || displayData?.belowMinimum,
2109
2691
  children: isSubmitting ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2110
2692
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "h-5 w-5 mr-2 animate-spin" }),
2111
2693
  "Creating..."
@@ -2116,56 +2698,56 @@ function AddFundsSheet({ open, onOpenChange, onSuccess }) {
2116
2698
  ] }) });
2117
2699
  }
2118
2700
  var WithdrawSchema = zod.z.object({
2119
- amount: zod.z.number().min(10, "Minimum $10.00"),
2701
+ amount: zod.z.number().min(10, "Minimum $10"),
2120
2702
  currency: zod.z.string().min(1, "Select a currency"),
2121
2703
  wallet_address: zod.z.string().min(26, "Invalid wallet address")
2122
2704
  });
2123
- var SERVICE_FEE_PERCENT = 0.01;
2124
- var NETWORK_FEE_USD = 1;
2705
+ var STORAGE_KEY2 = "payments:withdraw";
2125
2706
  function WithdrawSheet({ open, onOpenChange, onSuccess }) {
2126
2707
  const { currencies, isLoadingCurrencies, withdraw, balanceAmount } = useWallet();
2127
- const [isSubmitting, setIsSubmitting] = React2.useState(false);
2128
- const [error, setError] = React2.useState(null);
2708
+ const [isSubmitting, setIsSubmitting] = react.useState(false);
2709
+ const [error, setError] = react.useState(null);
2710
+ const [saved, setSaved] = uiCore.useLocalStorage(STORAGE_KEY2, {
2711
+ currency: "",
2712
+ wallet: ""
2713
+ });
2129
2714
  const form = reactHookForm.useForm({
2130
2715
  resolver: zod$1.zodResolver(WithdrawSchema),
2131
2716
  defaultValues: {
2132
2717
  amount: 10,
2133
- currency: "",
2134
- wallet_address: ""
2718
+ currency: saved.currency,
2719
+ wallet_address: saved.wallet
2135
2720
  }
2136
2721
  });
2137
- const currencyOptions = React2.useMemo(() => {
2138
- return currencies.map((c) => ({
2139
- value: c.code,
2140
- label: c.network ? `${c.code} (${c.network})` : c.code,
2141
- rate: c.rate,
2142
- network: c.network
2143
- }));
2144
- }, [currencies]);
2145
- React2.useMemo(() => {
2146
- if (currencyOptions.length > 0 && !form.getValues("currency")) {
2147
- const usdt = currencyOptions.find((c) => c.value.includes("USDT"));
2148
- form.setValue("currency", usdt?.value || currencyOptions[0].value);
2149
- }
2150
- }, [currencyOptions, form]);
2151
- const selectedCurrency = currencyOptions.find((c) => c.value === form.watch("currency"));
2152
- const amount = form.watch("amount") || 0;
2153
- const feeBreakdown = React2.useMemo(() => {
2154
- const serviceFee = amount * SERVICE_FEE_PERCENT;
2155
- const networkFee = NETWORK_FEE_USD;
2156
- const totalFee = serviceFee + networkFee;
2157
- const finalAmount = Math.max(0, amount - totalFee);
2158
- const cryptoAmount = selectedCurrency?.rate ? finalAmount / selectedCurrency.rate : null;
2722
+ const watchedAmount = form.watch("amount");
2723
+ const watchedCurrency = form.watch("currency");
2724
+ const watchedWallet = form.watch("wallet_address");
2725
+ useAutoSave(watchedCurrency, (v) => setSaved((prev) => ({ ...prev, currency: v })));
2726
+ useAutoSave(watchedWallet, (v) => setSaved((prev) => ({ ...prev, wallet: v })), (v) => v.length >= 26);
2727
+ const currencyOptions = useCurrencyOptions(currencies);
2728
+ useDefaultCurrency({
2729
+ currencyOptions,
2730
+ savedCurrency: saved.currency,
2731
+ currentValue: watchedCurrency,
2732
+ setValue: (v) => form.setValue("currency", v)
2733
+ });
2734
+ const selectedCurrency = currencyOptions.find((c) => c.value === watchedCurrency);
2735
+ const amount = watchedAmount || 0;
2736
+ const { estimate, isLoading: isLoadingEstimate } = useWithdrawalEstimate({
2737
+ currencyCode: watchedCurrency,
2738
+ amountUsd: amount,
2739
+ minAmount: 10,
2740
+ skip: amount < 10
2741
+ });
2742
+ const cryptoDisplay = react.useMemo(() => {
2743
+ if (!selectedCurrency || !estimate) return null;
2159
2744
  return {
2160
- serviceFee,
2161
- networkFee,
2162
- totalFee,
2163
- finalAmount,
2164
- cryptoAmount
2745
+ token: selectedCurrency.token,
2746
+ cryptoAmount: formatCryptoAmount(estimate.estimatedAmount, estimate.isStablecoin)
2165
2747
  };
2166
- }, [amount, selectedCurrency]);
2748
+ }, [selectedCurrency, estimate]);
2167
2749
  const insufficientBalance = amount > balanceAmount;
2168
- const handleSubmit = React2.useCallback(async (data) => {
2750
+ const handleSubmit = react.useCallback(async (data) => {
2169
2751
  try {
2170
2752
  setIsSubmitting(true);
2171
2753
  setError(null);
@@ -2178,19 +2760,23 @@ function WithdrawSheet({ open, onOpenChange, onSuccess }) {
2178
2760
  onOpenChange(false);
2179
2761
  onSuccess?.(result);
2180
2762
  } catch (err) {
2181
- const message = err?.response?.data?.error || err?.response?.data?.message || err?.response?.data?.detail || err?.message || "Failed to create withdrawal request";
2182
- setError(message);
2763
+ setError(extractErrorMessage(err, "Failed to create withdrawal request"));
2183
2764
  } finally {
2184
2765
  setIsSubmitting(false);
2185
2766
  }
2186
2767
  }, [withdraw, form, onOpenChange, onSuccess]);
2187
- const handleOpenChange = React2.useCallback((open2) => {
2188
- if (!open2) {
2768
+ const handleOpenChange = react.useCallback((open2) => {
2769
+ if (open2) {
2770
+ form.reset({
2771
+ amount: 10,
2772
+ currency: saved.currency,
2773
+ wallet_address: saved.wallet
2774
+ });
2775
+ } else {
2189
2776
  setError(null);
2190
- form.reset();
2191
2777
  }
2192
2778
  onOpenChange(open2);
2193
- }, [form, onOpenChange]);
2779
+ }, [form, onOpenChange, saved]);
2194
2780
  return /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheet, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetContent, { className: "sm:max-w-md", children: [
2195
2781
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetHeader, { children: [
2196
2782
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetTitle, { children: "Withdraw" }),
@@ -2212,7 +2798,7 @@ function WithdrawSheet({ open, onOpenChange, onSuccess }) {
2212
2798
  type: "number",
2213
2799
  step: "0.01",
2214
2800
  min: "10",
2215
- placeholder: "10.00",
2801
+ placeholder: "10",
2216
2802
  className: "pl-8 text-2xl h-14 font-semibold",
2217
2803
  ...field,
2218
2804
  onChange: (e) => field.onChange(parseFloat(e.target.value) || 0)
@@ -2222,7 +2808,7 @@ function WithdrawSheet({ open, onOpenChange, onSuccess }) {
2222
2808
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormMessage, {}),
2223
2809
  insufficientBalance && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-destructive mt-1", children: [
2224
2810
  "Insufficient balance (Available: $",
2225
- balanceAmount.toFixed(2),
2811
+ formatUsdAmount(balanceAmount),
2226
2812
  ")"
2227
2813
  ] })
2228
2814
  ] })
@@ -2236,23 +2822,12 @@ function WithdrawSheet({ open, onOpenChange, onSuccess }) {
2236
2822
  render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(uiCore.FormItem, { children: [
2237
2823
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormLabel, { children: "Withdraw as" }),
2238
2824
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsx(
2239
- uiCore.Combobox,
2825
+ CurrencyCombobox,
2240
2826
  {
2241
2827
  options: currencyOptions,
2242
2828
  value: field.value,
2243
- onValueChange: field.onChange,
2244
- placeholder: "Select currency...",
2245
- searchPlaceholder: "Search...",
2246
- disabled: isLoadingCurrencies,
2247
- className: "h-14",
2248
- renderOption: (option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 flex-1", children: [
2249
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: option.value, size: 24 }),
2250
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: option.label })
2251
- ] }),
2252
- renderValue: (option) => option && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2253
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: option.value, size: 24 }),
2254
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: option.label })
2255
- ] })
2829
+ onChange: field.onChange,
2830
+ disabled: isLoadingCurrencies
2256
2831
  }
2257
2832
  ) }),
2258
2833
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormMessage, {})
@@ -2283,36 +2858,49 @@ function WithdrawSheet({ open, onOpenChange, onSuccess }) {
2283
2858
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Amount" }),
2284
2859
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2285
2860
  "$",
2286
- amount.toFixed(2)
2861
+ formatUsdAmount(amount)
2287
2862
  ] })
2288
2863
  ] }),
2289
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
2290
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Service fee (1%)" }),
2291
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-destructive", children: [
2292
- "-$",
2293
- feeBreakdown.serviceFee.toFixed(2)
2294
- ] })
2295
- ] }),
2296
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
2297
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Network fee" }),
2298
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-destructive", children: [
2299
- "-$",
2300
- feeBreakdown.networkFee.toFixed(2)
2301
- ] })
2302
- ] }),
2303
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t pt-2 mt-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2304
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "You will receive" }),
2305
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
2306
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-semibold", children: [
2307
- "$",
2308
- feeBreakdown.finalAmount.toFixed(2)
2864
+ isLoadingEstimate ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground py-2", children: [
2865
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
2866
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Calculating fees..." })
2867
+ ] }) : estimate ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2868
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
2869
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
2870
+ "Service fee (",
2871
+ estimate.serviceFeePercent,
2872
+ "%)"
2309
2873
  ] }),
2310
- feeBreakdown.cryptoAmount !== null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
2311
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: selectedCurrency.value, size: 16 }),
2312
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: feeBreakdown.cryptoAmount.toFixed(8) })
2874
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-destructive", children: [
2875
+ "-$",
2876
+ formatUsdAmount(estimate.serviceFeeUsd)
2313
2877
  ] })
2314
- ] })
2315
- ] }) })
2878
+ ] }),
2879
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
2880
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Network fee" }),
2881
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-destructive", children: [
2882
+ "-$",
2883
+ formatUsdAmount(estimate.networkFeeUsd)
2884
+ ] })
2885
+ ] }),
2886
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t pt-2 mt-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2887
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "You will receive" }),
2888
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
2889
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-semibold", children: [
2890
+ "$",
2891
+ formatUsdAmount(estimate.amountToReceive)
2892
+ ] }),
2893
+ cryptoDisplay && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
2894
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: cryptoDisplay.token, size: 16 }),
2895
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
2896
+ cryptoDisplay.cryptoAmount,
2897
+ " ",
2898
+ cryptoDisplay.token
2899
+ ] })
2900
+ ] })
2901
+ ] })
2902
+ ] }) })
2903
+ ] }) : null
2316
2904
  ] }),
2317
2905
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.Alert, { children: [
2318
2906
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-4 w-4" }),
@@ -2325,7 +2913,7 @@ function WithdrawSheet({ open, onOpenChange, onSuccess }) {
2325
2913
  type: "submit",
2326
2914
  size: "lg",
2327
2915
  className: "w-full h-14 text-lg rounded-xl",
2328
- disabled: isSubmitting || currencyOptions.length === 0 || insufficientBalance || feeBreakdown.finalAmount <= 0,
2916
+ disabled: isSubmitting || currencyOptions.length === 0 || insufficientBalance || !estimate || estimate.amountToReceive <= 0 || isLoadingEstimate,
2329
2917
  children: isSubmitting ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2330
2918
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "h-5 w-5 mr-2 animate-spin" }),
2331
2919
  "Submitting..."
@@ -2336,6 +2924,229 @@ function WithdrawSheet({ open, onOpenChange, onSuccess }) {
2336
2924
  ] }) });
2337
2925
  }
2338
2926
  var statusConfig2 = {
2927
+ pending: {
2928
+ icon: lucideReact.Clock,
2929
+ color: "text-yellow-500",
2930
+ bg: "bg-yellow-500/10",
2931
+ label: "Pending Approval"
2932
+ },
2933
+ approved: {
2934
+ icon: lucideReact.CheckCircle2,
2935
+ color: "text-blue-500",
2936
+ bg: "bg-blue-500/10",
2937
+ label: "Approved"
2938
+ },
2939
+ processing: {
2940
+ icon: lucideReact.RefreshCw,
2941
+ color: "text-blue-500",
2942
+ bg: "bg-blue-500/10",
2943
+ label: "Processing",
2944
+ animate: true
2945
+ },
2946
+ completed: {
2947
+ icon: lucideReact.CheckCircle2,
2948
+ color: "text-green-500",
2949
+ bg: "bg-green-500/10",
2950
+ label: "Completed"
2951
+ },
2952
+ rejected: {
2953
+ icon: lucideReact.XCircle,
2954
+ color: "text-red-500",
2955
+ bg: "bg-red-500/10",
2956
+ label: "Rejected"
2957
+ },
2958
+ cancelled: {
2959
+ icon: lucideReact.Ban,
2960
+ color: "text-muted-foreground",
2961
+ bg: "bg-muted",
2962
+ label: "Cancelled"
2963
+ }
2964
+ };
2965
+ function WithdrawalSheet({ withdrawalId, open, onOpenChange }) {
2966
+ const { getWithdrawalDetails, cancelWithdrawal, refreshWallet } = useWallet();
2967
+ const { data: withdrawal, isLoading, error, mutate } = useSWR__default.default(
2968
+ open && withdrawalId ? ["withdrawal-details", withdrawalId] : null,
2969
+ () => getWithdrawalDetails(withdrawalId),
2970
+ { refreshInterval: 3e4 }
2971
+ );
2972
+ const displayData = react.useMemo(() => {
2973
+ const s = withdrawal?.status?.toLowerCase() || "pending";
2974
+ const config2 = statusConfig2[s] || statusConfig2.pending;
2975
+ const isPending = s === "pending";
2976
+ const isCompleted = s === "completed";
2977
+ const isRejected = s === "rejected";
2978
+ const isCancelled = s === "cancelled";
2979
+ const isProcessing = s === "processing" || s === "approved";
2980
+ const canCancel2 = isPending;
2981
+ let description2 = "";
2982
+ if (isPending) description2 = "Waiting for admin approval";
2983
+ else if (isProcessing) description2 = "Your withdrawal is being processed";
2984
+ else if (isCompleted) description2 = "Withdrawal completed successfully";
2985
+ else if (isRejected) description2 = "Withdrawal was rejected";
2986
+ else if (isCancelled) description2 = "Withdrawal was cancelled";
2987
+ const amountUsd2 = withdrawal?.amount_usd ? `$${parseFloat(withdrawal.amount_usd).toFixed(2)}` : "";
2988
+ const finalAmountUsd2 = withdrawal?.final_amount_usd ? `$${parseFloat(withdrawal.final_amount_usd).toFixed(2)}` : "";
2989
+ const totalFeeUsd2 = withdrawal?.total_fee_usd ? `$${parseFloat(withdrawal.total_fee_usd).toFixed(2)}` : "";
2990
+ const createdAt2 = withdrawal?.created_at ? moment3__default.default.utc(withdrawal.created_at).local().format("MMM D, YYYY HH:mm") : "";
2991
+ const completedAt2 = withdrawal?.completed_at ? moment3__default.default.utc(withdrawal.completed_at).local().format("MMM D, YYYY HH:mm") : null;
2992
+ return {
2993
+ status: s,
2994
+ config: config2,
2995
+ isPending,
2996
+ isCompleted,
2997
+ isRejected,
2998
+ isCancelled,
2999
+ isProcessing,
3000
+ canCancel: canCancel2,
3001
+ description: description2,
3002
+ amountUsd: amountUsd2,
3003
+ finalAmountUsd: finalAmountUsd2,
3004
+ totalFeeUsd: totalFeeUsd2,
3005
+ createdAt: createdAt2,
3006
+ completedAt: completedAt2
3007
+ };
3008
+ }, [withdrawal]);
3009
+ const { config, canCancel, description, amountUsd, finalAmountUsd, totalFeeUsd, createdAt, completedAt } = displayData;
3010
+ const StatusIcon = config.icon;
3011
+ const handleCancel = async () => {
3012
+ if (!withdrawalId) return;
3013
+ try {
3014
+ await cancelWithdrawal(withdrawalId);
3015
+ await mutate();
3016
+ await refreshWallet();
3017
+ } catch (err) {
3018
+ console.error("Failed to cancel withdrawal:", err);
3019
+ }
3020
+ };
3021
+ return /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheet, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetContent, { className: "sm:max-w-lg", children: [
3022
+ /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetHeader, { children: [
3023
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetTitle, { children: "Withdrawal Details" }),
3024
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetDescription, { children: description })
3025
+ ] }),
3026
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 sm:p-0 sm:mt-4 overflow-y-auto max-h-[70vh]", children: [
3027
+ isLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
3028
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-16 w-full rounded-xl" }),
3029
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-24 w-full rounded-xl" }),
3030
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-20 w-full rounded-xl" })
3031
+ ] }),
3032
+ error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-12", children: [
3033
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-12 w-12 text-destructive mb-4" }),
3034
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4", children: "Failed to load withdrawal" }),
3035
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Button, { onClick: () => mutate(), children: "Try Again" })
3036
+ ] }),
3037
+ withdrawal && !isLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
3038
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex items-center gap-3 p-4 rounded-xl", config.bg), children: [
3039
+ /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { className: lib.cn("h-6 w-6", config.color, config.animate && "animate-spin") }),
3040
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
3041
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold", children: config.label }),
3042
+ withdrawal.admin_notes && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-muted-foreground", children: withdrawal.admin_notes })
3043
+ ] })
3044
+ ] }),
3045
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-muted rounded-xl p-4 space-y-3", children: [
3046
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
3047
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Amount" }),
3048
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: amountUsd })
3049
+ ] }),
3050
+ totalFeeUsd && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
3051
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Total fees" }),
3052
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-destructive", children: [
3053
+ "-",
3054
+ totalFeeUsd
3055
+ ] })
3056
+ ] }),
3057
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pt-2 border-t", children: [
3058
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "You receive" }),
3059
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3060
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.TokenIcon, { symbol: withdrawal.currency_code, size: 24 }),
3061
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono font-bold text-lg", children: finalAmountUsd })
3062
+ ] })
3063
+ ] }),
3064
+ withdrawal.crypto_amount && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
3065
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Crypto amount" }),
3066
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
3067
+ withdrawal.crypto_amount,
3068
+ " ",
3069
+ withdrawal.currency_token
3070
+ ] })
3071
+ ] })
3072
+ ] }),
3073
+ withdrawal.wallet_address && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3074
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Destination Wallet" }),
3075
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3076
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 p-3 bg-muted rounded-xl font-mono text-sm break-all", children: withdrawal.wallet_address }),
3077
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.CopyButton, { value: withdrawal.wallet_address, variant: "outline", className: "shrink-0" })
3078
+ ] })
3079
+ ] }),
3080
+ withdrawal.transaction_hash && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3081
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Transaction Hash" }),
3082
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3083
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 p-3 bg-muted rounded-xl font-mono text-sm break-all", children: withdrawal.transaction_hash }),
3084
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.CopyButton, { value: withdrawal.transaction_hash, variant: "outline", className: "shrink-0" })
3085
+ ] })
3086
+ ] }),
3087
+ withdrawal.explorer_link && /* @__PURE__ */ jsxRuntime.jsxs(
3088
+ uiCore.Button,
3089
+ {
3090
+ variant: "outline",
3091
+ className: "w-full",
3092
+ onClick: () => window.open(withdrawal.explorer_link, "_blank"),
3093
+ children: [
3094
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-4 w-4 mr-2" }),
3095
+ "View on Explorer"
3096
+ ]
3097
+ }
3098
+ ),
3099
+ canCancel && /* @__PURE__ */ jsxRuntime.jsxs(
3100
+ uiCore.Button,
3101
+ {
3102
+ variant: "destructive",
3103
+ className: "w-full",
3104
+ onClick: handleCancel,
3105
+ children: [
3106
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ban, { className: "h-4 w-4 mr-2" }),
3107
+ "Cancel Withdrawal"
3108
+ ]
3109
+ }
3110
+ ),
3111
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 text-xs text-muted-foreground pt-4 border-t", children: [
3112
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
3113
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Withdrawal ID" }),
3114
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: withdrawal.id })
3115
+ ] }),
3116
+ withdrawal.internal_withdrawal_id && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
3117
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Reference #" }),
3118
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: withdrawal.internal_withdrawal_id })
3119
+ ] }),
3120
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
3121
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Created" }),
3122
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: createdAt })
3123
+ ] }),
3124
+ completedAt && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
3125
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Completed" }),
3126
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: completedAt })
3127
+ ] }),
3128
+ withdrawal.currency_network && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
3129
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Network" }),
3130
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: withdrawal.currency_network })
3131
+ ] })
3132
+ ] }),
3133
+ /* @__PURE__ */ jsxRuntime.jsxs(
3134
+ uiCore.Button,
3135
+ {
3136
+ variant: "ghost",
3137
+ className: "w-full",
3138
+ onClick: () => mutate(),
3139
+ children: [
3140
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "h-4 w-4 mr-2" }),
3141
+ "Refresh Status"
3142
+ ]
3143
+ }
3144
+ )
3145
+ ] })
3146
+ ] })
3147
+ ] }) });
3148
+ }
3149
+ var statusConfig3 = {
2339
3150
  pending: {
2340
3151
  icon: lucideReact.Clock,
2341
3152
  color: "text-yellow-500",
@@ -2368,25 +3179,25 @@ var statusConfig2 = {
2368
3179
  label: "Expired"
2369
3180
  }
2370
3181
  };
2371
- function PaymentSheet({ paymentId, open, onOpenChange }) {
3182
+ function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }) {
2372
3183
  const { getPaymentDetails } = useWallet();
2373
- const [timeLeft, setTimeLeft] = React2.useState("");
2374
- const { data: payment, isLoading, error, mutate } = useSWR2__default.default(
3184
+ const [timeLeft, setTimeLeft] = react.useState("");
3185
+ const { data: payment, isLoading, error, mutate } = useSWR__default.default(
2375
3186
  open && paymentId ? ["payment-details", paymentId] : null,
2376
3187
  () => getPaymentDetails(paymentId),
2377
3188
  { refreshInterval: 1e4 }
2378
3189
  );
2379
- React2.useEffect(() => {
3190
+ react.useEffect(() => {
2380
3191
  if (!payment?.expires_at) return;
2381
3192
  const updateTimeLeft = () => {
2382
- const now = moment2__default.default();
2383
- const expires = moment2__default.default.utc(payment.expires_at);
3193
+ const now = moment3__default.default();
3194
+ const expires = moment3__default.default.utc(payment.expires_at);
2384
3195
  const diff = expires.diff(now);
2385
3196
  if (diff <= 0) {
2386
3197
  setTimeLeft("Expired");
2387
3198
  return;
2388
3199
  }
2389
- const duration = moment2__default.default.duration(diff);
3200
+ const duration = moment3__default.default.duration(diff);
2390
3201
  const hours = Math.floor(duration.asHours());
2391
3202
  const minutes = duration.minutes();
2392
3203
  const seconds = duration.seconds();
@@ -2396,22 +3207,59 @@ function PaymentSheet({ paymentId, open, onOpenChange }) {
2396
3207
  const interval = setInterval(updateTimeLeft, 1e3);
2397
3208
  return () => clearInterval(interval);
2398
3209
  }, [payment?.expires_at]);
2399
- const status = React2.useMemo(() => {
3210
+ const displayData = react.useMemo(() => {
2400
3211
  const s = payment?.status?.toLowerCase();
2401
- if (s === "completed" || s === "success" || s === "finished") return "completed";
2402
- if (s === "confirming" || s === "partially_paid") return "confirming";
2403
- if (s === "expired") return "expired";
2404
- if (s === "failed" || s === "error" || s === "cancelled") return "failed";
2405
- return "pending";
2406
- }, [payment?.status]);
2407
- const config = statusConfig2[status];
3212
+ let status;
3213
+ if (s === "completed" || s === "success" || s === "finished") status = "completed";
3214
+ else if (s === "confirming" || s === "partially_paid") status = "confirming";
3215
+ else if (s === "expired") status = "expired";
3216
+ else if (s === "failed" || s === "error" || s === "cancelled") status = "failed";
3217
+ else status = "pending";
3218
+ const config2 = statusConfig3[status];
3219
+ const isPending = status === "pending";
3220
+ const isExpired2 = status === "expired" || timeLeft === "Expired";
3221
+ const isCompleted = status === "completed";
3222
+ const isFailed = status === "failed";
3223
+ const isConfirming = status === "confirming";
3224
+ const canPay2 = isPending && !isExpired2;
3225
+ let description2 = "";
3226
+ if (canPay2) description2 = "Send cryptocurrency to complete payment";
3227
+ else if (isExpired2) description2 = "This payment has expired";
3228
+ else if (isCompleted) description2 = "Payment completed successfully";
3229
+ else if (isFailed) description2 = "Payment failed";
3230
+ else if (isConfirming) description2 = "Confirming your payment";
3231
+ const statusBadge2 = {
3232
+ bg: isExpired2 ? "bg-muted" : config2.bg,
3233
+ iconColor: isExpired2 ? "text-muted-foreground" : config2.color,
3234
+ iconAnimate: config2.animate,
3235
+ label: isExpired2 ? "Payment Expired" : config2.label,
3236
+ subtitle: canPay2 && timeLeft ? `Expires in ${timeLeft}` : isExpired2 ? "Please create a new payment to continue" : null
3237
+ };
3238
+ const qrCodeUrl2 = payment?.pay_address && canPay2 ? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(payment.pay_address)}` : null;
3239
+ const amountUsd2 = payment?.amount_usd ? `$${parseFloat(payment.amount_usd).toFixed(2)} USD` : "";
3240
+ const createdAt2 = payment?.created_at ? moment3__default.default.utc(payment.created_at).local().format("MMM D, YYYY HH:mm") : "";
3241
+ return {
3242
+ status,
3243
+ config: config2,
3244
+ isPending,
3245
+ isExpired: isExpired2,
3246
+ isCompleted,
3247
+ isFailed,
3248
+ isConfirming,
3249
+ canPay: canPay2,
3250
+ description: description2,
3251
+ statusBadge: statusBadge2,
3252
+ qrCodeUrl: qrCodeUrl2,
3253
+ amountUsd: amountUsd2,
3254
+ createdAt: createdAt2
3255
+ };
3256
+ }, [payment, timeLeft]);
3257
+ const { config, canPay, isExpired, description, statusBadge, qrCodeUrl, amountUsd, createdAt } = displayData;
2408
3258
  const StatusIcon = config.icon;
2409
- const qrCodeUrl = payment?.pay_address ? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(payment.pay_address)}` : null;
2410
- const isPending = status === "pending";
2411
3259
  return /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheet, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetContent, { className: "sm:max-w-lg", children: [
2412
3260
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetHeader, { children: [
2413
3261
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetTitle, { children: "Payment Details" }),
2414
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetDescription, { children: isPending ? "Send cryptocurrency to complete payment" : "Payment information" })
3262
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetDescription, { children: description })
2415
3263
  ] }),
2416
3264
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 sm:p-0 sm:mt-4 overflow-y-auto max-h-[70vh]", children: [
2417
3265
  isLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
@@ -2425,14 +3273,11 @@ function PaymentSheet({ paymentId, open, onOpenChange }) {
2425
3273
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.Button, { onClick: () => mutate(), children: "Try Again" })
2426
3274
  ] }),
2427
3275
  payment && !isLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
2428
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex items-center gap-3 p-4 rounded-xl", config.bg), children: [
2429
- /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { className: lib.cn("h-6 w-6", config.color, config.animate && "animate-spin") }),
3276
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex items-center gap-3 p-4 rounded-xl", statusBadge.bg), children: [
3277
+ /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { className: lib.cn("h-6 w-6", statusBadge.iconColor, statusBadge.iconAnimate && "animate-spin") }),
2430
3278
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
2431
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold", children: config.label }),
2432
- isPending && timeLeft && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-muted-foreground", children: [
2433
- "Expires in ",
2434
- timeLeft
2435
- ] })
3279
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold", children: statusBadge.label }),
3280
+ statusBadge.subtitle && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-muted-foreground", children: statusBadge.subtitle })
2436
3281
  ] })
2437
3282
  ] }),
2438
3283
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-muted rounded-xl p-4 space-y-3", children: [
@@ -2449,30 +3294,38 @@ function PaymentSheet({ paymentId, open, onOpenChange }) {
2449
3294
  ] }),
2450
3295
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
2451
3296
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Equivalent" }),
2452
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
2453
- "$",
2454
- parseFloat(payment.amount_usd).toFixed(2),
2455
- " USD"
2456
- ] })
3297
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: amountUsd })
2457
3298
  ] }),
2458
3299
  payment.currency_network && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm pt-2 border-t", children: [
2459
3300
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Network" }),
2460
3301
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: payment.currency_network })
2461
3302
  ] })
2462
3303
  ] }),
2463
- qrCodeUrl && isPending && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center p-6 bg-white rounded-xl", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: qrCodeUrl, alt: "Payment QR Code", className: "w-48 h-48" }) }),
2464
- payment.pay_address && isPending && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3304
+ qrCodeUrl && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center p-6 bg-white rounded-xl", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: qrCodeUrl, alt: "Payment QR Code", className: "w-48 h-48" }) }),
3305
+ payment.pay_address && canPay && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2465
3306
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Payment Address" }),
2466
3307
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2467
3308
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 p-3 bg-muted rounded-xl font-mono text-sm break-all", children: payment.pay_address }),
2468
3309
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.CopyButton, { value: payment.pay_address, variant: "outline", className: "shrink-0" })
2469
3310
  ] })
2470
3311
  ] }),
3312
+ isExpired && onCreateNew && /* @__PURE__ */ jsxRuntime.jsx(
3313
+ uiCore.Button,
3314
+ {
3315
+ size: "lg",
3316
+ className: "w-full",
3317
+ onClick: () => {
3318
+ onOpenChange(false);
3319
+ onCreateNew();
3320
+ },
3321
+ children: "Create New Payment"
3322
+ }
3323
+ ),
2471
3324
  payment.transaction_hash && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2472
3325
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Transaction Hash" }),
2473
3326
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 bg-muted rounded-xl font-mono text-sm break-all", children: payment.transaction_hash })
2474
3327
  ] }),
2475
- payment.payment_url && isPending && /* @__PURE__ */ jsxRuntime.jsxs(
3328
+ payment.payment_url && canPay && /* @__PURE__ */ jsxRuntime.jsxs(
2476
3329
  uiCore.Button,
2477
3330
  {
2478
3331
  variant: "outline",
@@ -2495,7 +3348,7 @@ function PaymentSheet({ paymentId, open, onOpenChange }) {
2495
3348
  ] }),
2496
3349
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
2497
3350
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Created" }),
2498
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: moment2__default.default.utc(payment.created_at).local().format("MMM D, YYYY HH:mm") })
3351
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: createdAt })
2499
3352
  ] })
2500
3353
  ] }),
2501
3354
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2514,54 +3367,85 @@ function PaymentSheet({ paymentId, open, onOpenChange }) {
2514
3367
  ] })
2515
3368
  ] }) });
2516
3369
  }
2517
- var ResponsiveSheetContext = React2__namespace.createContext({ isMobile: false });
2518
- function ResponsiveSheet4({ open, onOpenChange, children }) {
2519
- const isMobile = uiCore.useIsMobile();
2520
- if (isMobile) {
2521
- return /* @__PURE__ */ jsxRuntime.jsx(ResponsiveSheetContext.Provider, { value: { isMobile: true }, children: /* @__PURE__ */ jsxRuntime.jsx(uiCore.Drawer, { open, onOpenChange, children }) });
2522
- }
2523
- return /* @__PURE__ */ jsxRuntime.jsx(ResponsiveSheetContext.Provider, { value: { isMobile: false }, children: /* @__PURE__ */ jsxRuntime.jsx(uiCore.Dialog, { open, onOpenChange, children }) });
2524
- }
2525
- function ResponsiveSheetContent4({ children, className }) {
2526
- const { isMobile } = React2__namespace.useContext(ResponsiveSheetContext);
2527
- if (isMobile) {
2528
- return /* @__PURE__ */ jsxRuntime.jsx(uiCore.DrawerContent, { className, children });
2529
- }
2530
- return /* @__PURE__ */ jsxRuntime.jsx(uiCore.DialogContent, { className, children });
2531
- }
2532
- function ResponsiveSheetHeader4({ children, className }) {
2533
- const { isMobile } = React2__namespace.useContext(ResponsiveSheetContext);
2534
- if (isMobile) {
2535
- return /* @__PURE__ */ jsxRuntime.jsx(uiCore.DrawerHeader, { className, children });
2536
- }
2537
- return /* @__PURE__ */ jsxRuntime.jsx(uiCore.DialogHeader, { className, children });
2538
- }
2539
- function ResponsiveSheetTitle4({ children, className }) {
2540
- const { isMobile } = React2__namespace.useContext(ResponsiveSheetContext);
2541
- if (isMobile) {
2542
- return /* @__PURE__ */ jsxRuntime.jsx(uiCore.DrawerTitle, { className, children });
2543
- }
2544
- return /* @__PURE__ */ jsxRuntime.jsx(uiCore.DialogTitle, { className, children });
2545
- }
2546
- function ResponsiveSheetDescription4({ children, className }) {
2547
- const { isMobile } = React2__namespace.useContext(ResponsiveSheetContext);
2548
- if (isMobile) {
2549
- return /* @__PURE__ */ jsxRuntime.jsx(uiCore.DrawerDescription, { className, children });
2550
- }
2551
- return /* @__PURE__ */ jsxRuntime.jsx(uiCore.DialogDescription, { className, children });
2552
- }
2553
- function ResponsiveSheetFooter({ children, className }) {
2554
- const { isMobile } = React2__namespace.useContext(ResponsiveSheetContext);
2555
- if (isMobile) {
2556
- return /* @__PURE__ */ jsxRuntime.jsx(uiCore.DrawerFooter, { className, children });
2557
- }
2558
- return /* @__PURE__ */ jsxRuntime.jsx(uiCore.DialogFooter, { className, children });
3370
+ var WalletContent = () => {
3371
+ const [addFundsOpen, setAddFundsOpen] = react.useState(false);
3372
+ const [withdrawOpen, setWithdrawOpen] = react.useState(false);
3373
+ const [selectedPaymentId, setSelectedPaymentId] = react.useState(null);
3374
+ const [paymentSheetOpen, setPaymentSheetOpen] = react.useState(false);
3375
+ const [selectedWithdrawalId, setSelectedWithdrawalId] = react.useState(null);
3376
+ const [withdrawalSheetOpen, setWithdrawalSheetOpen] = react.useState(false);
3377
+ const handleActivityClick = react.useCallback((item) => {
3378
+ if (item.payment) {
3379
+ setSelectedPaymentId(item.payment.id);
3380
+ setPaymentSheetOpen(true);
3381
+ } else if (item.withdrawal) {
3382
+ setSelectedWithdrawalId(item.withdrawal.id);
3383
+ setWithdrawalSheetOpen(true);
3384
+ }
3385
+ }, []);
3386
+ const handlePaymentCreated = react.useCallback((payment) => {
3387
+ setSelectedPaymentId(payment.id);
3388
+ setPaymentSheetOpen(true);
3389
+ }, []);
3390
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen", children: [
3391
+ /* @__PURE__ */ jsxRuntime.jsx(
3392
+ BalanceHero,
3393
+ {
3394
+ onAddFunds: () => setAddFundsOpen(true),
3395
+ onWithdraw: () => setWithdrawOpen(true),
3396
+ className: "bg-muted/30 border-b"
3397
+ }
3398
+ ),
3399
+ /* @__PURE__ */ jsxRuntime.jsx(
3400
+ ActivityList,
3401
+ {
3402
+ onItemClick: handleActivityClick,
3403
+ limit: 20,
3404
+ className: "max-w-2xl mx-auto pb-12"
3405
+ }
3406
+ ),
3407
+ /* @__PURE__ */ jsxRuntime.jsx(
3408
+ AddFundsSheet,
3409
+ {
3410
+ open: addFundsOpen,
3411
+ onOpenChange: setAddFundsOpen,
3412
+ onSuccess: handlePaymentCreated
3413
+ }
3414
+ ),
3415
+ /* @__PURE__ */ jsxRuntime.jsx(
3416
+ WithdrawSheet,
3417
+ {
3418
+ open: withdrawOpen,
3419
+ onOpenChange: setWithdrawOpen
3420
+ }
3421
+ ),
3422
+ /* @__PURE__ */ jsxRuntime.jsx(
3423
+ PaymentSheet,
3424
+ {
3425
+ paymentId: selectedPaymentId,
3426
+ open: paymentSheetOpen,
3427
+ onOpenChange: setPaymentSheetOpen,
3428
+ onCreateNew: () => setAddFundsOpen(true)
3429
+ }
3430
+ ),
3431
+ /* @__PURE__ */ jsxRuntime.jsx(
3432
+ WithdrawalSheet,
3433
+ {
3434
+ withdrawalId: selectedWithdrawalId,
3435
+ open: withdrawalSheetOpen,
3436
+ onOpenChange: setWithdrawalSheetOpen
3437
+ }
3438
+ )
3439
+ ] });
3440
+ };
3441
+ function WalletPage() {
3442
+ return /* @__PURE__ */ jsxRuntime.jsx(WalletProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(WalletContent, {}) });
2559
3443
  }
2560
3444
 
2561
3445
  // package.json
2562
3446
  var package_default = {
2563
3447
  name: "@djangocfg/ext-payments",
2564
- version: "1.0.17",
3448
+ version: "1.0.19",
2565
3449
  description: "Payments system extension for DjangoCFG",
2566
3450
  keywords: [
2567
3451
  "django",
@@ -2723,23 +3607,22 @@ exports.NetworkError = NetworkError;
2723
3607
  exports.PaginatedPaymentListListSchema = PaginatedPaymentListListSchema;
2724
3608
  exports.PaginatedWithdrawalListListSchema = PaginatedWithdrawalListListSchema;
2725
3609
  exports.PaymentCreateRequestSchema = PaymentCreateRequestSchema;
3610
+ exports.PaymentCreateResponseSchema = PaymentCreateResponseSchema;
2726
3611
  exports.PaymentDetailSchema = PaymentDetailSchema;
2727
3612
  exports.PaymentListSchema = PaymentListSchema;
2728
3613
  exports.PaymentSheet = PaymentSheet;
2729
3614
  exports.REFRESH_TOKEN_KEY = REFRESH_TOKEN_KEY;
2730
- exports.ResponsiveSheet = ResponsiveSheet4;
2731
- exports.ResponsiveSheetContent = ResponsiveSheetContent4;
2732
- exports.ResponsiveSheetDescription = ResponsiveSheetDescription4;
2733
- exports.ResponsiveSheetFooter = ResponsiveSheetFooter;
2734
- exports.ResponsiveSheetHeader = ResponsiveSheetHeader4;
2735
- exports.ResponsiveSheetTitle = ResponsiveSheetTitle4;
2736
3615
  exports.Schemas = schemas_exports;
2737
3616
  exports.TOKEN_KEY = TOKEN_KEY;
2738
3617
  exports.TransactionSchema = TransactionSchema;
3618
+ exports.WalletPage = WalletPage;
2739
3619
  exports.WithdrawSheet = WithdrawSheet;
3620
+ exports.WithdrawalCancelResponseSchema = WithdrawalCancelResponseSchema;
2740
3621
  exports.WithdrawalCreateRequestSchema = WithdrawalCreateRequestSchema;
3622
+ exports.WithdrawalCreateResponseSchema = WithdrawalCreateResponseSchema;
2741
3623
  exports.WithdrawalDetailSchema = WithdrawalDetailSchema;
2742
3624
  exports.WithdrawalListSchema = WithdrawalListSchema;
3625
+ exports.WithdrawalSheet = WithdrawalSheet;
2743
3626
  exports.apiPayments = apiPayments;
2744
3627
  exports.clearAPITokens = clearAPITokens;
2745
3628
  exports.configureAPI = configureAPI;
@@ -2752,7 +3635,9 @@ exports.extensionConfig = extensionConfig;
2752
3635
  exports.formatZodError = formatZodError;
2753
3636
  exports.getAPIInstance = getAPIInstance;
2754
3637
  exports.getPaymentsBalanceRetrieve = getPaymentsBalanceRetrieve;
3638
+ exports.getPaymentsCurrenciesEstimateRetrieve = getPaymentsCurrenciesEstimateRetrieve;
2755
3639
  exports.getPaymentsCurrenciesList = getPaymentsCurrenciesList;
3640
+ exports.getPaymentsCurrenciesWithdrawalEstimateRetrieve = getPaymentsCurrenciesWithdrawalEstimateRetrieve;
2756
3641
  exports.getPaymentsPaymentsList = getPaymentsPaymentsList;
2757
3642
  exports.getPaymentsPaymentsRetrieve = getPaymentsPaymentsRetrieve;
2758
3643
  exports.getPaymentsPaymentsStatusRetrieve = getPaymentsPaymentsStatusRetrieve;