Files
new-api/web/default/src/features/wallet/components/dialogs/transfer-dialog.tsx
T
t0ng7u d146e45e2f ⚖️ chore(web/default): add reusable copyright header tooling
Add a Bun script to apply and normalize AGPL copyright headers across the default frontend source files.

The script keeps headers idempotent, upgrades existing headers to the 2023-2026 QuantumNous range, and is exposed through `bun run copyright` for future maintenance.
2026-05-09 11:35:07 +08:00

130 lines
4.0 KiB
TypeScript
Vendored

/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { useState, useEffect } from 'react'
import { Loader2 } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { formatQuota } from '@/lib/format'
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { QUOTA_PER_DOLLAR } from '../../constants'
interface TransferDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
onConfirm: (amount: number) => Promise<boolean>
availableQuota: number
transferring: boolean
}
export function TransferDialog({
open,
onOpenChange,
onConfirm,
availableQuota,
transferring,
}: TransferDialogProps) {
const { t } = useTranslation()
const [amount, setAmount] = useState(QUOTA_PER_DOLLAR)
useEffect(() => {
if (open) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setAmount(QUOTA_PER_DOLLAR)
}
}, [open])
const handleConfirm = async () => {
const success = await onConfirm(amount)
if (success) {
onOpenChange(false)
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className='max-sm:w-[calc(100vw-1.5rem)] sm:max-w-md'>
<DialogHeader>
<DialogTitle className='text-xl font-semibold'>
{t('Transfer Rewards')}
</DialogTitle>
<DialogDescription>
{t('Move affiliate rewards to your main balance')}
</DialogDescription>
</DialogHeader>
<div className='space-y-4 py-3 sm:space-y-6 sm:py-4'>
<div className='space-y-2'>
<Label className='text-muted-foreground text-xs font-medium tracking-wider uppercase'>
{t('Available Rewards')}
</Label>
<div className='text-2xl font-semibold'>
{formatQuota(availableQuota)}
</div>
</div>
<div className='space-y-3'>
<Label
htmlFor='transfer-amount'
className='text-muted-foreground text-xs font-medium tracking-wider uppercase'
>
{t('Transfer Amount')}
</Label>
<Input
id='transfer-amount'
type='number'
value={amount}
onChange={(e) => setAmount(Number(e.target.value))}
min={QUOTA_PER_DOLLAR}
max={availableQuota}
step={QUOTA_PER_DOLLAR}
className='font-mono text-lg'
/>
<p className='text-muted-foreground text-xs'>
{t('Minimum:')} {formatQuota(QUOTA_PER_DOLLAR)}
</p>
</div>
</div>
<DialogFooter className='grid grid-cols-2 gap-2 sm:flex'>
<Button
variant='outline'
onClick={() => onOpenChange(false)}
disabled={transferring}
>
{t('Cancel')}
</Button>
<Button onClick={handleConfirm} disabled={transferring}>
{transferring && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
{t('Transfer')}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}