-
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
<?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>
|
||||
Reference in New Issue
Block a user