feat(banking): inline currency emblem display with optional label hiding
This commit is contained in:
parent
ae4ed8402d
commit
213a8c106f
4 changed files with 24 additions and 16 deletions
|
|
@ -3,7 +3,7 @@ import { getPayload } from "payload";
|
|||
import { notFound } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { headers as nextHeaders } from "next/headers";
|
||||
import type { BankAccount, Campaign, Faction, Media, Mission, Resource } from "@/payload-types";
|
||||
import type { BankAccount, Campaign, Faction, Media, Mission } from "@/payload-types";
|
||||
import { RichText } from "@payloadcms/richtext-lexical/react";
|
||||
import { convertLexicalToPlaintext } from "@payloadcms/richtext-lexical/plaintext";
|
||||
import {
|
||||
|
|
@ -16,7 +16,8 @@ import {
|
|||
} from "lucide-react";
|
||||
import { Item, ItemContent, ItemHeader, ItemTitle } from "@/components/ui/item";
|
||||
import { MissionCard } from "@/components/frontend/intelligence/MissionCard";
|
||||
import { formatAmount } from "@/lib/banking/format";
|
||||
import { getCurrencyConfig } from "@/lib/banking";
|
||||
import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount";
|
||||
|
||||
interface FactionDetailPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
|
|
@ -118,10 +119,9 @@ export default async function FactionDetailPage({ params }: FactionDetailPagePro
|
|||
})
|
||||
.catch(() => ({ docs: [] }));
|
||||
const bankAccount = (bankRes.docs[0] as BankAccount | undefined) ?? null;
|
||||
const bankCurrency =
|
||||
bankAccount && typeof bankAccount.currency === "object"
|
||||
? (bankAccount.currency as Resource)
|
||||
: null;
|
||||
// Currency emblem/label is shared across all accounts (from Game Rules), so
|
||||
// the bank balance renders the same emblem the banking surfaces show.
|
||||
const currencyConfig = await getCurrencyConfig(payload);
|
||||
|
||||
// Missions involving this faction
|
||||
const missionsRes = await payload
|
||||
|
|
@ -365,7 +365,7 @@ export default async function FactionDetailPage({ params }: FactionDetailPagePro
|
|||
<ItemContent>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-lg font-semibold font-mono">
|
||||
{formatAmount(bankAccount.balance ?? 0, bankCurrency)}
|
||||
<CurrencyAmount value={bankAccount.balance ?? 0} currency={currencyConfig} />
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">{bankAccount.name}</span>
|
||||
{bankAccount.status !== "open" && (
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { formatAmount } from "@/lib/banking/format";
|
||||
import { formatAmount, isCurrencyConfig } from "@/lib/banking/format";
|
||||
import type { CurrencyDisplay } from "@/lib/banking/format";
|
||||
import { CurrencySymbol } from "./CurrencySymbol";
|
||||
|
||||
/**
|
||||
* Renders the currency emblem inline with the formatted amount. Delegates
|
||||
* symbol rendering to <CurrencySymbol> and formatting to formatAmount.
|
||||
* Renders nothing when currency is null/undefined/number (no emblem to show).
|
||||
* symbol rendering to <CurrencySymbol> and formatting to formatAmount. When an
|
||||
* emblem is configured the unit label (e.g. "credits") is dropped so the icon
|
||||
* sits beside the number without a trailing word; renders nothing when no
|
||||
* emblem is configured.
|
||||
*/
|
||||
export function CurrencyAmount({
|
||||
value,
|
||||
|
|
@ -14,10 +16,12 @@ export function CurrencyAmount({
|
|||
readonly value: number;
|
||||
readonly currency?: CurrencyDisplay | null;
|
||||
}) {
|
||||
const showSymbol =
|
||||
currency != null && typeof currency === "object" && isCurrencyConfig(currency) && !!currency.symbol;
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<CurrencySymbol currency={currency} />
|
||||
{formatAmount(value, currency)}
|
||||
</>
|
||||
<span>{formatAmount(value, currency, showSymbol ? { hideLabel: true } : undefined)}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export function CurrencySymbol({
|
|||
aria-hidden={ariaHidden}
|
||||
loading="lazy"
|
||||
className={cn(
|
||||
"inline-block h-[0.85em] align-text-bottom max-h-[1.15em] object-contain",
|
||||
"inline-block h-[0.85em] align-middle max-h-[1.15em] object-contain",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -44,9 +44,13 @@ export function currencyLabel(currency?: CurrencyDisplay, amount = 2): string {
|
|||
);
|
||||
}
|
||||
|
||||
export function formatAmount(amount: number, currency?: CurrencyDisplay): string {
|
||||
export function formatAmount(
|
||||
amount: number,
|
||||
currency?: CurrencyDisplay,
|
||||
options?: { readonly hideLabel?: boolean },
|
||||
): string {
|
||||
const n = amount.toLocaleString();
|
||||
const label = currencyLabel(currency, amount);
|
||||
const label = options?.hideLabel ? "" : currencyLabel(currency, amount);
|
||||
return label ? `${n} ${label}` : n;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue