Add banking overview UI and enhance currency tab functionality

- Introduced a new banking overview section with account details for checking, savings, and charity balances.
- Implemented a tabbed interface for the currency section, allowing users to switch between leaderboard and banking views.
- Enhanced user experience with animations and improved layout for better visibility of account information.
- Maintained existing functionality while integrating new banking features seamlessly.
This commit is contained in:
Louis Whittington 2026-03-31 11:45:46 -05:00
parent c9828d8ff3
commit 6d27eb62f8
2 changed files with 238 additions and 199 deletions

View File

@ -609,6 +609,50 @@ body.header-tone-light .app-header .tab.active {
font-weight: 600;
}
.banking-hero {
border-radius: 0.8rem;
padding: 0.9rem 1rem;
background: linear-gradient(135deg, rgba(74, 144, 226, 0.12), rgba(74, 226, 168, 0.1));
border: 1px solid rgba(74, 144, 226, 0.2);
}
.banking-overview-card {
overflow: hidden;
position: relative;
}
@media (prefers-reduced-motion: no-preference) {
.banking-overview-card {
animation: banking-card-in 0.55s var(--fh-ease-out) both;
}
.banking-overview-card:nth-child(1) { animation-delay: 0.04s; }
.banking-overview-card:nth-child(2) { animation-delay: 0.1s; }
.banking-overview-card:nth-child(3) { animation-delay: 0.16s; }
.banking-overview-card:nth-child(4) { animation-delay: 0.22s; }
}
.banking-overview-card::after {
content: "";
position: absolute;
inset: auto -20% -45% auto;
width: 140px;
height: 140px;
border-radius: 50%;
opacity: 0.2;
pointer-events: none;
}
.banking-overview-checking::after { background: #4a90e2; }
.banking-overview-savings::after { background: #2fbf71; }
.banking-overview-charity::after { background: #8f65ff; }
.banking-overview-donated::after { background: #f2994a; }
@keyframes banking-card-in {
from { opacity: 0; transform: translateY(10px) scale(0.98); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,

View File

@ -78,13 +78,74 @@ foreach ($bankRows as $row) {
}
$donatedByPersonThisMonth[$pid] = ($donatedByPersonThisMonth[$pid] ?? 0) + (float) ($row['amount'] ?? 0);
}
$activePersonId = (string) ($activePerson['id'] ?? '');
$activeBankPerson = null;
foreach ($people as $p) {
if (($p['id'] ?? '') === $activePersonId) {
$activeBankPerson = $p;
break;
}
}
$activeChecking = is_numeric($activeBankPerson['checking_balance'] ?? null) ? (float) $activeBankPerson['checking_balance'] : 0.0;
$activeSavings = is_numeric($activeBankPerson['savings_balance'] ?? null) ? (float) $activeBankPerson['savings_balance'] : 0.0;
$activeCharityPending = is_numeric($activeBankPerson['charity_pending_balance'] ?? null) ? (float) $activeBankPerson['charity_pending_balance'] : 0.0;
$activeCharityDonated = is_numeric($activeBankPerson['charity_donated_total'] ?? null) ? (float) $activeBankPerson['charity_donated_total'] : 0.0;
?>
<div id="currency" class="tab-content">
<h2 class="mb-2"><?= htmlspecialchars($tabTitle, ENT_QUOTES, 'UTF-8') ?></h2>
<p class="text-muted small mb-4">Leaderboard and expenses use the family currency from Family settings.</p>
<?php if ($bankingMode): ?>
<ul class="nav nav-pills mb-3 gap-2" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="pill" data-bs-target="#currencyLeaderboardPane" type="button" role="tab" aria-selected="true">
<i class="fa fa-trophy me-1"></i>Leaderboard
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="pill" data-bs-target="#currencyBankingPane" type="button" role="tab" aria-selected="false">
<i class="fa fa-building-columns me-1"></i>Banking
</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="currencyLeaderboardPane" role="tabpanel" tabindex="0">
<div class="card mb-4">
<div class="card-header"><i class="fa fa-trophy me-1"></i>Total stars leaderboard</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped mb-0 leaderboard-table">
<thead>
<tr>
<th scope="col" class="text-muted">#</th>
<th scope="col">Name</th>
<th scope="col" class="text-end">Total <?= htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($leaderboard as $rank => $p): ?>
<?php
$pid = (string) ($p['id'] ?? '');
$total = is_numeric($p['checking_balance'] ?? null) ? (float) $p['checking_balance'] : (is_numeric($p['currency_balance'] ?? null) ? (float) $p['currency_balance'] : 0.0);
$isSelf = $activePerson && ($activePerson['id'] ?? '') === $pid;
?>
<tr class="<?= $isSelf ? 'table-primary' : '' ?>">
<td class="text-muted"><?= (int) $rank + 1 ?></td>
<td><?= sanitizeInput($p['name'] ?? '') ?><?php if ($isSelf): ?> <span class="badge text-bg-info">You</span><?php endif; ?></td>
<td class="text-end text-nowrap"><strong><?= htmlspecialchars(number_format($total, 2, '.', ''), ENT_QUOTES, 'UTF-8') ?></strong> <?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="currencyBankingPane" role="tabpanel" tabindex="0">
<?php endif; ?>
<div class="row g-4">
<div class="<?= $bankingMode ? '' : 'row g-4' ?>">
<?php if (!$bankingMode): ?>
<div class="col-lg-5">
<div class="card h-100">
<div class="card-header">
@ -155,152 +216,130 @@ foreach ($bankRows as $row) {
</div>
</div>
</div>
<?php endif; ?>
<div class="col-lg-7">
<div class="<?= $bankingMode ? '' : 'col-lg-7' ?>">
<?php if ($bankingMode): ?>
<div class="card border-secondary mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="fa fa-arrow-right-arrow-left me-1"></i> Move your funds</span>
<div class="row g-4">
<div class="col-lg-8">
<div class="banking-hero mb-3">
<h3 class="h5 mb-1">Account overview for <?= sanitizeInput((string) ($activeBankPerson['name'] ?? 'current person')) ?></h3>
<p class="small text-muted mb-0">Track your accounts and move money with one tap.</p>
</div>
<div class="row g-3 mb-4">
<div class="col-sm-6">
<div class="card banking-overview-card banking-overview-checking"><div class="card-body"><div class="small text-muted">Checking</div><div class="h4 mb-0"><?= htmlspecialchars(number_format($activeChecking, 2, '.', ''), ENT_QUOTES, 'UTF-8') ?> <?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?></div></div></div>
</div>
<div class="col-sm-6">
<div class="card banking-overview-card banking-overview-savings"><div class="card-body"><div class="small text-muted">Savings</div><div class="h4 mb-0"><?= htmlspecialchars(number_format($activeSavings, 2, '.', ''), ENT_QUOTES, 'UTF-8') ?> <?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?></div></div></div>
</div>
<div class="col-sm-6">
<div class="card banking-overview-card banking-overview-charity"><div class="card-body"><div class="small text-muted">Charity pending</div><div class="h4 mb-0"><?= htmlspecialchars(number_format($activeCharityPending, 2, '.', ''), ENT_QUOTES, 'UTF-8') ?> <?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?></div></div></div>
</div>
<div class="col-sm-6">
<div class="card banking-overview-card banking-overview-donated"><div class="card-body"><div class="small text-muted">Donated total</div><div class="h4 mb-0"><?= htmlspecialchars(number_format($activeCharityDonated, 2, '.', ''), ENT_QUOTES, 'UTF-8') ?> <?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?></div></div></div>
</div>
</div>
<div class="card">
<div class="card-header">Recent bank transactions</div>
<div class="card-body p-0">
<?php if (count($recentBank) === 0): ?>
<p class="p-3 text-muted mb-0">No bank transactions recorded yet.</p>
<?php else: ?>
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead>
<tr><th>Date</th><th>Person</th><th>Type</th><th>Category</th><th>Note</th><th class="text-end">Amount</th><?php if ($canRecordExpense): ?><th></th><?php endif; ?></tr>
</thead>
<tbody>
<?php foreach ($recentBank as $row): ?>
<?php $pid = (string) ($row['person_id'] ?? ''); $rawAmt = (float) ($row['amount'] ?? 0); $type = (string) ($row['type'] ?? ''); $amtSign = in_array($type, ['manual_debit', 'charity_outflow'], true) ? '' : '+'; ?>
<tr>
<td class="text-nowrap"><?= sanitizeInput((string) ($row['created_at'] ?? '')) ?></td>
<td><?= sanitizeInput($nameById[$pid] ?? '') ?></td>
<td><?= sanitizeInput($type) ?></td>
<td><?= sanitizeInput((string) ($row['category'] ?? '')) ?></td>
<td><?= sanitizeInput((string) ($row['note'] ?? '')) ?></td>
<td class="text-end text-nowrap"><?= $amtSign ?><?= htmlspecialchars(number_format($rawAmt, 2, '.', ''), ENT_QUOTES, 'UTF-8') ?> <?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?></td>
<?php if ($canRecordExpense): ?><td class="text-end"><?php if (empty($row['reversed_by_transaction_id']) && ($row['type'] ?? '') !== 'reversal'): ?><button type="button" class="btn btn-sm btn-outline-danger btn-bank-reverse" data-id="<?= htmlspecialchars((string) ($row['id'] ?? ''), ENT_QUOTES, 'UTF-8') ?>">Reverse</button><?php endif; ?></td><?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="card-body">
<form id="bankTransferForm" class="row g-3">
<div class="col-md-4">
<label class="form-label" for="transfer_from_account">From</label>
<select class="form-select" id="transfer_from_account" required>
<option value="checking">Checking</option>
<option value="savings">Savings</option>
</select>
<div class="col-lg-4">
<div class="accordion" id="bankingActionsAccordion">
<div class="accordion-item">
<h2 class="accordion-header"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#bankActionTransfer">Move funds</button></h2>
<div id="bankActionTransfer" class="accordion-collapse collapse" data-bs-parent="#bankingActionsAccordion"><div class="accordion-body">
<form id="bankTransferForm" class="row g-3">
<div class="col-6"><label class="form-label" for="transfer_from_account">From</label><select class="form-select" id="transfer_from_account" required><option value="checking">Checking</option><option value="savings">Savings</option></select></div>
<div class="col-6"><label class="form-label" for="transfer_to_account">To</label><select class="form-select" id="transfer_to_account" required><option value="savings">Savings</option><option value="checking">Checking</option><option value="charity">Charity (irreversible)</option></select></div>
<div class="col-12"><label class="form-label" for="transfer_amount">Amount (<?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?>)</label><input type="number" step="0.01" min="0.01" class="form-control" id="transfer_amount" required></div>
<div class="col-12"><label class="form-label" for="transfer_category">Category</label><input class="form-control" id="transfer_category" maxlength="50" value="transfer"></div>
<div class="col-12"><label class="form-label" for="transfer_note">Note</label><input class="form-control" id="transfer_note" maxlength="200" placeholder="Optional note"></div>
<div class="col-12"><button type="submit" class="btn btn-primary w-100">Submit transfer</button></div>
<div class="col-12"><div class="alert d-none" id="bankTransferFeedback" role="status"></div></div>
</form>
</div></div>
</div>
<div class="col-md-4">
<label class="form-label" for="transfer_to_account">To</label>
<select class="form-select" id="transfer_to_account" required>
<option value="savings">Savings</option>
<option value="checking">Checking</option>
<option value="charity">Charity (irreversible)</option>
</select>
<div class="accordion-item">
<h2 class="accordion-header"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#bankActionGoal">Donation goal</button></h2>
<div id="bankActionGoal" class="accordion-collapse collapse" data-bs-parent="#bankingActionsAccordion"><div class="accordion-body">
<form id="donationGoalForm" class="row g-3">
<?php if ($canRecordExpense): ?><div class="col-12"><label class="form-label" for="donation_goal_person">Person</label><select class="form-select" id="donation_goal_person"><?php foreach ($people as $p): ?><?php if (empty($p['id'])) { continue; } ?><option value="<?= htmlspecialchars((string) $p['id'], ENT_QUOTES, 'UTF-8') ?>" <?= (($activePerson['id'] ?? '') === ($p['id'] ?? '')) ? 'selected' : '' ?>><?= sanitizeInput($p['name'] ?? '') ?></option><?php endforeach; ?></select></div><?php else: ?><input type="hidden" id="donation_goal_person" value="<?= htmlspecialchars((string) ($activePerson['id'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"><?php endif; ?>
<div class="col-12"><label class="form-label" for="donation_goal_monthly">Goal for month (<?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?>)</label><input type="number" step="0.01" min="0" class="form-control" id="donation_goal_monthly" value="<?= htmlspecialchars(number_format((float) ($activePerson['donation_goal_monthly'] ?? 0), 2, '.', ''), ENT_QUOTES, 'UTF-8') ?>"></div>
<div class="col-12"><button type="submit" class="btn btn-outline-primary w-100">Save donation goal</button></div>
<div class="col-12"><div class="alert d-none" id="donationGoalFeedback" role="status"></div></div>
</form>
</div></div>
</div>
<div class="col-md-4">
<label class="form-label" for="transfer_amount">Amount (<?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?>)</label>
<input type="number" step="0.01" min="0.01" class="form-control" id="transfer_amount" required>
</div>
<div class="col-md-4">
<label class="form-label" for="transfer_category">Category</label>
<input class="form-control" id="transfer_category" maxlength="50" value="transfer">
</div>
<div class="col-md-8">
<label class="form-label" for="transfer_note">Note</label>
<input class="form-control" id="transfer_note" maxlength="200" placeholder="Optional note">
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">Submit transfer</button>
</div>
<div class="col-12">
<div class="alert d-none" id="bankTransferFeedback" role="status"></div>
</div>
</form>
</div>
</div>
<div class="card border-secondary mb-4">
<div class="card-header"><i class="fa fa-heart me-1"></i> Monthly donation goal</div>
<div class="card-body">
<form id="donationGoalForm" class="row g-3">
<?php if ($canRecordExpense): ?>
<div class="col-md-6">
<label class="form-label" for="donation_goal_person">Person</label>
<select class="form-select" id="donation_goal_person">
<?php foreach ($people as $p): ?>
<?php if (empty($p['id'])) { continue; } ?>
<option value="<?= htmlspecialchars((string) $p['id'], ENT_QUOTES, 'UTF-8') ?>" <?= (($activePerson['id'] ?? '') === ($p['id'] ?? '')) ? 'selected' : '' ?>><?= sanitizeInput($p['name'] ?? '') ?></option>
<?php endforeach; ?>
</select>
<div class="accordion-item">
<h2 class="accordion-header"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#bankActionAdjust">HoH checking credit/debit</button></h2>
<div id="bankActionAdjust" class="accordion-collapse collapse" data-bs-parent="#bankingActionsAccordion"><div class="accordion-body">
<form id="bankAdjustForm" class="row g-3">
<div class="col-12"><label class="form-label" for="bank_adjust_person">Person</label><select class="form-select" id="bank_adjust_person" required><?php foreach ($people as $p): ?><?php if (empty($p['id'])) { continue; } ?><option value="<?= htmlspecialchars((string) $p['id'], ENT_QUOTES, 'UTF-8') ?>"><?= sanitizeInput($p['name'] ?? '') ?></option><?php endforeach; ?></select></div>
<div class="col-6"><label class="form-label" for="bank_adjust_type">Type</label><select class="form-select" id="bank_adjust_type"><option value="credit">Credit</option><option value="debit">Debit</option></select></div>
<div class="col-6"><label class="form-label" for="bank_adjust_amount">Amount</label><input type="number" min="0.01" step="0.01" class="form-control" id="bank_adjust_amount" required></div>
<div class="col-12"><label class="form-label" for="bank_adjust_category">Category</label><input class="form-control" id="bank_adjust_category" maxlength="50" value="manual"></div>
<div class="col-12"><label class="form-label" for="bank_adjust_note">Note</label><input class="form-control" id="bank_adjust_note" maxlength="200"></div>
<div class="col-12"><button type="submit" class="btn btn-danger w-100">Save checking transaction</button></div>
<div class="col-12"><div class="alert d-none" id="bankAdjustFeedback" role="status"></div></div>
</form>
</div></div>
</div>
<div class="accordion-item">
<h2 class="accordion-header"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#bankActionCharity">HoH charity outflow</button></h2>
<div id="bankActionCharity" class="accordion-collapse collapse" data-bs-parent="#bankingActionsAccordion"><div class="accordion-body">
<form id="charityOutflowForm" class="row g-3">
<div class="col-12"><label class="form-label" for="charity_outflow_person">Person</label><select class="form-select" id="charity_outflow_person" required><?php foreach ($people as $p): ?><?php if (empty($p['id'])) { continue; } ?><option value="<?= htmlspecialchars((string) $p['id'], ENT_QUOTES, 'UTF-8') ?>"><?= sanitizeInput($p['name'] ?? '') ?></option><?php endforeach; ?></select></div>
<div class="col-6"><label class="form-label" for="charity_outflow_amount">Amount</label><input type="number" min="0.01" step="0.01" class="form-control" id="charity_outflow_amount" required></div>
<div class="col-6"><label class="form-label" for="charity_outflow_category">Category</label><input class="form-control" id="charity_outflow_category" maxlength="50" value="donation"></div>
<div class="col-12"><label class="form-label" for="charity_outflow_note">Note</label><input class="form-control" id="charity_outflow_note" maxlength="200"></div>
<div class="col-12"><button type="submit" class="btn btn-warning w-100">Log charity outflow</button></div>
<div class="col-12"><div class="alert d-none" id="charityOutflowFeedback" role="status"></div></div>
</form>
</div></div>
</div>
<?php else: ?>
<input type="hidden" id="donation_goal_person" value="<?= htmlspecialchars((string) ($activePerson['id'] ?? ''), ENT_QUOTES, 'UTF-8') ?>">
<?php endif; ?>
<div class="<?= $canRecordExpense ? 'col-md-6' : 'col-md-12' ?>">
<label class="form-label" for="donation_goal_monthly">Goal for month (<?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?>)</label>
<input type="number" step="0.01" min="0" class="form-control" id="donation_goal_monthly" value="<?= htmlspecialchars(number_format((float) ($activePerson['donation_goal_monthly'] ?? 0), 2, '.', ''), ENT_QUOTES, 'UTF-8') ?>">
</div>
<div class="card mt-3">
<div class="card-header">Monthly statements</div>
<div class="card-body">
<p class="small text-muted mb-2">Download your monthly statement as JSON or CSV.</p>
<div class="d-flex flex-wrap gap-2">
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($familyHubApiBase, ENT_QUOTES, 'UTF-8') ?>/bank_statement.php?month=<?= htmlspecialchars($currentMonth, ENT_QUOTES, 'UTF-8') ?>&format=json">Current month JSON</a>
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($familyHubApiBase, ENT_QUOTES, 'UTF-8') ?>/bank_statement.php?month=<?= htmlspecialchars($currentMonth, ENT_QUOTES, 'UTF-8') ?>&format=csv">Current month CSV</a>
</div>
</div>
<div class="col-12">
<button type="submit" class="btn btn-outline-primary">Save donation goal</button>
</div>
<div class="col-12">
<div class="alert d-none" id="donationGoalFeedback" role="status"></div>
</div>
</form>
</div>
</div>
</div>
<?php if ($canRecordExpense): ?>
<div class="card border-secondary mb-4">
<div class="card-header">HoH checking credit/debit</div>
<div class="card-body">
<form id="bankAdjustForm" class="row g-3">
<div class="col-md-4">
<label class="form-label" for="bank_adjust_person">Person</label>
<select class="form-select" id="bank_adjust_person" required>
<?php foreach ($people as $p): ?>
<?php if (empty($p['id'])) { continue; } ?>
<option value="<?= htmlspecialchars((string) $p['id'], ENT_QUOTES, 'UTF-8') ?>"><?= sanitizeInput($p['name'] ?? '') ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label" for="bank_adjust_type">Type</label>
<select class="form-select" id="bank_adjust_type">
<option value="credit">Credit</option>
<option value="debit">Debit</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label" for="bank_adjust_amount">Amount</label>
<input type="number" min="0.01" step="0.01" class="form-control" id="bank_adjust_amount" required>
</div>
<div class="col-md-4">
<label class="form-label" for="bank_adjust_category">Category</label>
<input class="form-control" id="bank_adjust_category" maxlength="50" value="manual">
</div>
<div class="col-md-8">
<label class="form-label" for="bank_adjust_note">Note</label>
<input class="form-control" id="bank_adjust_note" maxlength="200">
</div>
<div class="col-12"><button type="submit" class="btn btn-danger">Save checking transaction</button></div>
<div class="col-12"><div class="alert d-none" id="bankAdjustFeedback" role="status"></div></div>
</form>
</div>
</div>
<div class="card border-secondary mb-4">
<div class="card-header">HoH charity outflow</div>
<div class="card-body">
<form id="charityOutflowForm" class="row g-3">
<div class="col-md-4">
<label class="form-label" for="charity_outflow_person">Person</label>
<select class="form-select" id="charity_outflow_person" required>
<?php foreach ($people as $p): ?>
<?php if (empty($p['id'])) { continue; } ?>
<option value="<?= htmlspecialchars((string) $p['id'], ENT_QUOTES, 'UTF-8') ?>"><?= sanitizeInput($p['name'] ?? '') ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label" for="charity_outflow_amount">Amount</label>
<input type="number" min="0.01" step="0.01" class="form-control" id="charity_outflow_amount" required>
</div>
<div class="col-md-4">
<label class="form-label" for="charity_outflow_category">Category</label>
<input class="form-control" id="charity_outflow_category" maxlength="50" value="donation">
</div>
<div class="col-md-12">
<label class="form-label" for="charity_outflow_note">Note</label>
<input class="form-control" id="charity_outflow_note" maxlength="200">
</div>
<div class="col-12"><button type="submit" class="btn btn-warning">Log charity outflow</button></div>
<div class="col-12"><div class="alert d-none" id="charityOutflowFeedback" role="status"></div></div>
</form>
</div>
</div>
<?php endif; ?>
<?php elseif ($canRecordExpense): ?>
<div class="card border-secondary mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
@ -353,87 +392,43 @@ foreach ($bankRows as $row) {
<p class="text-muted small">Switch to a verified Head of household to record expenses.</p>
<?php endif; ?>
<?php if (!$bankingMode): ?>
<div class="card">
<div class="card-header"><?= $bankingMode ? 'Recent bank transactions' : 'Recent expenses' ?></div>
<div class="card-header">Recent expenses</div>
<div class="card-body p-0">
<?php if ($bankingMode && count($recentBank) === 0): ?>
<p class="p-3 text-muted mb-0">No bank transactions recorded yet.</p>
<?php elseif (!$bankingMode && count($recentExpenses) === 0): ?>
<?php if (count($recentExpenses) === 0): ?>
<p class="p-3 text-muted mb-0">No expenses recorded yet.</p>
<?php else: ?>
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead>
<tr>
<?php if ($bankingMode): ?>
<th>Date</th>
<th>Person</th>
<th>Type</th>
<th>Category</th>
<th>Note</th>
<th class="text-end">Amount</th>
<?php if ($canRecordExpense): ?><th></th><?php endif; ?>
<?php else: ?>
<th>Date</th>
<th>Title</th>
<th>To</th>
<th class="text-end">Amount</th>
<?php endif; ?>
<th>Date</th>
<th>Title</th>
<th>To</th>
<th class="text-end">Amount</th>
</tr>
</thead>
<tbody>
<?php if ($bankingMode): ?>
<?php foreach ($recentBank as $row): ?>
<?php
$pid = (string) ($row['person_id'] ?? '');
$rawAmt = (float) ($row['amount'] ?? 0);
$type = (string) ($row['type'] ?? '');
$amtSign = in_array($type, ['manual_debit', 'charity_outflow'], true) ? '' : '+';
?>
<tr>
<td class="text-nowrap"><?= sanitizeInput((string) ($row['created_at'] ?? '')) ?></td>
<td><?= sanitizeInput($nameById[$pid] ?? '') ?></td>
<td><?= sanitizeInput($type) ?></td>
<td><?= sanitizeInput((string) ($row['category'] ?? '')) ?></td>
<td><?= sanitizeInput((string) ($row['note'] ?? '')) ?></td>
<td class="text-end text-nowrap"><?= $amtSign ?><?= htmlspecialchars(number_format($rawAmt, 2, '.', ''), ENT_QUOTES, 'UTF-8') ?> <?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?></td>
<?php if ($canRecordExpense): ?>
<td class="text-end">
<?php if (empty($row['reversed_by_transaction_id']) && ($row['type'] ?? '') !== 'reversal'): ?>
<button type="button" class="btn btn-sm btn-outline-danger btn-bank-reverse" data-id="<?= htmlspecialchars((string) ($row['id'] ?? ''), ENT_QUOTES, 'UTF-8') ?>">Reverse</button>
<?php endif; ?>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
<?php else: ?>
<?php foreach ($recentExpenses as $ex): ?>
<tr>
<td class="text-nowrap"><?= sanitizeInput((string) ($ex['date'] ?? '')) ?></td>
<td><?= sanitizeInput((string) ($ex['title'] ?? '')) ?></td>
<td><?= sanitizeInput($nameById[(string) ($ex['assignee_id'] ?? '')] ?? '') ?></td>
<td class="text-end text-nowrap"><?= htmlspecialchars(number_format((float) ($ex['value'] ?? 0), 2, '.', ''), ENT_QUOTES, 'UTF-8') ?> <?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
<?php foreach ($recentExpenses as $ex): ?>
<tr>
<td class="text-nowrap"><?= sanitizeInput((string) ($ex['date'] ?? '')) ?></td>
<td><?= sanitizeInput((string) ($ex['title'] ?? '')) ?></td>
<td><?= sanitizeInput($nameById[(string) ($ex['assignee_id'] ?? '')] ?? '') ?></td>
<td class="text-end text-nowrap"><?= htmlspecialchars(number_format((float) ($ex['value'] ?? 0), 2, '.', ''), ENT_QUOTES, 'UTF-8') ?> <?= htmlspecialchars($sym, ENT_QUOTES, 'UTF-8') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
<?php if ($bankingMode): ?>
<div class="card mt-4">
<div class="card-header">Monthly statements</div>
<div class="card-body">
<p class="small text-muted mb-2">Download your monthly statement as JSON or CSV.</p>
<div class="d-flex flex-wrap gap-2">
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($familyHubApiBase, ENT_QUOTES, 'UTF-8') ?>/bank_statement.php?month=<?= htmlspecialchars($currentMonth, ENT_QUOTES, 'UTF-8') ?>&format=json">Current month JSON</a>
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($familyHubApiBase, ENT_QUOTES, 'UTF-8') ?>/bank_statement.php?month=<?= htmlspecialchars($currentMonth, ENT_QUOTES, 'UTF-8') ?>&format=csv">Current month CSV</a>
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
<?php if ($bankingMode): ?>
</div>
</div>
<?php endif; ?>
</div>