refactor: enhance wallet transfer dialogs and forms
- Removed unused trigger styles and refactored button variants for improved consistency. - Introduced new components for transfer information, previews, and error handling to streamline the UI. - Updated layout and styling for better user experience in transfer dialogs and forms.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import { isAxiosError } from "axios";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useMemo, useState, type ReactNode } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
|
||||
@@ -55,6 +55,73 @@ type PanelBase = {
|
||||
|
||||
type PanelVariant = "dialog" | "page";
|
||||
|
||||
function TransferInfoBlock({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-muted/40 px-4 py-3 text-sm leading-relaxed">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TransferPreview({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<p className="rounded-lg bg-muted/50 px-3 py-2 text-sm text-muted-foreground">{children}</p>
|
||||
);
|
||||
}
|
||||
|
||||
function TransferError({ message }: { message: string }) {
|
||||
return (
|
||||
<p className="rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
||||
{message}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
function TransferDialogFooter({
|
||||
submitting,
|
||||
confirmLabel,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}: {
|
||||
submitting: boolean;
|
||||
confirmLabel: string;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation("player");
|
||||
|
||||
return (
|
||||
<div className="mt-5 grid gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
size="lg"
|
||||
className="h-11 w-full bg-[#07459f] text-base font-semibold text-white hover:bg-[#063b88]"
|
||||
disabled={submitting}
|
||||
onClick={onConfirm}
|
||||
>
|
||||
{submitting ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 size-4 animate-spin" />
|
||||
{t("actions.processing")}
|
||||
</>
|
||||
) : (
|
||||
confirmLabel
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="h-10 w-full"
|
||||
disabled={submitting}
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** 弹窗内:取消关闭;独立页:仅展示提交(返回用顶栏或上方链接) */
|
||||
export function TransferInPanel({
|
||||
currency,
|
||||
@@ -113,7 +180,8 @@ export function TransferInPanel({
|
||||
variant === "page" ? (
|
||||
<Button
|
||||
type="button"
|
||||
className="h-11 w-full rounded-lg bg-[#07459f] text-base font-bold text-white hover:bg-[#063b88]"
|
||||
size="lg"
|
||||
className="mt-5 h-11 w-full bg-[#07459f] text-base font-semibold text-white hover:bg-[#063b88]"
|
||||
disabled={submitting}
|
||||
onClick={() => void submit()}
|
||||
>
|
||||
@@ -127,51 +195,31 @@ export function TransferInPanel({
|
||||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<div className="grid gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
className="h-12 w-full rounded-2xl bg-[#0b3f96] text-base font-black text-white shadow-[0_10px_22px_rgba(11,63,150,0.18)] hover:bg-[#08357f]"
|
||||
disabled={submitting}
|
||||
onClick={() => void submit()}
|
||||
>
|
||||
{submitting ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 size-4 animate-spin" />
|
||||
{t("actions.processing")}
|
||||
</>
|
||||
) : (
|
||||
t("wallet.confirmIn")
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="h-11 rounded-2xl border-[#dce7f7] bg-white text-base font-bold text-[#32518d] hover:bg-[#f8fbff]"
|
||||
disabled={submitting}
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
<TransferDialogFooter
|
||||
submitting={submitting}
|
||||
confirmLabel={t("wallet.confirmIn")}
|
||||
onConfirm={() => void submit()}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid gap-4 py-1">
|
||||
<div className="rounded-2xl border border-[#dbe7fb] bg-gradient-to-br from-[#f8fbff] to-white px-4 py-3 text-sm shadow-[0_8px_22px_rgba(11,63,150,0.06)]">
|
||||
<p className="text-slate-500">
|
||||
<div className="grid gap-4">
|
||||
<TransferInfoBlock>
|
||||
<p className="text-muted-foreground">
|
||||
{t("wallet.mainBalance")}{" "}
|
||||
<span className="font-semibold text-slate-700">{t("wallet.mainPending")}</span>
|
||||
<span className="font-medium text-foreground">{t("wallet.mainPending")}</span>
|
||||
</p>
|
||||
<p className="mt-2 text-slate-500">
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
{t("wallet.lotteryBalance")}{" "}
|
||||
<span className="font-black tabular-nums text-[#0b3f96]">
|
||||
<span className="font-semibold tabular-nums text-foreground">
|
||||
{formatMinorAsCurrency(lotteryMinor, currency)}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-2.5">
|
||||
<Label htmlFor={tid} className="text-sm font-black text-[#101a33]">{t("wallet.inAmount")}</Label>
|
||||
</TransferInfoBlock>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor={tid}>{t("wallet.inAmount")}</Label>
|
||||
<Input
|
||||
id={tid}
|
||||
inputMode="decimal"
|
||||
@@ -180,17 +228,15 @@ export function TransferInPanel({
|
||||
onChange={(ev) => setAmountText(ev.target.value)}
|
||||
disabled={submitting}
|
||||
autoComplete="off"
|
||||
className="h-13 rounded-2xl border-[#d7e5fb] bg-white px-4 text-lg font-bold tabular-nums text-[#101a33] shadow-inner focus-visible:ring-[#0b3f96]/20"
|
||||
className="h-11 text-base tabular-nums"
|
||||
/>
|
||||
<p className="rounded-2xl bg-[#f8fbff] px-3 py-2 text-xs font-semibold leading-5 text-[#32518d]">
|
||||
<TransferPreview>
|
||||
{t("wallet.afterInPreview", {
|
||||
amount: formatMinorAsCurrency(previewAfter, currency),
|
||||
})}
|
||||
</p>
|
||||
</TransferPreview>
|
||||
</div>
|
||||
{localError ? (
|
||||
<p className="rounded-2xl border border-red-200 bg-red-50 px-3 py-2 text-sm font-semibold text-[#d81435]">{localError}</p>
|
||||
) : null}
|
||||
{localError ? <TransferError message={localError} /> : null}
|
||||
</div>
|
||||
{footer}
|
||||
</>
|
||||
@@ -266,7 +312,8 @@ export function TransferOutPanel({
|
||||
variant === "page" ? (
|
||||
<Button
|
||||
type="button"
|
||||
className="h-11 w-full rounded-lg bg-[#e5002c] text-base font-bold text-white hover:bg-[#d10028]"
|
||||
size="lg"
|
||||
className="mt-5 h-11 w-full bg-[#07459f] text-base font-semibold text-white hover:bg-[#063b88]"
|
||||
disabled={submitting}
|
||||
onClick={() => void submit()}
|
||||
>
|
||||
@@ -280,52 +327,32 @@ export function TransferOutPanel({
|
||||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<div className="grid gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
className="h-12 w-full rounded-2xl bg-[#e5002c] text-base font-black text-white shadow-[0_10px_22px_rgba(229,0,44,0.18)] hover:bg-[#d10028]"
|
||||
disabled={submitting}
|
||||
onClick={() => void submit()}
|
||||
>
|
||||
{submitting ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 size-4 animate-spin" />
|
||||
{t("actions.processing")}
|
||||
</>
|
||||
) : (
|
||||
t("wallet.confirmOut")
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="h-11 rounded-2xl border-[#ffd7df] bg-white text-base font-bold text-[#d81435] hover:bg-[#fff5f7]"
|
||||
disabled={submitting}
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
<TransferDialogFooter
|
||||
submitting={submitting}
|
||||
confirmLabel={t("wallet.confirmOut")}
|
||||
onConfirm={() => void submit()}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid gap-4 py-1">
|
||||
<div className="rounded-2xl border border-[#ffd7df] bg-gradient-to-br from-[#fff7f8] to-white px-4 py-3 text-sm shadow-[0_8px_22px_rgba(216,20,53,0.06)]">
|
||||
<p className="text-slate-500">
|
||||
<div className="grid gap-4">
|
||||
<TransferInfoBlock>
|
||||
<p className="text-muted-foreground">
|
||||
{t("wallet.lotteryAvailable")}{" "}
|
||||
<span className="font-black tabular-nums text-[#d81435]">
|
||||
<span className="font-semibold tabular-nums text-foreground">
|
||||
{formatMinorAsCurrency(availableMinor, currency)}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-2.5">
|
||||
<div className="flex items-end justify-between gap-2">
|
||||
<Label htmlFor={tid} className="text-sm font-black text-[#101a33]">{t("wallet.outAmount")}</Label>
|
||||
</TransferInfoBlock>
|
||||
<div className="grid gap-2">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<Label htmlFor={tid}>{t("wallet.outAmount")}</Label>
|
||||
<Button
|
||||
type="button"
|
||||
variant="link"
|
||||
className="h-auto p-0 text-xs font-black text-[#d81435] hover:text-[#b80f2b]"
|
||||
className="h-auto p-0 text-sm font-medium"
|
||||
onClick={fillAll}
|
||||
disabled={submitting || availableMinor < 1}
|
||||
>
|
||||
@@ -342,17 +369,15 @@ export function TransferOutPanel({
|
||||
onChange={(ev) => setAmountText(ev.target.value)}
|
||||
disabled={submitting}
|
||||
autoComplete="off"
|
||||
className="h-13 rounded-2xl border-[#ffd7df] bg-white px-4 text-lg font-bold tabular-nums text-[#101a33] shadow-inner focus-visible:ring-[#e5002c]/20"
|
||||
className="h-11 text-base tabular-nums"
|
||||
/>
|
||||
<p className="rounded-2xl bg-[#fff7f8] px-3 py-2 text-xs font-semibold leading-5 text-[#9f1730]">
|
||||
<TransferPreview>
|
||||
{t("wallet.afterOutPreview", {
|
||||
amount: formatMinorAsCurrency(previewAfter, currency),
|
||||
})}
|
||||
</p>
|
||||
</TransferPreview>
|
||||
</div>
|
||||
{localError ? (
|
||||
<p className="rounded-2xl border border-red-200 bg-red-50 px-3 py-2 text-sm font-semibold text-[#d81435]">{localError}</p>
|
||||
) : null}
|
||||
{localError ? <TransferError message={localError} /> : null}
|
||||
</div>
|
||||
{footer}
|
||||
</>
|
||||
@@ -375,12 +400,10 @@ export function TransferInPage({
|
||||
backHref="/wallet"
|
||||
backLabel={t("wallet.title")}
|
||||
>
|
||||
<Card className="rounded-xl border-[#e5edf8] shadow-[0_8px_24px_rgba(15,23,42,0.05)]">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-[#0b3f96]">{t("wallet.transferInTitle")}</CardTitle>
|
||||
<CardDescription>
|
||||
{t("wallet.transferInDescription")}
|
||||
</CardDescription>
|
||||
<CardTitle>{t("wallet.transferInTitle")}</CardTitle>
|
||||
<CardDescription>{t("wallet.transferInDescription")}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<TransferInPanel
|
||||
@@ -413,12 +436,10 @@ export function TransferOutPage({
|
||||
backHref="/wallet"
|
||||
backLabel={t("wallet.title")}
|
||||
>
|
||||
<Card className="rounded-xl border-[#e5edf8] shadow-[0_8px_24px_rgba(15,23,42,0.05)]">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-[#0b3f96]">{t("wallet.transferOutTitle")}</CardTitle>
|
||||
<CardDescription>
|
||||
{t("wallet.transferOutDescription")}
|
||||
</CardDescription>
|
||||
<CardTitle>{t("wallet.transferOutTitle")}</CardTitle>
|
||||
<CardDescription>{t("wallet.transferOutDescription")}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<TransferOutPanel
|
||||
|
||||
Reference in New Issue
Block a user