247 lines
13 KiB
HTML
247 lines
13 KiB
HTML
<?php
|
||
/**
|
||
* @package Bicycle
|
||
* @description index.html — дашборд/«Обзор» (главная страница после входа). Дизайн — по
|
||
* согласованному макету (тёмная тема, room-card, dock-навбар), рендерится
|
||
* внутри layout.html через Controller::render().
|
||
*
|
||
* @var array $user Текущий авторизованный пользователь (без пароля)
|
||
* @var object[] $accounts Счета пользователя (accounts, не архивные)
|
||
* @var object[] $archived_accounts Архивные счета пользователя (is_delete=1)
|
||
* @var float $total Сумма счетов с include_in_total=1 в рублях
|
||
*/
|
||
|
||
use App\Classes\Currency;
|
||
use System\Classes\CSRF;
|
||
|
||
$type_icons = [
|
||
'cash' => 'ic-wallet',
|
||
'bank' => 'ic-card',
|
||
'savings' => 'ic-piggy',
|
||
];
|
||
|
||
$type_accents = [
|
||
'cash' => 'var(--teal)',
|
||
'bank' => 'var(--blue)',
|
||
'savings' => 'var(--purple)',
|
||
];
|
||
|
||
$this->setScript('accounts.js');
|
||
|
||
?>
|
||
|
||
<svg width="0" height="0" style="position:absolute">
|
||
<defs>
|
||
<symbol id="ic-wallet" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="2" y="6" width="20" height="14" rx="2"/>
|
||
<path d="M16 12h4v4h-4a2 2 0 0 1 0-4Z"/>
|
||
</symbol>
|
||
<symbol id="ic-card" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="2" y="5" width="20" height="14" rx="2"/>
|
||
<line x1="2" y1="10" x2="22" y2="10"/>
|
||
</symbol>
|
||
<symbol id="ic-piggy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="3" y="9" width="18" height="9" rx="4.5"/>
|
||
<line x1="9" y1="13.5" x2="14" y2="13.5"/>
|
||
<circle cx="17" cy="6" r="2"/>
|
||
<line x1="17" y1="8" x2="17" y2="9"/>
|
||
</symbol>
|
||
<symbol id="ic-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<line x1="12" y1="5" x2="12" y2="19"/>
|
||
<line x1="5" y1="12" x2="19" y2="12"/>
|
||
</symbol>
|
||
<symbol id="ic-dots" viewBox="0 0 24 24" fill="currentColor">
|
||
<circle cx="12" cy="5" r="1.6"/>
|
||
<circle cx="12" cy="12" r="1.6"/>
|
||
<circle cx="12" cy="19" r="1.6"/>
|
||
</symbol>
|
||
<symbol id="ic-pencil" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M12 20h9"/>
|
||
<path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/>
|
||
</symbol>
|
||
<symbol id="ic-trash" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M3 6h18"/>
|
||
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
|
||
</symbol>
|
||
<symbol id="ic-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M3 10.5 12 3l9 7.5"/>
|
||
<path d="M5 9.5V20a1 1 0 0 0 1 1h4v-6h4v6h4a1 1 0 0 0 1-1V9.5"/>
|
||
</symbol>
|
||
<symbol id="ic-tag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M12 2 2 12l9 9 11-11V2Z"/>
|
||
<circle cx="7.5" cy="7.5" r="1.5"/>
|
||
</symbol>
|
||
<symbol id="ic-swap" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M7 7h11l-3-3"/>
|
||
<path d="M17 17H6l3 3"/>
|
||
</symbol>
|
||
<symbol id="ic-target" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="9"/>
|
||
<circle cx="12" cy="12" r="5"/>
|
||
<circle cx="12" cy="12" r="1.5"/>
|
||
</symbol>
|
||
<symbol id="ic-archive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="2" y="4" width="20" height="5" rx="1"/>
|
||
<path d="M4 9v9a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9"/>
|
||
<line x1="10" y1="13" x2="14" y2="13"/>
|
||
</symbol>
|
||
<symbol id="ic-undo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M3 10h10a5 5 0 0 1 0 10H8"/>
|
||
<path d="M8 5 3 10l5 5"/>
|
||
</symbol>
|
||
</defs>
|
||
</svg>
|
||
|
||
<div class="top-row">
|
||
<div>
|
||
<div class="hello">Здравствуйте, <?= htmlspecialchars($user['full_name'] ?? $user['login'], ENT_QUOTES, 'UTF-8') ?></div>
|
||
<div class="balance-label">Доступно сейчас</div>
|
||
<div class="balance num"><?= number_format($total, 0, ',', ' ') ?> <span>₽</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-label">
|
||
<span>Счета</span>
|
||
<div style="display:flex;gap:0.5rem;">
|
||
<button type="button" class="btn-add js-account-archive-open" title="Архив счетов"><svg class="ico"><use href="#ic-archive"/></svg></button>
|
||
<button type="button" class="btn-add js-account-add" title="Добавить счёт"><svg class="ico"><use href="#ic-plus"/></svg></button>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if (empty($accounts)): ?>
|
||
<div class="empty-state">Пока нет ни одного счёта — нажмите «+», чтобы добавить первый</div>
|
||
<?php else: ?>
|
||
<div class="grid-cards">
|
||
<?php foreach ($accounts as $account): ?>
|
||
<div class="room-card"
|
||
style="--card-accent:<?= $type_accents[$account->type_acc] ?? 'var(--blue)' ?>"
|
||
data-id="<?= (int)$account->id ?>"
|
||
data-title="<?= htmlspecialchars($account->title, ENT_QUOTES, 'UTF-8') ?>"
|
||
data-type="<?= htmlspecialchars($account->type_acc, ENT_QUOTES, 'UTF-8') ?>"
|
||
data-summa="<?= htmlspecialchars((string)$account->summa, ENT_QUOTES, 'UTF-8') ?>"
|
||
data-currency="<?= htmlspecialchars($account->currency, ENT_QUOTES, 'UTF-8') ?>"
|
||
data-description="<?= htmlspecialchars((string)($account->description ?? ''), ENT_QUOTES, 'UTF-8') ?>"
|
||
data-include="<?= $account->include_in_total ? '1' : '' ?>">
|
||
<div class="room-card-head">
|
||
<span class="room-icon"><svg class="ico"><use href="#<?= $type_icons[$account->type_acc] ?? 'ic-wallet' ?>"/></svg></span>
|
||
<div class="room-menu">
|
||
<button type="button" class="room-menu-btn">
|
||
<svg class="ico"><use href="#ic-dots"/></svg>
|
||
</button>
|
||
<div class="room-menu-list">
|
||
<a href="#" class="js-account-edit"><svg class="ico ico-sm"><use href="#ic-pencil"/></svg>Редактировать</a>
|
||
<a href="#" class="danger js-account-delete"><svg class="ico ico-sm"><use href="#ic-trash"/></svg>Удалить</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="room-title"><?= htmlspecialchars($account->title, ENT_QUOTES, 'UTF-8') ?></div>
|
||
<div class="room-value num">
|
||
<?= number_format((float)$account->summa, 2, ',', ' ') ?>
|
||
<?= htmlspecialchars($account->currency, ENT_QUOTES, 'UTF-8') ?>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="modal fade" id="account-modal" tabindex="-1" data-bs-theme="dark">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content" style="background:var(--surface-2);color:var(--ink);">
|
||
<form id="account-form" method="post">
|
||
<?= CSRF::field() ?>
|
||
<input type="hidden" name="id">
|
||
<div class="modal-header" style="border-color:var(--surface-3)">
|
||
<h5 class="modal-title" id="account-modal-title">Новый счёт</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="account-form-error" class="alert alert-danger" style="display:none"></div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Название</label>
|
||
<input type="text" class="form-control" name="title" maxlength="128" required>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Тип</label>
|
||
<select class="form-select" name="type_acc">
|
||
<option value="cash">Кошелёк (наличные)</option>
|
||
<option value="bank">Банковский счёт</option>
|
||
<option value="savings">Копилка</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-7 mb-3">
|
||
<label class="form-label">Сумма</label>
|
||
<input type="number" step="0.01" min="0" class="form-control" name="summa" required>
|
||
</div>
|
||
<div class="col-5 mb-3">
|
||
<label class="form-label">Валюта</label>
|
||
<select class="form-select" name="currency">
|
||
<?php foreach (Currency::LIST as $code => $name): ?>
|
||
<option value="<?= $code ?>"<?= $code === 'RUB' ? ' selected' : '' ?> title="<?= htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?>"><?= $code ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3" id="account-rate-group">
|
||
<div id="account-rate-field" style="display:none">
|
||
<label class="form-label">Ставка, % годовых</label>
|
||
<input type="number" step="0.01" min="0" class="form-control" name="rate_percent">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Описание</label>
|
||
<input type="text" class="form-control" name="description" maxlength="255">
|
||
</div>
|
||
|
||
<div class="form-check">
|
||
<input type="checkbox" class="form-check-input" name="include_in_total" value="1" id="account-include" checked>
|
||
<label class="form-check-label" for="account-include">Учитывать в «Доступно сейчас»</label>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer" style="border-color:var(--surface-3)">
|
||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal fade" id="archive-modal" tabindex="-1" data-bs-theme="dark">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content" style="background:var(--surface-2);color:var(--ink);">
|
||
<div class="modal-header" style="border-color:var(--surface-3)">
|
||
<h5 class="modal-title">Архив счетов</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<?php if (empty($archived_accounts)): ?>
|
||
<div class="empty-state">Архив пуст</div>
|
||
<?php else: ?>
|
||
<?php foreach ($archived_accounts as $account): ?>
|
||
<div class="archive-row" data-id="<?= (int)$account->id ?>" data-title="<?= htmlspecialchars($account->title, ENT_QUOTES, 'UTF-8') ?>">
|
||
<div>
|
||
<div class="room-title"><?= htmlspecialchars($account->title, ENT_QUOTES, 'UTF-8') ?></div>
|
||
<div class="room-sub num">
|
||
<?= number_format((float)$account->summa, 2, ',', ' ') ?>
|
||
<?= htmlspecialchars($account->currency, ENT_QUOTES, 'UTF-8') ?>
|
||
</div>
|
||
</div>
|
||
<button type="button" class="btn btn-sm btn-outline-light js-account-restore" title="Восстановить">
|
||
<svg class="ico"><use href="#ic-undo"/></svg>
|
||
Восстановить
|
||
</button>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|