Operacje: nowrap dla daty/kwoty, ucinanie opisu i stronicowanie 10/str
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,12 +21,20 @@ class Operations extends BaseController
|
|||||||
'type' => $this->request->getGet('type'),
|
'type' => $this->request->getGet('type'),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$perPage = 10;
|
||||||
|
$total = $this->model()->countFiltered($f);
|
||||||
|
$pages = max(1, (int) ceil($total / $perPage));
|
||||||
|
$page = min($pages, max(1, (int) $this->request->getGet('page')));
|
||||||
|
|
||||||
return view('operations/index', [
|
return view('operations/index', [
|
||||||
'title' => 'Operacje',
|
'title' => 'Operacje',
|
||||||
'operations' => $this->model()->filtered($f),
|
'operations' => $this->model()->filtered($f, $perPage, $page),
|
||||||
'totals' => $this->model()->totals($f),
|
'totals' => $this->model()->totals($f),
|
||||||
'categories' => model(CategoryModel::class)->tree(),
|
'categories' => model(CategoryModel::class)->tree(),
|
||||||
'filter' => $f,
|
'filter' => $f,
|
||||||
|
'page' => $page,
|
||||||
|
'pages' => $pages,
|
||||||
|
'total' => $total,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class OperationModel extends Model
|
|||||||
protected $primaryKey = 'id';
|
protected $primaryKey = 'id';
|
||||||
protected $returnType = 'array';
|
protected $returnType = 'array';
|
||||||
protected $useTimestamps = true;
|
protected $useTimestamps = true;
|
||||||
protected $allowedFields = ['date', 'amount', 'type', 'category_id', 'description'];
|
protected $allowedFields = ['date', 'amount', 'type', 'category_id', 'receipt_id', 'description'];
|
||||||
|
|
||||||
protected $validationRules = [
|
protected $validationRules = [
|
||||||
'date' => 'required|valid_date[Y-m-d]',
|
'date' => 'required|valid_date[Y-m-d]',
|
||||||
@@ -23,14 +23,39 @@ class OperationModel extends Model
|
|||||||
/**
|
/**
|
||||||
* Lista operacji z nazwa kategorii, z filtrami.
|
* Lista operacji z nazwa kategorii, z filtrami.
|
||||||
* $f: from, to, category_id, type (wszystkie opcjonalne).
|
* $f: from, to, category_id, type (wszystkie opcjonalne).
|
||||||
|
* Gdy $perPage > 0, zwraca tylko jedna strone (LIMIT/OFFSET).
|
||||||
*/
|
*/
|
||||||
public function filtered(array $f): array
|
public function filtered(array $f, int $perPage = 0, int $page = 1): array
|
||||||
{
|
{
|
||||||
$b = $this->db->table('operations o')
|
$b = $this->db->table('operations o')
|
||||||
->select('o.*, c.name AS category_name, p.name AS parent_name')
|
->select('o.*, c.name AS category_name, p.name AS parent_name')
|
||||||
->join('categories c', 'c.id = o.category_id', 'left')
|
->join('categories c', 'c.id = o.category_id', 'left')
|
||||||
->join('categories p', 'p.id = c.parent_id', 'left');
|
->join('categories p', 'p.id = c.parent_id', 'left');
|
||||||
|
|
||||||
|
$this->applyFilters($b, $f);
|
||||||
|
|
||||||
|
$b->orderBy('o.date', 'DESC')->orderBy('o.id', 'DESC');
|
||||||
|
|
||||||
|
if ($perPage > 0) {
|
||||||
|
$b->limit($perPage, max(0, ($page - 1) * $perPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $b->get()->getResultArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liczba operacji pasujacych do filtrow (do stronicowania).
|
||||||
|
*/
|
||||||
|
public function countFiltered(array $f): int
|
||||||
|
{
|
||||||
|
$b = $this->db->table('operations o');
|
||||||
|
$this->applyFilters($b, $f);
|
||||||
|
|
||||||
|
return $b->countAllResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyFilters($b, array $f): void
|
||||||
|
{
|
||||||
if (! empty($f['from'])) {
|
if (! empty($f['from'])) {
|
||||||
$b->where('o.date >=', $f['from']);
|
$b->where('o.date >=', $f['from']);
|
||||||
}
|
}
|
||||||
@@ -43,8 +68,6 @@ class OperationModel extends Model
|
|||||||
if (! empty($f['type'])) {
|
if (! empty($f['type'])) {
|
||||||
$b->where('o.type', $f['type']);
|
$b->where('o.type', $f['type']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $b->orderBy('o.date', 'DESC')->orderBy('o.id', 'DESC')->get()->getResultArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
<?= $this->section('content') ?>
|
<?= $this->section('content') ?>
|
||||||
<?php $fmt = static fn ($v) => number_format((float) $v, 2, ',', ' ') . ' zł'; ?>
|
<?php $fmt = static fn ($v) => number_format((float) $v, 2, ',', ' ') . ' zł'; ?>
|
||||||
|
<style>
|
||||||
|
.col-desc { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
</style>
|
||||||
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
<h1 class="h4 mb-0">Operacje</h1>
|
<h1 class="h4 mb-0">Operacje</h1>
|
||||||
@@ -65,10 +68,10 @@
|
|||||||
<table class="table table-hover mb-0 align-middle">
|
<table class="table table-hover mb-0 align-middle">
|
||||||
<thead class="table-light">
|
<thead class="table-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Data</th>
|
<th class="text-nowrap">Data</th>
|
||||||
<th>Kategoria</th>
|
<th class="text-nowrap">Kategoria</th>
|
||||||
<th>Opis</th>
|
<th>Opis</th>
|
||||||
<th class="text-end">Kwota</th>
|
<th class="text-end text-nowrap">Kwota</th>
|
||||||
<th class="text-end">Akcje</th>
|
<th class="text-end">Akcje</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -77,8 +80,8 @@
|
|||||||
<tr><td colspan="5" class="text-center text-muted py-4">Brak operacji dla wybranych filtrów.</td></tr>
|
<tr><td colspan="5" class="text-center text-muted py-4">Brak operacji dla wybranych filtrów.</td></tr>
|
||||||
<?php else: foreach ($operations as $o): ?>
|
<?php else: foreach ($operations as $o): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?= esc($o['date']) ?></td>
|
<td class="text-nowrap"><?= esc($o['date']) ?></td>
|
||||||
<td>
|
<td class="text-nowrap">
|
||||||
<?php
|
<?php
|
||||||
if (! empty($o['category_name'])) {
|
if (! empty($o['category_name'])) {
|
||||||
echo esc(! empty($o['parent_name'])
|
echo esc(! empty($o['parent_name'])
|
||||||
@@ -89,11 +92,11 @@
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-muted"><?= esc($o['description'] ?? '') ?></td>
|
<td class="text-muted col-desc" title="<?= esc($o['description'] ?? '') ?>"><?= esc($o['description'] ?? '') ?></td>
|
||||||
<td class="text-end <?= $o['type'] === 'income' ? 'amount-income' : 'amount-expense' ?>">
|
<td class="text-end text-nowrap <?= $o['type'] === 'income' ? 'amount-income' : 'amount-expense' ?>">
|
||||||
<?= $o['type'] === 'income' ? '+' : '−' ?><?= $fmt($o['amount']) ?>
|
<?= $o['type'] === 'income' ? '+' : '−' ?><?= $fmt($o['amount']) ?>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end">
|
<td class="text-end text-nowrap">
|
||||||
<a href="<?= site_url('operations/' . $o['id'] . '/edit') ?>" class="btn btn-sm btn-outline-secondary">Edytuj</a>
|
<a href="<?= site_url('operations/' . $o['id'] . '/edit') ?>" class="btn btn-sm btn-outline-secondary">Edytuj</a>
|
||||||
<a href="<?= site_url('operations/' . $o['id'] . '/delete') ?>" class="btn btn-sm btn-outline-danger"
|
<a href="<?= site_url('operations/' . $o['id'] . '/delete') ?>" class="btn btn-sm btn-outline-danger"
|
||||||
onclick="return confirm('Usunąć operację?')">Usuń</a>
|
onclick="return confirm('Usunąć operację?')">Usuń</a>
|
||||||
@@ -103,5 +106,34 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<?php if ($pages > 1): ?>
|
||||||
|
<?php
|
||||||
|
$qs = array_filter([
|
||||||
|
'from' => $filter['from'] ?? '',
|
||||||
|
'to' => $filter['to'] ?? '',
|
||||||
|
'category_id' => $filter['category_id'] ?? '',
|
||||||
|
'type' => $filter['type'] ?? '',
|
||||||
|
], static fn ($v) => $v !== '' && $v !== null);
|
||||||
|
$link = static fn (int $p) => site_url('operations') . '?' . http_build_query($qs + ['page' => $p]);
|
||||||
|
?>
|
||||||
|
<div class="card-footer d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||||
|
<span class="text-muted small">Strona <?= $page ?> z <?= $pages ?> (<?= $total ?> operacji)</span>
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination pagination-sm mb-0">
|
||||||
|
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
|
||||||
|
<a class="page-link" href="<?= $link(max(1, $page - 1)) ?>">‹</a>
|
||||||
|
</li>
|
||||||
|
<?php for ($p = 1; $p <= $pages; $p++): ?>
|
||||||
|
<li class="page-item <?= $p === $page ? 'active' : '' ?>">
|
||||||
|
<a class="page-link" href="<?= $link($p) ?>"><?= $p ?></a>
|
||||||
|
</li>
|
||||||
|
<?php endfor; ?>
|
||||||
|
<li class="page-item <?= $page >= $pages ? 'disabled' : '' ?>">
|
||||||
|
<a class="page-link" href="<?= $link(min($pages, $page + 1)) ?>">›</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?= $this->endSection() ?>
|
<?= $this->endSection() ?>
|
||||||
|
|||||||
Reference in New Issue
Block a user