diff --git a/src/app/(frontend)/logistics/banking/page.tsx b/src/app/(frontend)/logistics/banking/page.tsx
index 516ca25..5aba39a 100644
--- a/src/app/(frontend)/logistics/banking/page.tsx
+++ b/src/app/(frontend)/logistics/banking/page.tsx
@@ -1,7 +1,7 @@
import config from "@payload-config";
import { getPayload } from "payload";
import { headers as nextHeaders } from "next/headers";
-import type { BankAccount, Faction } from "@/payload-types";
+import type { BankAccount, BankTransaction, Faction, Resource } from "@/payload-types";
import { LandmarkIcon } from "lucide-react";
import { BankingOverview } from "@/components/frontend/banking/BankingOverview";
import hasRoles from "@/utils/access-control/hasRoles";
@@ -26,6 +26,14 @@ export default async function BankingPage() {
});
const accounts = accountsRes.docs as unknown as BankAccount[];
+ const transactionsRes = await payload.find({
+ collection: "bank-transactions",
+ sort: "-timestamp",
+ limit: 25,
+ depth: 1,
+ });
+ const recentTransactions = transactionsRes.docs as unknown as BankTransaction[];
+
const factionsRes = await payload.find({
collection: "factions",
limit: 100,
@@ -41,6 +49,21 @@ export default async function BankingPage() {
(await hasLogisticsQualification(payload, user).catch(() => false))
: false;
+ const currentUser = user
+ ? {
+ id: user.id as number,
+ username: user.username,
+ displayName: user.displayName ?? user.username,
+ }
+ : null;
+
+ // Currency is shared across all accounts (main currency set in Game Rules).
+ // Resolve from the first account whose currency populated at depth 1.
+ const currencyResource =
+ accounts.length > 0 && typeof accounts[0].currency === "object"
+ ? (accounts[0].currency as Resource)
+ : null;
+
return (
@@ -55,10 +78,12 @@ export default async function BankingPage() {
);
-}
+}
\ No newline at end of file
diff --git a/src/components/frontend/banking/BankTransactionDialog.tsx b/src/components/frontend/banking/BankTransactionDialog.tsx
index 906f4cb..c8e1c55 100644
--- a/src/components/frontend/banking/BankTransactionDialog.tsx
+++ b/src/components/frontend/banking/BankTransactionDialog.tsx
@@ -124,6 +124,7 @@ export function BankTransactionDialog({
size="sm"
variant={mode === m ? "default" : "outline"}
onClick={() => openMode(m)}
+ className="font-mono text-xs uppercase tracking-wider"
>
{M.label}
diff --git a/src/components/frontend/banking/BankingOverview.tsx b/src/components/frontend/banking/BankingOverview.tsx
index 26e1731..d8f0ae0 100644
--- a/src/components/frontend/banking/BankingOverview.tsx
+++ b/src/components/frontend/banking/BankingOverview.tsx
@@ -1,18 +1,25 @@
"use client";
import type { ReactNode } from "react";
-import Link from "next/link";
-import type { BankAccount } from "@/payload-types";
-import { LandmarkIcon, WalletIcon } from "lucide-react";
-import { Item, ItemContent, ItemHeader, ItemMedia, ItemTitle } from "@/components/ui/item";
-import { formatAmount } from "@/lib/banking/format";
+import type { BankAccount, BankTransaction, Resource } from "@/payload-types";
+import { LandmarkIcon } from "lucide-react";
+import {
+ Tabs,
+ TabsList,
+ TabsTrigger,
+ TabsContent,
+} from "@/components/ui/tabs";
import { AccountCard } from "@/components/frontend/banking/AccountCard";
import { CreateAccountDialog } from "@/components/frontend/banking/CreateAccountDialog";
-import { MyWalletCard } from "@/components/frontend/banking/MyWalletCard";
+import { WalletHero } from "@/components/frontend/banking/WalletHero";
+import { ActivityFeed } from "@/components/frontend/banking/ActivityFeed";
+import { currencyLabel, formatAmount } from "@/lib/banking/format";
interface BankingOverviewProps {
accounts: BankAccount[];
- currentUserId: number;
+ recentTransactions: BankTransaction[];
+ currentUser: { id: number; username: string; displayName?: string } | null;
+ currencyResource: Resource | null;
isManager: boolean;
factions: { id: number; name: string }[];
}
@@ -29,10 +36,10 @@ function Section({
return (
-
+
{title}
-
-
+
+
{count} account{count !== 1 ? "s" : ""}
@@ -49,124 +56,148 @@ function Section({
export function BankingOverview({
accounts,
- currentUserId,
+ recentTransactions,
+ currentUser,
+ currencyResource,
isManager,
factions,
}: BankingOverviewProps) {
const treasury = accounts.filter((a) => a.accountType === "treasury");
- const faction = accounts.filter((a) => a.accountType === "faction");
+ const factionAccounts = accounts.filter((a) => a.accountType === "faction");
const personal = accounts.filter((a) => a.accountType === "personal");
- const myAccount = personal.find((a) => {
- const ownerId = typeof a.ownerUser === "object" ? a.ownerUser?.id : a.ownerUser;
- return ownerId === currentUserId;
- });
+ const myAccount = currentUser
+ ? personal.find((a) => {
+ if (!a.ownerUser) return false;
+ const ownerId =
+ typeof a.ownerUser === "object" ? a.ownerUser.id : a.ownerUser;
+ return ownerId === currentUser.id;
+ })
+ : undefined;
- const unitFunds = [...treasury, ...faction].reduce(
+ // Regular users see only their own personal account; managers see everything.
+ const visiblePersonal = isManager
+ ? personal
+ : myAccount
+ ? [myAccount]
+ : [];
+
+ const unitFundsTotal = [...treasury, ...factionAccounts].reduce(
(sum, a) => sum + (a.balance ?? 0),
0,
);
- const currency =
- accounts.length > 0 && typeof accounts[0].currency === "object"
- ? accounts[0].currency
- : null;
- const accountTypeLabels: Record = {
- treasury: "Treasury",
- faction: "Faction",
- personal: "Personal",
- };
+ const visibleAccountsCount =
+ (isManager ? treasury.length + factionAccounts.length : 0) +
+ visiblePersonal.length;
+
+ const tickerCurrency = (label: string): string =>
+ label.length > 8 ? `${label.slice(0, 8)}…` : label;
return (
-
-
-
-
-
-
-
-
-
- Unit funds
-
-
- {formatAmount(unitFunds, currency)}
-
-
-
+
+
-
-
-
-
-
-
-
- Accounts
-
-
- {accounts.length}
-
-
-
+
+
+
+
+ Activity
+
+
+ Accounts
+
+
- {myAccount ? (
-
- -
-
-
-
-
-
- My wallet
-
-
- {formatAmount(myAccount.balance ?? 0, currency)}
-
-
-
-
- ) : (
-
- )}
-
-
-
-
-
- Accounts
-
+ {isManager && (
+
+ UNIT FUNDS
+ {formatAmount(unitFundsTotal, currencyResource)}
+
+ )}
-
-
-
- {treasury.map((account) => (
-
- ))}
-
+
+
+
-
- {faction.map((account) => (
-
- ))}
-
+
+
+
+
+
+ Unit Accounts
+
+
+ {visibleAccountsCount}
+
+
+
+
-
- {personal.map((account) => (
-
- ))}
-
+ {visibleAccountsCount === 0 ? (
+
+ No accessible accounts.{" "}
+ {!isManager && "Ask logistics personnel to set up the unit treasury."}
+
+ ) : (
+ <>
+ {isManager && treasury.length > 0 && (
+
+ {treasury.map((account) => (
+
+ ))}
+
+ )}
+
+ {isManager && factionAccounts.length > 0 && (
+
+ {factionAccounts.map((account) => (
+
+ ))}
+
+ )}
+
+ {visiblePersonal.length > 0 && (
+
+ {visiblePersonal.map((account) => (
+
+ ))}
+
+ )}
+ >
+ )}
+
+
{accounts.length === 0 && (
No accounts yet. Create your first account to get started.
- {isManager ? "" : " (Ask logistics personnel to set up the unit treasury.)"}
)}
-
- Account types: {Object.values(accountTypeLabels).join(" · ")}.
- Balances are denominated in the main currency set in Game Rules.
+
+ SYS.BANKING / currency={tickerCurrency(currencyLabel(currencyResource))}
);
-}
+}
\ No newline at end of file