@djangocfg/ext-payments 1.0.13 → 1.0.17

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 (59) hide show
  1. package/dist/config.cjs +5 -8
  2. package/dist/config.js +5 -8
  3. package/dist/hooks.cjs +1 -1
  4. package/dist/hooks.js +1 -1
  5. package/dist/index.cjs +1085 -1107
  6. package/dist/index.d.cts +480 -41
  7. package/dist/index.d.ts +480 -41
  8. package/dist/index.js +1037 -1093
  9. package/package.json +13 -16
  10. package/src/api/generated/ext_payments/CLAUDE.md +7 -3
  11. package/src/api/generated/ext_payments/_utils/fetchers/ext_payments__payments.ts +237 -5
  12. package/src/api/generated/ext_payments/_utils/hooks/ext_payments__payments.ts +71 -3
  13. package/src/api/generated/ext_payments/_utils/schemas/PaginatedWithdrawalListList.schema.ts +24 -0
  14. package/src/api/generated/ext_payments/_utils/schemas/PaymentCreateRequest.schema.ts +21 -0
  15. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCreateRequest.schema.ts +21 -0
  16. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalDetail.schema.ts +42 -0
  17. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalList.schema.ts +29 -0
  18. package/src/api/generated/ext_payments/_utils/schemas/index.ts +5 -0
  19. package/src/api/generated/ext_payments/enums.ts +36 -0
  20. package/src/api/generated/ext_payments/ext_payments__payments/client.ts +58 -5
  21. package/src/api/generated/ext_payments/ext_payments__payments/models.ts +141 -0
  22. package/src/api/generated/ext_payments/schema.json +579 -3
  23. package/src/components/ActivityItem.tsx +118 -0
  24. package/src/components/ActivityList.tsx +93 -0
  25. package/src/components/AddFundsSheet.tsx +258 -0
  26. package/src/components/BalanceHero.tsx +102 -0
  27. package/src/components/PaymentSheet.tsx +290 -0
  28. package/src/components/ResponsiveSheet.tsx +151 -0
  29. package/src/components/WithdrawSheet.tsx +329 -0
  30. package/src/components/index.ts +18 -0
  31. package/src/contexts/WalletContext.tsx +355 -0
  32. package/src/contexts/index.ts +12 -45
  33. package/src/index.ts +6 -18
  34. package/src/contexts/BalancesContext.tsx +0 -63
  35. package/src/contexts/CurrenciesContext.tsx +0 -64
  36. package/src/contexts/OverviewContext.tsx +0 -173
  37. package/src/contexts/PaymentsContext.tsx +0 -122
  38. package/src/contexts/PaymentsExtensionProvider.tsx +0 -56
  39. package/src/contexts/README.md +0 -201
  40. package/src/contexts/RootPaymentsContext.tsx +0 -66
  41. package/src/contexts/types.ts +0 -40
  42. package/src/hooks/index.ts +0 -20
  43. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -90
  44. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -274
  45. package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -287
  46. package/src/layouts/PaymentsLayout/components/index.ts +0 -2
  47. package/src/layouts/PaymentsLayout/events.ts +0 -47
  48. package/src/layouts/PaymentsLayout/index.ts +0 -16
  49. package/src/layouts/PaymentsLayout/types.ts +0 -6
  50. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -121
  51. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -139
  52. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
  53. package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -21
  54. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -279
  55. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
  56. package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -18
  57. package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -260
  58. package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
  59. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -18
@@ -0,0 +1,329 @@
1
+ /**
2
+ * Withdraw Sheet (Apple-style)
3
+ *
4
+ * Responsive: Dialog on desktop, Drawer on mobile
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import { useState, useMemo, useCallback } from 'react';
10
+ import { RefreshCw, AlertCircle } from 'lucide-react';
11
+ import { useForm } from 'react-hook-form';
12
+ import { z } from 'zod';
13
+ import { zodResolver } from '@hookform/resolvers/zod';
14
+
15
+ import {
16
+ Alert,
17
+ AlertDescription,
18
+ Button,
19
+ Combobox,
20
+ Form,
21
+ FormControl,
22
+ FormField,
23
+ FormItem,
24
+ FormLabel,
25
+ FormMessage,
26
+ Input,
27
+ TokenIcon,
28
+ ResponsiveSheet,
29
+ ResponsiveSheetContent,
30
+ ResponsiveSheetDescription,
31
+ ResponsiveSheetHeader,
32
+ ResponsiveSheetTitle,
33
+ } from '@djangocfg/ui-core';
34
+
35
+ import { useWallet } from '../contexts/WalletContext';
36
+ import type { WithdrawalDetail } from '../api/generated/ext_payments/_utils/schemas';
37
+
38
+ // ─────────────────────────────────────────────────────────────────────────────
39
+ // Schema
40
+ // ─────────────────────────────────────────────────────────────────────────────
41
+
42
+ const WithdrawSchema = z.object({
43
+ amount: z.number().min(10, 'Minimum $10.00'),
44
+ currency: z.string().min(1, 'Select a currency'),
45
+ wallet_address: z.string().min(26, 'Invalid wallet address'),
46
+ });
47
+
48
+ type WithdrawForm = z.infer<typeof WithdrawSchema>;
49
+
50
+ // ─────────────────────────────────────────────────────────────────────────────
51
+ // Props
52
+ // ─────────────────────────────────────────────────────────────────────────────
53
+
54
+ interface WithdrawSheetProps {
55
+ open: boolean;
56
+ onOpenChange: (open: boolean) => void;
57
+ onSuccess?: (withdrawal: WithdrawalDetail) => void;
58
+ }
59
+
60
+ // ─────────────────────────────────────────────────────────────────────────────
61
+ // Fee Config
62
+ // ─────────────────────────────────────────────────────────────────────────────
63
+
64
+ const SERVICE_FEE_PERCENT = 0.01; // 1%
65
+ const NETWORK_FEE_USD = 1.00; // $1
66
+
67
+ // ─────────────────────────────────────────────────────────────────────────────
68
+ // Component
69
+ // ─────────────────────────────────────────────────────────────────────────────
70
+
71
+ export function WithdrawSheet({ open, onOpenChange, onSuccess }: WithdrawSheetProps) {
72
+ const { currencies, isLoadingCurrencies, withdraw, balanceAmount } = useWallet();
73
+ const [isSubmitting, setIsSubmitting] = useState(false);
74
+ const [error, setError] = useState<string | null>(null);
75
+
76
+ const form = useForm<WithdrawForm>({
77
+ resolver: zodResolver(WithdrawSchema),
78
+ defaultValues: {
79
+ amount: 10,
80
+ currency: '',
81
+ wallet_address: '',
82
+ },
83
+ });
84
+
85
+ // Currency options for combobox
86
+ const currencyOptions = useMemo(() => {
87
+ return currencies.map((c) => ({
88
+ value: c.code,
89
+ label: c.network ? `${c.code} (${c.network})` : c.code,
90
+ rate: c.rate,
91
+ network: c.network,
92
+ }));
93
+ }, [currencies]);
94
+
95
+ // Set default currency when loaded
96
+ useMemo(() => {
97
+ if (currencyOptions.length > 0 && !form.getValues('currency')) {
98
+ const usdt = currencyOptions.find(c => c.value.includes('USDT'));
99
+ form.setValue('currency', usdt?.value || currencyOptions[0].value);
100
+ }
101
+ }, [currencyOptions, form]);
102
+
103
+ // Calculate fees and final amount
104
+ const selectedCurrency = currencyOptions.find(c => c.value === form.watch('currency'));
105
+ const amount = form.watch('amount') || 0;
106
+
107
+ const feeBreakdown = useMemo(() => {
108
+ const serviceFee = amount * SERVICE_FEE_PERCENT;
109
+ const networkFee = NETWORK_FEE_USD;
110
+ const totalFee = serviceFee + networkFee;
111
+ const finalAmount = Math.max(0, amount - totalFee);
112
+ const cryptoAmount = selectedCurrency?.rate ? finalAmount / selectedCurrency.rate : null;
113
+
114
+ return {
115
+ serviceFee,
116
+ networkFee,
117
+ totalFee,
118
+ finalAmount,
119
+ cryptoAmount,
120
+ };
121
+ }, [amount, selectedCurrency]);
122
+
123
+ // Check if user has enough balance
124
+ const insufficientBalance = amount > balanceAmount;
125
+
126
+ // Handle submit
127
+ const handleSubmit = useCallback(async (data: WithdrawForm) => {
128
+ try {
129
+ setIsSubmitting(true);
130
+ setError(null);
131
+
132
+ const result = await withdraw({
133
+ amount_usd: String(data.amount),
134
+ currency_code: data.currency,
135
+ wallet_address: data.wallet_address,
136
+ });
137
+
138
+ form.reset();
139
+ onOpenChange(false);
140
+ onSuccess?.(result);
141
+ } catch (err: any) {
142
+ const message = err?.response?.data?.error
143
+ || err?.response?.data?.message
144
+ || err?.response?.data?.detail
145
+ || err?.message
146
+ || 'Failed to create withdrawal request';
147
+ setError(message);
148
+ } finally {
149
+ setIsSubmitting(false);
150
+ }
151
+ }, [withdraw, form, onOpenChange, onSuccess]);
152
+
153
+ // Reset on close
154
+ const handleOpenChange = useCallback((open: boolean) => {
155
+ if (!open) {
156
+ setError(null);
157
+ form.reset();
158
+ }
159
+ onOpenChange(open);
160
+ }, [form, onOpenChange]);
161
+
162
+ return (
163
+ <ResponsiveSheet open={open} onOpenChange={handleOpenChange}>
164
+ <ResponsiveSheetContent className="sm:max-w-md">
165
+ <ResponsiveSheetHeader>
166
+ <ResponsiveSheetTitle>Withdraw</ResponsiveSheetTitle>
167
+ <ResponsiveSheetDescription>
168
+ Withdraw funds to your cryptocurrency wallet
169
+ </ResponsiveSheetDescription>
170
+ </ResponsiveSheetHeader>
171
+
172
+ <Form {...form}>
173
+ <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6 p-4 sm:p-0 sm:mt-4">
174
+ {/* Amount Input */}
175
+ <FormField
176
+ control={form.control}
177
+ name="amount"
178
+ render={({ field }) => (
179
+ <FormItem>
180
+ <FormLabel>Amount (USD)</FormLabel>
181
+ <FormControl>
182
+ <div className="relative">
183
+ <span className="absolute left-4 top-1/2 -translate-y-1/2 text-muted-foreground text-lg">
184
+ $
185
+ </span>
186
+ <Input
187
+ type="number"
188
+ step="0.01"
189
+ min="10"
190
+ placeholder="10.00"
191
+ className="pl-8 text-2xl h-14 font-semibold"
192
+ {...field}
193
+ onChange={(e) => field.onChange(parseFloat(e.target.value) || 0)}
194
+ />
195
+ </div>
196
+ </FormControl>
197
+ <FormMessage />
198
+ {insufficientBalance && (
199
+ <p className="text-sm text-destructive mt-1">
200
+ Insufficient balance (Available: ${balanceAmount.toFixed(2)})
201
+ </p>
202
+ )}
203
+ </FormItem>
204
+ )}
205
+ />
206
+
207
+ {/* Currency Selection */}
208
+ <FormField
209
+ control={form.control}
210
+ name="currency"
211
+ render={({ field }) => (
212
+ <FormItem>
213
+ <FormLabel>Withdraw as</FormLabel>
214
+ <FormControl>
215
+ <Combobox
216
+ options={currencyOptions}
217
+ value={field.value}
218
+ onValueChange={field.onChange}
219
+ placeholder="Select currency..."
220
+ searchPlaceholder="Search..."
221
+ disabled={isLoadingCurrencies}
222
+ className="h-14"
223
+ renderOption={(option) => (
224
+ <div className="flex items-center gap-3 flex-1">
225
+ <TokenIcon symbol={option.value} size={24} />
226
+ <span className="font-medium">{option.label}</span>
227
+ </div>
228
+ )}
229
+ renderValue={(option) => option && (
230
+ <div className="flex items-center gap-3">
231
+ <TokenIcon symbol={option.value} size={24} />
232
+ <span className="font-medium">{option.label}</span>
233
+ </div>
234
+ )}
235
+ />
236
+ </FormControl>
237
+ <FormMessage />
238
+ </FormItem>
239
+ )}
240
+ />
241
+
242
+ {/* Wallet Address */}
243
+ <FormField
244
+ control={form.control}
245
+ name="wallet_address"
246
+ render={({ field }) => (
247
+ <FormItem>
248
+ <FormLabel>Wallet Address</FormLabel>
249
+ <FormControl>
250
+ <Input
251
+ placeholder="Enter your wallet address"
252
+ className="font-mono text-sm"
253
+ {...field}
254
+ />
255
+ </FormControl>
256
+ <FormMessage />
257
+ </FormItem>
258
+ )}
259
+ />
260
+
261
+ {/* Fee Breakdown */}
262
+ {amount >= 10 && selectedCurrency && (
263
+ <div className="bg-muted rounded-xl p-4 space-y-2">
264
+ <div className="flex items-center justify-between text-sm">
265
+ <span className="text-muted-foreground">Amount</span>
266
+ <span>${amount.toFixed(2)}</span>
267
+ </div>
268
+ <div className="flex items-center justify-between text-sm">
269
+ <span className="text-muted-foreground">Service fee (1%)</span>
270
+ <span className="text-destructive">-${feeBreakdown.serviceFee.toFixed(2)}</span>
271
+ </div>
272
+ <div className="flex items-center justify-between text-sm">
273
+ <span className="text-muted-foreground">Network fee</span>
274
+ <span className="text-destructive">-${feeBreakdown.networkFee.toFixed(2)}</span>
275
+ </div>
276
+ <div className="border-t pt-2 mt-2">
277
+ <div className="flex items-center justify-between">
278
+ <span className="font-medium">You will receive</span>
279
+ <div className="text-right">
280
+ <div className="font-semibold">${feeBreakdown.finalAmount.toFixed(2)}</div>
281
+ {feeBreakdown.cryptoAmount !== null && (
282
+ <div className="flex items-center gap-1 text-sm text-muted-foreground">
283
+ <TokenIcon symbol={selectedCurrency.value} size={16} />
284
+ <span className="font-mono">{feeBreakdown.cryptoAmount.toFixed(8)}</span>
285
+ </div>
286
+ )}
287
+ </div>
288
+ </div>
289
+ </div>
290
+ </div>
291
+ )}
292
+
293
+ {/* Warning */}
294
+ <Alert>
295
+ <AlertCircle className="h-4 w-4" />
296
+ <AlertDescription>
297
+ Withdrawal requests require admin approval. Processing may take 24-48 hours.
298
+ </AlertDescription>
299
+ </Alert>
300
+
301
+ {/* Error */}
302
+ {error && (
303
+ <Alert variant="destructive">
304
+ <AlertDescription>{error}</AlertDescription>
305
+ </Alert>
306
+ )}
307
+
308
+ {/* Submit Button */}
309
+ <Button
310
+ type="submit"
311
+ size="lg"
312
+ className="w-full h-14 text-lg rounded-xl"
313
+ disabled={isSubmitting || currencyOptions.length === 0 || insufficientBalance || feeBreakdown.finalAmount <= 0}
314
+ >
315
+ {isSubmitting ? (
316
+ <>
317
+ <RefreshCw className="h-5 w-5 mr-2 animate-spin" />
318
+ Submitting...
319
+ </>
320
+ ) : (
321
+ 'Request Withdrawal'
322
+ )}
323
+ </Button>
324
+ </form>
325
+ </Form>
326
+ </ResponsiveSheetContent>
327
+ </ResponsiveSheet>
328
+ );
329
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Wallet Components (Apple-style)
3
+ */
4
+
5
+ export { BalanceHero } from './BalanceHero';
6
+ export { ActivityList } from './ActivityList';
7
+ export { ActivityItem } from './ActivityItem';
8
+ export { AddFundsSheet } from './AddFundsSheet';
9
+ export { WithdrawSheet } from './WithdrawSheet';
10
+ export { PaymentSheet } from './PaymentSheet';
11
+ export {
12
+ ResponsiveSheet,
13
+ ResponsiveSheetContent,
14
+ ResponsiveSheetHeader,
15
+ ResponsiveSheetTitle,
16
+ ResponsiveSheetDescription,
17
+ ResponsiveSheetFooter,
18
+ } from './ResponsiveSheet';