125927e0e8
   TODO: 补充说明书链接 Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/86 Co-authored-by: 王德宇 <kvtodev@outlook.com> Co-committed-by: 王德宇 <kvtodev@outlook.com>
131 lines
4.7 KiB
TypeScript
131 lines
4.7 KiB
TypeScript
import './index.css';
|
|
|
|
import { Coins, X } from 'lucide-react';
|
|
import { useId } from 'react';
|
|
|
|
import type { ProfileWalletLedgerResponse } from '../../contracts/runtime';
|
|
import { buildWalletLedgerPresentation } from './model';
|
|
|
|
export type PlatformProfileWalletLedgerModalProps = {
|
|
ledger: ProfileWalletLedgerResponse | null;
|
|
fallbackBalance: number;
|
|
isLoading: boolean;
|
|
error: string | null;
|
|
onClose: () => void;
|
|
onRetry: () => void;
|
|
};
|
|
|
|
export function PlatformProfileWalletLedgerModal({
|
|
ledger,
|
|
fallbackBalance,
|
|
isLoading,
|
|
error,
|
|
onClose,
|
|
onRetry,
|
|
}: PlatformProfileWalletLedgerModalProps) {
|
|
const titleId = useId();
|
|
const presentation = buildWalletLedgerPresentation(ledger, fallbackBalance);
|
|
|
|
return (
|
|
<div className="platform-profile-wallet-ledger-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-3 py-5">
|
|
<section
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-labelledby={titleId}
|
|
className="platform-profile-wallet-ledger-modal relative max-h-[min(92vh,42rem)] w-full max-w-[30rem] overflow-y-auto rounded-[1.35rem] px-4 pb-5 pt-4 sm:px-5"
|
|
>
|
|
<button
|
|
type="button"
|
|
aria-label="关闭泥点账单"
|
|
title="关闭泥点账单"
|
|
onClick={onClose}
|
|
className="platform-profile-wallet-ledger-modal__close absolute right-3 top-3 z-10 flex h-8 w-8 items-center justify-center rounded-full"
|
|
>
|
|
<X className="h-4 w-4" aria-hidden="true" />
|
|
</button>
|
|
|
|
<header className="pr-10">
|
|
<div className="text-[10px] font-black uppercase text-rose-500">
|
|
LEDGER
|
|
</div>
|
|
<div className="mt-1 flex flex-wrap items-center gap-2.5">
|
|
<h2 id={titleId} className="text-xl font-black text-zinc-950">
|
|
泥点账单
|
|
</h2>
|
|
<span className="platform-profile-wallet-ledger-modal__badge inline-flex items-center gap-1.5 rounded-full border border-rose-100 bg-white/70 px-2.5 py-1 text-[11px] font-black">
|
|
<Coins
|
|
className="h-3.5 w-3.5 text-[#ff4056]"
|
|
aria-hidden="true"
|
|
/>
|
|
{presentation.balanceLabel}
|
|
</span>
|
|
</div>
|
|
</header>
|
|
|
|
{error ? (
|
|
<div
|
|
role="alert"
|
|
className="platform-profile-wallet-ledger-modal__status-error mt-4 rounded-xl px-3 py-3 text-sm font-semibold"
|
|
>
|
|
<div>{error}</div>
|
|
<button
|
|
type="button"
|
|
className="platform-profile-wallet-ledger-modal__retry mt-3 rounded-full px-3 py-1.5 text-xs font-black"
|
|
onClick={onRetry}
|
|
>
|
|
重新加载
|
|
</button>
|
|
</div>
|
|
) : isLoading ? (
|
|
<div
|
|
role="status"
|
|
aria-label="泥点账单加载中"
|
|
className="mt-5 space-y-3"
|
|
>
|
|
{Array.from({ length: 5 }).map((_, index) => (
|
|
<div
|
|
key={index}
|
|
className="h-16 animate-pulse rounded-xl bg-zinc-100"
|
|
/>
|
|
))}
|
|
</div>
|
|
) : presentation.entries.length === 0 ? (
|
|
<div className="platform-empty-state mt-5 rounded-xl border border-[var(--platform-subpanel-border)] bg-[var(--platform-subpanel-fill)] px-4 py-8 text-center text-sm font-semibold text-[var(--platform-text-soft)]">
|
|
暂无账单记录
|
|
</div>
|
|
) : (
|
|
<div className="mt-5 space-y-2.5">
|
|
{presentation.entries.map((entry) => (
|
|
<div
|
|
key={entry.id}
|
|
className="platform-profile-wallet-ledger-modal__row flex items-center justify-between gap-3 rounded-lg border border-[var(--platform-subpanel-border)] px-3 py-3 shadow-sm"
|
|
>
|
|
<div className="min-w-0">
|
|
<div className="truncate text-sm font-black text-zinc-900">
|
|
{entry.sourceLabel}
|
|
</div>
|
|
<div className="mt-1 text-xs font-semibold text-zinc-500">
|
|
{entry.createdAtLabel}
|
|
</div>
|
|
</div>
|
|
<div className="shrink-0 text-right">
|
|
<div
|
|
className={`text-base font-black ${
|
|
entry.isIncome ? 'text-emerald-600' : 'text-rose-500'
|
|
}`}
|
|
>
|
|
{entry.amountLabel}
|
|
</div>
|
|
<div className="mt-1 text-[11px] font-semibold text-zinc-400">
|
|
{entry.balanceLabel}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
</div>
|
|
);
|
|
}
|