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:
parent
c9828d8ff3
commit
6d27eb62f8
@ -609,6 +609,50 @@ body.header-tone-light .app-header .tab.active {
|
|||||||
font-weight: 600;
|
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) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
*,
|
*,
|
||||||
*::before,
|
*::before,
|
||||||
|
|||||||
@ -78,13 +78,74 @@ foreach ($bankRows as $row) {
|
|||||||
}
|
}
|
||||||
$donatedByPersonThisMonth[$pid] = ($donatedByPersonThisMonth[$pid] ?? 0) + (float) ($row['amount'] ?? 0);
|
$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">
|
<div id="currency" class="tab-content">
|
||||||
<h2 class="mb-2"><?= htmlspecialchars($tabTitle, ENT_QUOTES, 'UTF-8') ?></h2>
|
<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>
|
<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="col-lg-5">
|
||||||
<div class="card h-100">
|
<div class="card h-100">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
@ -155,152 +216,130 @@ foreach ($bankRows as $row) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="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>
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<?php else: ?>
|
|
||||||
<input type="hidden" id="donation_goal_person" value="<?= htmlspecialchars((string) ($activePerson['id'] ?? ''), ENT_QUOTES, 'UTF-8') ?>">
|
|
||||||
<?php endif; ?>
|
<?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="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>
|
|
||||||
|
|
||||||
<?php if ($canRecordExpense): ?>
|
<div class="<?= $bankingMode ? '' : 'col-lg-7' ?>">
|
||||||
<div class="card border-secondary mb-4">
|
<?php if ($bankingMode): ?>
|
||||||
<div class="card-header">HoH checking credit/debit</div>
|
<div class="row g-4">
|
||||||
<div class="card-body">
|
<div class="col-lg-8">
|
||||||
<form id="bankAdjustForm" class="row g-3">
|
<div class="banking-hero mb-3">
|
||||||
<div class="col-md-4">
|
<h3 class="h5 mb-1">Account overview for <?= sanitizeInput((string) ($activeBankPerson['name'] ?? 'current person')) ?></h3>
|
||||||
<label class="form-label" for="bank_adjust_person">Person</label>
|
<p class="small text-muted mb-0">Track your accounts and move money with one tap.</p>
|
||||||
<select class="form-select" id="bank_adjust_person" required>
|
</div>
|
||||||
<?php foreach ($people as $p): ?>
|
<div class="row g-3 mb-4">
|
||||||
<?php if (empty($p['id'])) { continue; } ?>
|
<div class="col-sm-6">
|
||||||
<option value="<?= htmlspecialchars((string) $p['id'], ENT_QUOTES, 'UTF-8') ?>"><?= sanitizeInput($p['name'] ?? '') ?></option>
|
<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; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<?php endif; ?>
|
||||||
<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>
|
||||||
<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>
|
||||||
<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>
|
||||||
<div class="col-md-8">
|
<div class="col-lg-4">
|
||||||
<label class="form-label" for="bank_adjust_note">Note</label>
|
<div class="accordion" id="bankingActionsAccordion">
|
||||||
<input class="form-control" id="bank_adjust_note" maxlength="200">
|
<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>
|
||||||
<div class="col-12"><button type="submit" class="btn btn-danger">Save checking transaction</button></div>
|
<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>
|
||||||
|
<?php if ($canRecordExpense): ?>
|
||||||
|
<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>
|
<div class="col-12"><div class="alert d-none" id="bankAdjustFeedback" role="status"></div></div>
|
||||||
</form>
|
</form>
|
||||||
|
</div></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="accordion-item">
|
||||||
<div class="card border-secondary mb-4">
|
<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 class="card-header">HoH charity outflow</div>
|
<div id="bankActionCharity" class="accordion-collapse collapse" data-bs-parent="#bankingActionsAccordion"><div class="accordion-body">
|
||||||
<div class="card-body">
|
|
||||||
<form id="charityOutflowForm" class="row g-3">
|
<form id="charityOutflowForm" class="row g-3">
|
||||||
<div class="col-md-4">
|
<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>
|
||||||
<label class="form-label" for="charity_outflow_person">Person</label>
|
<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>
|
||||||
<select class="form-select" id="charity_outflow_person" required>
|
<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>
|
||||||
<?php foreach ($people as $p): ?>
|
<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>
|
||||||
<?php if (empty($p['id'])) { continue; } ?>
|
<div class="col-12"><button type="submit" class="btn btn-warning w-100">Log charity outflow</button></div>
|
||||||
<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>
|
<div class="col-12"><div class="alert d-none" id="charityOutflowFeedback" role="status"></div></div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div></div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<?php elseif ($canRecordExpense): ?>
|
<?php elseif ($canRecordExpense): ?>
|
||||||
<div class="card border-secondary mb-4">
|
<div class="card border-secondary mb-4">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
@ -353,60 +392,24 @@ foreach ($bankRows as $row) {
|
|||||||
<p class="text-muted small">Switch to a verified Head of household to record expenses.</p>
|
<p class="text-muted small">Switch to a verified Head of household to record expenses.</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!$bankingMode): ?>
|
||||||
<div class="card">
|
<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">
|
<div class="card-body p-0">
|
||||||
<?php if ($bankingMode && count($recentBank) === 0): ?>
|
<?php if (count($recentExpenses) === 0): ?>
|
||||||
<p class="p-3 text-muted mb-0">No bank transactions recorded yet.</p>
|
|
||||||
<?php elseif (!$bankingMode && count($recentExpenses) === 0): ?>
|
|
||||||
<p class="p-3 text-muted mb-0">No expenses recorded yet.</p>
|
<p class="p-3 text-muted mb-0">No expenses recorded yet.</p>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-sm mb-0">
|
<table class="table table-sm mb-0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<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>Date</th>
|
||||||
<th>Title</th>
|
<th>Title</th>
|
||||||
<th>To</th>
|
<th>To</th>
|
||||||
<th class="text-end">Amount</th>
|
<th class="text-end">Amount</th>
|
||||||
<?php endif; ?>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<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): ?>
|
<?php foreach ($recentExpenses as $ex): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-nowrap"><?= sanitizeInput((string) ($ex['date'] ?? '')) ?></td>
|
<td class="text-nowrap"><?= sanitizeInput((string) ($ex['date'] ?? '')) ?></td>
|
||||||
@ -415,25 +418,17 @@ foreach ($bankRows as $row) {
|
|||||||
<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>
|
<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>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php endif; ?>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php if ($bankingMode): ?>
|
<?php endif; ?>
|
||||||
<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>
|
</div>
|
||||||
|
<?php if ($bankingMode): ?>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user