Files
budget/views/dashboard.php
T
Ty Clifford b7eaa81501 -
2026-06-14 15:25:31 -04:00

145 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
$remainingTone = $summary['remaining'] >= 0 ? 'green' : 'red';
$progress = $summary['gross_resources'] > 0 ? min(100, max(0, $summary['expenses'] / $summary['gross_resources'] * 100)) : 0;
?>
<section class="hero-grid">
<article class="hero-balance card glow-purple">
<div>
<p class="eyebrow"><?= h(date('F Y', strtotime($month . '-01'))) ?> remaining</p>
<div class="hero-amount <?= h($remainingTone) ?>"><?= h(Support::money($summary['remaining'], $symbol)) ?></div>
<p class="muted"><?= number_format($progress, 1) ?>% of available resources spent</p>
</div>
<div class="balance-ring" style="--progress:<?= h((string) $progress) ?>">
<span><?= number_format(100 - $progress, 0) ?>%</span><small>left</small>
</div>
</article>
<article class="metric-card card">
<div class="metric-icon blue">↙</div><p>Gross resources</p><strong><?= h(Support::money($summary['gross_resources'], $symbol)) ?></strong>
<small><?= h(Support::money($summary['starting_income'], $symbol)) ?> start + <?= h(Support::money($summary['rollover_in'], $symbol)) ?> rollover</small>
</article>
<article class="metric-card card">
<div class="metric-icon pink">↗</div><p>Expenses</p><strong><?= h(Support::money($summary['expenses'], $symbol)) ?></strong>
<small><?= count($items) ?> defined budget item<?= count($items) === 1 ? '' : 's' ?></small>
</article>
<article class="metric-card card">
<div class="metric-icon orange">◇</div><p>Dormant balance</p>
<strong><?= h(Support::money($deadBalance, $symbol)) ?></strong>
<small><?= number_format($summary['savings_rate'], 1) ?>% of this month routed away</small>
</article>
</section>
<section class="content-grid dashboard-grid">
<div class="span-2 stack">
<article class="card">
<div class="card-heading">
<div><p class="eyebrow">Progressional tracking</p><h2>Budget targets</h2></div>
<button class="button small" type="button" data-dialog-open="item-dialog">+ Add item</button>
</div>
<?php if ($items === []): ?>
<div class="empty-state"><strong>No targets yet</strong><p>Add a bill or category target to begin progress tracking.</p></div>
<?php else: ?>
<div class="budget-list">
<?php foreach ($items as $item): ?>
<div class="budget-row">
<span class="category-dot" style="--dot:<?= h($item['category_color'] ?: '#718096') ?>"></span>
<div class="budget-main">
<div class="budget-title">
<strong><?= h($item['name']) ?></strong>
<span><?= h($item['category_name'] ?: 'Uncategorized') ?><?= $item['due_date'] ? ' · due ' . h(date('M j', strtotime($item['due_date']))) : '' ?></span>
</div>
<div class="progress-track"><span style="width:<?= h((string) $item['progress']) ?>%;--bar:<?= h($item['category_color'] ?: '#7c5cff') ?>"></span></div>
<div class="budget-meta">
<span><?= h(Support::money($item['paid_amount'], $symbol)) ?> paid</span>
<span><?= h(Support::money($item['remaining_amount'], $symbol)) ?> owing</span>
</div>
</div>
<strong class="target"><?= h(Support::money($item['planned_amount'], $symbol)) ?></strong>
<form method="post" data-confirm="Remove this budget item? Existing expenses will remain.">
<?= csrf_field() ?><input type="hidden" name="action" value="item.delete"><input type="hidden" name="month" value="<?= h($month) ?>"><input type="hidden" name="id" value="<?= (int) $item['id'] ?>">
<button class="icon-button danger" title="Delete item" type="submit">×</button>
</form>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</article>
<article class="card">
<div class="card-heading">
<div><p class="eyebrow">Comparative view</p><h2>Six-month movement</h2></div>
<a class="text-link" href="<?= h(url('comparisons', ['month' => $month])) ?>">Full analysis →</a>
</div>
<canvas class="trend-chart" data-trend-chart='<?= h(json_encode($comparison)) ?>' data-symbol="<?= h($symbol) ?>" height="220"></canvas>
</article>
</div>
<div class="stack">
<article class="card accent-card">
<div class="card-heading"><div><p class="eyebrow">Monthly inputs</p><h2>Starting point</h2></div></div>
<form method="post" class="stack-form compact">
<?= csrf_field() ?><input type="hidden" name="action" value="plan.save"><input type="hidden" name="month" value="<?= h($month) ?>">
<label>Manual starting income<div class="money-input"><span><?= h($symbol) ?></span><input type="number" name="starting_income" min="0" step="0.01" value="<?= h((string) $plan['starting_income']) ?>"></div></label>
<label>Previous-month remainder<div class="money-input"><span><?= h($symbol) ?></span><input type="number" name="rollover_in" min="0" step="0.01" value="<?= h((string) $plan['rollover_in']) ?>"></div></label>
<label class="check-row slim"><input type="checkbox" name="rolling_enabled" value="1" <?= $plan['rolling_enabled'] ? 'checked' : '' ?>><span>Carry positive remainder forward</span></label>
<label>Monthly note<textarea name="notes" rows="2" placeholder="What matters this month?"><?= h($plan['notes']) ?></textarea></label>
<button class="button primary" type="submit">Save starting point</button>
</form>
<form method="post" class="inline-form top-gap">
<?= csrf_field() ?><input type="hidden" name="action" value="plan.refresh_rollover"><input type="hidden" name="month" value="<?= h($month) ?>">
<button class="text-link" type="submit">↻ Recalculate from <?= h(date('F', strtotime($month . '-01 -1 month'))) ?></button>
</form>
</article>
<?php if ($settings->module('income')): ?>
<article class="card">
<div class="card-heading"><div><p class="eyebrow">Income stream</p><h2>Additional income</h2></div><button class="button small secondary" type="button" data-dialog-open="income-dialog">+ Add</button></div>
<?php if ($incomes === []): ?>
<p class="muted">No additional income recorded.</p>
<?php else: ?>
<div class="mini-list">
<?php foreach (array_slice($incomes, 0, 5) as $income): ?>
<div><span><strong><?= h($income['label']) ?></strong><small><?= h(date('M j', strtotime($income['received_on']))) ?> · <?= h(Support::money($income['withholding_amount'], $symbol)) ?> withheld</small></span><strong class="green">+<?= h(Support::money($income['amount'], $symbol)) ?></strong></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</article>
<?php endif; ?>
<article class="card stats-card">
<div class="card-heading"><div><p class="eyebrow">Usage stats</p><h2>Planner activity</h2></div></div>
<div class="stat-trio"><div><strong><?= (int) $visitorStats['today'] ?></strong><span>today</span></div><div><strong><?= (int) $visitorStats['thirty_days'] ?></strong><span>30-day visitors</span></div><div><strong><?= (int) $visitorStats['views'] ?></strong><span>page views</span></div></div>
</article>
</div>
</section>
<dialog id="item-dialog">
<form method="post" class="dialog-card">
<?= csrf_field() ?><input type="hidden" name="action" value="item.add"><input type="hidden" name="month" value="<?= h($month) ?>">
<div class="card-heading"><div><p class="eyebrow">Define the target</p><h2>Add budget item</h2></div><button class="icon-button" type="button" data-dialog-close>×</button></div>
<div class="form-grid">
<label class="span-2">Name<input type="text" name="name" required placeholder="Internet, rent, groceries…"></label>
<label>Target amount<div class="money-input"><span><?= h($symbol) ?></span><input type="number" name="planned_amount" min="0" step="0.01" required></div></label>
<label>Category<select name="category_id"><option value="">Uncategorized</option><?php foreach ($categories as $category): ?><option value="<?= (int) $category['id'] ?>"><?= h($category['name']) ?></option><?php endforeach; ?></select></label>
<label>Due date<input type="date" name="due_date" value="<?= h($month . '-01') ?>"></label>
<label>Repeat<select name="recurrence"><option value="none">One time</option><option value="monthly">Monthly</option><option value="yearly">Yearly</option></select></label>
<label class="span-2">Notes<textarea name="notes" rows="2"></textarea></label>
</div>
<button class="button primary full" type="submit">Add budget item</button>
</form>
</dialog>
<dialog id="income-dialog">
<form method="post" class="dialog-card">
<?= csrf_field() ?><input type="hidden" name="action" value="income.add"><input type="hidden" name="month" value="<?= h($month) ?>">
<div class="card-heading"><div><p class="eyebrow">Beyond the starting point</p><h2>Add income</h2></div><button class="icon-button" type="button" data-dialog-close>×</button></div>
<div class="form-grid">
<label class="span-2">Label<input type="text" name="label" required placeholder="Paycheck, freelance, refund…"></label>
<label>Amount<div class="money-input"><span><?= h($symbol) ?></span><input type="number" name="amount" min="0.01" step="0.01" required></div></label>
<label>Received<input type="date" name="received_on" value="<?= h(date('Y-m-d')) ?>" required></label>
<label class="span-2">Notes<textarea name="notes" rows="2"></textarea></label>
</div>
<p class="form-note">The configured income withholding will move automatically to the dormant account.</p>
<button class="button primary full" type="submit">Record income</button>
</form>
</dialog>