fix(statements): owner assignment dropdown, fix Wise CC false positive, remove amount label

- Add owner <select> dropdown per row using useUpdateStatement + useParticipants
- Detect CC by statement_type.includes('card') instead of credit_limit/payment_due_date
  (Wise multi-currency account had payment_due_date set but is not a CC)
- Amount: remove 'due'/'balance' label; color green for positive bank balances, red for CC/overdraft
- Add statement_type to StatementRow type
This commit is contained in:
2026-03-09 21:05:34 +11:00
parent f90ba332bd
commit 8bd7d77a8a
2 changed files with 34 additions and 6 deletions
+33 -6
View File
@@ -1,7 +1,7 @@
"use client"; "use client";
import Link from "next/link"; import Link from "next/link";
import { useStatements } from "@/lib/hooks"; import { useStatements, useParticipants, useUpdateStatement } from "@/lib/hooks";
function formatDate(d: string | null) { function formatDate(d: string | null) {
if (!d) return "—"; if (!d) return "—";
@@ -32,6 +32,8 @@ function formatAmount(n: number | null): string {
export default function StatementsPage() { export default function StatementsPage() {
const { data: statements, isLoading } = useStatements(); const { data: statements, isLoading } = useStatements();
const { data: participants } = useParticipants();
const updateStatement = useUpdateStatement();
return ( return (
<div> <div>
@@ -49,18 +51,25 @@ export default function StatementsPage() {
<th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Bank</th> <th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Bank</th>
<th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Account</th> <th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Account</th>
<th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Period</th> <th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Period</th>
<th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Due / Updated</th> <th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Due / End</th>
<th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Ccy</th> <th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Ccy</th>
<th className="text-right px-4 py-2.5 text-xs text-zinc-500 font-medium">Amount</th> <th className="text-right px-4 py-2.5 text-xs text-zinc-500 font-medium">Amount</th>
<th className="text-right px-4 py-2.5 text-xs text-zinc-500 font-medium">Txns</th> <th className="text-right px-4 py-2.5 text-xs text-zinc-500 font-medium">Txns</th>
<th className="text-left px-4 py-2.5 text-xs text-zinc-500 font-medium">Owner</th>
<th className="px-4 py-2.5"></th> <th className="px-4 py-2.5"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{statements.map((s) => { {statements.map((s) => {
const isCreditCard = Number(s.credit_limit) > 0 || s.payment_due_date != null; const isCreditCard = s.statement_type?.toLowerCase().includes("card") ?? false;
const displayAmount = isCreditCard ? s.total_amount_due : s.closing_balance; const displayAmount = isCreditCard ? s.total_amount_due : s.closing_balance;
const amountLabel = isCreditCard ? "due" : "balance"; const amount = Number(displayAmount);
// CC: red (you owe). Bank: green if positive balance, red if overdraft.
const amountColor = isCreditCard
? "text-red-400"
: amount >= 0
? "text-green-400"
: "text-red-400";
return ( return (
<tr key={s.id} className="border-b border-zinc-800/50 hover:bg-zinc-800/20 transition-colors"> <tr key={s.id} className="border-b border-zinc-800/50 hover:bg-zinc-800/20 transition-colors">
@@ -88,17 +97,35 @@ export default function StatementsPage() {
</td> </td>
<td className="px-4 py-3 text-right tabular-nums"> <td className="px-4 py-3 text-right tabular-nums">
{displayAmount !== null && displayAmount !== undefined ? ( {displayAmount !== null && displayAmount !== undefined ? (
<span className={isCreditCard ? "text-red-400" : "text-zinc-300"}> <span className={amountColor}>
{formatAmount(displayAmount)} {formatAmount(displayAmount)}
</span> </span>
) : ( ) : (
<span className="text-zinc-600"></span> <span className="text-zinc-600"></span>
)} )}
<div className="text-xs text-zinc-600">{amountLabel}</div>
</td> </td>
<td className="px-4 py-3 text-right text-zinc-500"> <td className="px-4 py-3 text-right text-zinc-500">
{s.transaction_count} {s.transaction_count}
</td> </td>
<td className="px-4 py-3">
{participants?.length ? (
<select
value={s.owner_id ?? ""}
onChange={(e) =>
updateStatement.mutate({ id: s.id, owner_id: Number(e.target.value) })
}
className="bg-zinc-800 border border-zinc-700 rounded text-xs px-2 py-1 text-zinc-300 cursor-pointer hover:border-zinc-600 focus:outline-none focus:border-indigo-500"
>
{participants.map((p) => (
<option key={p.id} value={p.id}>
{p.name}
</option>
))}
</select>
) : (
<span className="text-zinc-600 text-xs">{s.owner_name}</span>
)}
</td>
<td className="px-4 py-3"> <td className="px-4 py-3">
<Link <Link
href={`/transactions?statement_id=${s.id}`} href={`/transactions?statement_id=${s.id}`}
+1
View File
@@ -55,6 +55,7 @@ export interface StatementRow {
fees_charged: number | null; fees_charged: number | null;
credit_limit: number | null; credit_limit: number | null;
currency: string; currency: string;
statement_type: string | null;
tier_used: string | null; tier_used: string | null;
owner_id: number; owner_id: number;
owner_name: string; owner_name: string;