Bicycle/App/view/Budgets/budgets.html
Egor Isaev c9b81cdcd1 dev
2026-08-14 13:09:54 +03:00

151 lines
8.3 KiB
HTML
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
/**
* @package Bicycle
* @description budgets.html — план бюджета на год по месяцам. Своя раскладка на каждое
* устройство (см. .claude/memory/бюджет_текущий_план.md → обсуждение дизайна):
* комп/планшет — карточки статьи с 12 месяцами (планшет — сеткой 6×2 вместо
* ряда, через container query); телефон — раскрывающиеся карточки-аккордeоны
* (все 12 месяцев по тапу). Ни один режим не даёт горизонтальный скролл.
* Факт (реальные transactions) здесь не показывается.
*
* @var array<int,object[]> $tree Активные статьи пользователя, сгруппированные по parent_id
* @var object[] $groups Верхнеуровневые группы (tree[0])
* @var array $plan [categorie_id => [month => ['plan_summ'=>float,'formula_pct_used'=>?float]]]
* @var int $year
*/
use System\Classes\CSRF;
$months = [
1 => 'Янв', 2 => 'Фев', 3 => 'Мар', 4 => 'Апр', 5 => 'Май', 6 => 'Июн',
7 => 'Июл', 8 => 'Авг', 9 => 'Сен', 10 => 'Окт', 11 => 'Ноя', 12 => 'Дек',
];
$sections = [
'income' => 'Доходы',
'expense' => 'Расходы',
];
// Категории, чей план считается автоматически по формуле связанной income-статьи —
// у них своя ячейка readonly (см. CategoriesController::parseFormulaLink()).
$formula_targets = [];
foreach ($tree as $items) {
foreach ($items as $c) {
if ($c->linked_expense_categorie_id) {
$formula_targets[(int)$c->linked_expense_categorie_id] = true;
}
}
}
/**
* Одна ячейка ввода — общая и для карточек (комп/планшет), и для аккордeона (телефон),
* чтобы имя поля/значение/readonly не разъезжались между двумя разметками одних данных.
*/
$planInput = static function (int $categorie_id, int $month, array $plan, bool $readonly) {
$value = $plan[$categorie_id][$month]['plan_summ'] ?? '';
echo '<input type="number" step="0.01" min="0" class="plan-input num" name="plan[' . $categorie_id . '][' . $month . ']"'
. ' value="' . htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8') . '"' . ($readonly ? ' readonly' : '') . '>';
};
$this->setScript('budgets.js');
?>
<div class="top-row">
<div>
<div class="hello">План бюджета на год</div>
</div>
<div style="display:flex;align-items:center;gap:0.75rem;">
<a class="btn btn-outline-light btn-sm" href="?year=<?= $year - 1 ?>">&larr;</a>
<div class="balance num" style="font-size:2rem;"><?= $year ?></div>
<a class="btn btn-outline-light btn-sm" href="?year=<?= $year + 1 ?>">&rarr;</a>
</div>
</div>
<form id="budgets-form" method="post" action="/budgets/save">
<?= CSRF::field() ?>
<input type="hidden" name="year" value="<?= $year ?>">
<?php foreach ($sections as $type => $label): ?>
<div class="section-label"><span><?= $label ?></span></div>
<?php $type_groups = array_filter($groups, static fn ($g) => $g->type === $type); ?>
<?php if (empty($type_groups)): ?>
<div class="empty-state">Нет статей — сначала добавьте их на странице «Статьи»</div>
<?php else: ?>
<div class="budget-zoom">
<!-- ---------- комп/планшет: карточки, 12 месяцев (планшет — сеткой 6×2) ---------- -->
<div class="budget-cards-wrap">
<?php foreach ($type_groups as $group): ?>
<?php $children = $tree[$group->id] ?? []; ?>
<div class="budget-plan-group">
<div class="budget-group-head" style="--card-accent:var(--blue)">
<span class="budget-group-name"><?= htmlspecialchars($group->title, ENT_QUOTES, 'UTF-8') ?></span>
</div>
<div class="budget-cards">
<?php foreach (($children !== [] ? $children : [$group]) as $category): ?>
<?php $is_target = isset($formula_targets[(int)$category->id]); ?>
<div class="budget-card">
<?php if ($children !== []): ?>
<div class="budget-card-name"><?= htmlspecialchars($category->title, ENT_QUOTES, 'UTF-8') ?></div>
<?php endif; ?>
<div class="budget-months-inline">
<?php foreach ($months as $m => $mlabel): ?>
<div class="budget-cell">
<div class="budget-cell-label"><?= $mlabel ?></div>
<?php $planInput((int)$category->id, $m, $plan, $is_target); ?>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- ---------- телефон: один аккордeон на статью верхнего уровня (группу или
самостоятельную статью), дети — подписями внутри того же аккордeона ---------- -->
<div class="budget-accordion">
<?php foreach ($type_groups as $group): ?>
<?php
$children = $tree[$group->id] ?? [];
$rows = $children !== [] ? $children : [$group];
$year_sum = 0.0;
foreach ($rows as $row_category) {
foreach ($months as $m => $_) {
$year_sum += (float)($plan[$row_category->id][$m]['plan_summ'] ?? 0);
}
}
?>
<details class="budget-acc-card">
<summary>
<span class="budget-acc-name"><?= htmlspecialchars($group->title, ENT_QUOTES, 'UTF-8') ?></span>
<span class="budget-acc-total num"><?= number_format($year_sum, 0, ',', ' ') ?> ₽/год</span>
</summary>
<?php foreach ($rows as $category): ?>
<?php $is_target = isset($formula_targets[(int)$category->id]); ?>
<?php if ($children !== []): ?>
<div class="budget-acc-caption"><?= htmlspecialchars($category->title, ENT_QUOTES, 'UTF-8') ?></div>
<?php endif; ?>
<div class="budget-acc-body">
<?php foreach ($months as $m => $mlabel): ?>
<div class="budget-acc-month">
<span><?= $mlabel ?></span>
<?php $planInput((int)$category->id, $m, $plan, $is_target); ?>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</details>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
<div id="budgets-form-error" class="alert alert-danger" style="display:none"></div>
</form>