1
0
Fork 0
polaris-task-force/src/components/frontend/banking/WalletHero.tsx

140 lines
No EOL
5.4 KiB
TypeScript

"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { ChevronRightIcon, WalletIcon } from "lucide-react";
import type { BankAccount, Resource } from "@/payload-types";
import { Button } from "@/components/ui/button";
import { formatAmount } from "@/lib/banking/format";
import {
type AccountStatus,
accountStatusBase,
accountStatusClass,
formatLastActivity,
} from "@/lib/banking/ui";
import { BankTransactionDialog } from "@/components/frontend/banking/BankTransactionDialog";
import { ensureMyAccount } from "@/app/(frontend)/logistics/banking/actions";
interface WalletHeroProps {
account: BankAccount | null;
currentUser: { id: number; username: string; displayName?: string } | null;
currencyResource: Resource | null;
}
// Shared terminal-aesthetic class for small uppercase mono labels in the hero.
const labelCls = "font-mono text-[10px] uppercase tracking-wider text-muted-foreground";
export function WalletHero({ account, currentUser, currencyResource }: WalletHeroProps) {
const router = useRouter();
const [initializing, setInitializing] = useState(false);
const [initError, setInitError] = useState<string | null>(null);
async function handleInitialize() {
setInitializing(true);
setInitError(null);
const res = await ensureMyAccount();
if (res.success) {
router.push(`/logistics/banking/${res.data}`);
router.refresh();
} else {
setInitError(res.error ?? "Failed to initialize wallet.");
setInitializing(false);
}
}
// Empty state — terminal-flavored "WALLET NOT INITIALIZED"
if (!account || !currentUser) {
return (
<section className="rounded-lg border border-blue-500/25 bg-blue-500/[0.03] p-5">
<div className="flex flex-col gap-3">
<div className="flex items-center gap-2">
<WalletIcon className="size-4 text-blue-400/80" />
<span className={labelCls}>&lt;OPERATOR WALLET&gt;</span>
</div>
<p className="font-mono text-sm text-muted-foreground/80">{"> WALLET NOT INITIALIZED"}</p>
<p className="font-mono text-xs text-muted-foreground/60">
Run initialization to provision your personal account.
</p>
{initError && (
<p className="font-mono text-xs text-red-400">
{"> ERROR: "}
{initError}
</p>
)}
<div>
<Button
size="sm"
onClick={handleInitialize}
disabled={initializing}
className="font-mono text-xs uppercase tracking-wider"
>
{initializing ? "> INITIALIZING…" : "> INITIALIZE"}
</Button>
</div>
</div>
</section>
);
}
const accountId = account.id;
const lastActivity = formatLastActivity(account.updatedAt);
const currency = typeof account.currency === "object" ? account.currency : currencyResource;
const operatorLabel = currentUser.displayName ?? currentUser.username;
const statusKey = (account.status ?? "open") as AccountStatus;
// otherAccounts requires names for the transfer destination select.
// We only know this wallet here; the BankTransactionDialog receives an empty
// list — transfer mode will prompt the user to use the account detail page.
// Deposits/withdrawals work standalone on the hero.
const otherAccounts: { id: number; name: string }[] = [];
return (
<section className="rounded-lg border border-blue-500/25 bg-blue-500/[0.03] p-5">
<div className="flex flex-col gap-4">
<div className="flex flex-wrap items-start justify-between gap-2">
<div className="flex flex-col gap-1">
<div className="flex items-center gap-2">
<WalletIcon className="size-4 text-blue-400/80" />
<span className={labelCls}>&lt;OPERATOR WALLET&gt;</span>
</div>
<div className="flex items-center gap-2">
<span className="font-mono text-sm text-muted-foreground/90">@{operatorLabel}</span>
<span
className={`${accountStatusBase} ${accountStatusClass[statusKey] ?? accountStatusClass.open}`}
>
{statusKey}
</span>
</div>
</div>
<span className="font-mono text-[10px] uppercase tracking-wider text-muted-foreground/50">
ID #{accountId.toString().padStart(6, "0")}
</span>
</div>
<div className="flex flex-col gap-1">
<p className="text-4xl md:text-5xl font-bold font-mono tracking-tight">
{formatAmount(account.balance ?? 0, currency)}
</p>
<p className="font-mono text-[10px] uppercase tracking-wider text-muted-foreground/60">
LAST ACTIVITY {lastActivity}
</p>
</div>
<div className="flex flex-wrap items-center gap-2 pt-3 border-t border-blue-500/15">
<BankTransactionDialog accountId={accountId} otherAccounts={otherAccounts} />
<Link href={`/logistics/banking/${accountId}`} prefetch={false}>
<Button
size="sm"
variant="outline"
className="font-mono text-xs uppercase tracking-wider"
>
{"> LEDGER"}
<ChevronRightIcon className="size-3.5" />
</Button>
</Link>
</div>
</div>
</section>
);
}