rozbicie na moduły, poprawki i komendy cli
This commit is contained in:
373
zbiorka_app/templates/admin/dashboard.html
Normal file
373
zbiorka_app/templates/admin/dashboard.html
Normal file
@@ -0,0 +1,373 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Panel Admina{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
|
||||
<!-- Nagłówek + akcje globalne -->
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-4">
|
||||
<div>
|
||||
<h2 class="mb-1">Panel Admina</h2>
|
||||
<p class="text-muted mb-0">Zarządzaj zbiórkami i monitoruj finanse</p>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="{{ url_for('admin_statystyki') }}" class="btn btn-info">
|
||||
<i class="bi bi-graph-up"></i> Statystyki
|
||||
</a>
|
||||
<a href="{{ url_for('formularz_zbiorek') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Dodaj zbiórkę
|
||||
</a>
|
||||
<a href="{{ url_for('lista_rezerwowych') }}" class="btn btn-outline-light">
|
||||
<i class="bi bi-wallet2"></i> Listy rezerwowe
|
||||
</a>
|
||||
<a href="{{ url_for('admin_ustawienia') }}" class="btn btn-outline-light">
|
||||
<i class="bi bi-gear"></i> Ustawienia
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Szybkie statystyki (cards) -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card border-primary">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-list-check fs-1 text-primary mb-2"></i>
|
||||
<h3 class="mb-0">{{ active_zbiorki|length }}</h3>
|
||||
<small class="text-muted">Aktywnych zbiórek</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card border-success">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-check-circle fs-1 text-success mb-2"></i>
|
||||
<h3 class="mb-0">{{ completed_zbiorki|length }}</h3>
|
||||
<small class="text-muted">Zrealizowanych</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card border-info">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-bar-chart fs-1 text-info mb-2"></i>
|
||||
<h3 class="mb-0">{{ active_zbiorki|length + completed_zbiorki|length }}</h3>
|
||||
<small class="text-muted">Łącznie zbiórek</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card border-warning">
|
||||
<div class="card-body text-center">
|
||||
<a href="{{ url_for('admin_statystyki') }}" class="text-decoration-none">
|
||||
<i class="bi bi-graph-up-arrow fs-1 text-warning mb-2"></i>
|
||||
<h5 class="mb-0 text-white">Zobacz pełne</h5>
|
||||
<small class="text-muted">statystyki</small>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pigułki: Aktywne / Zrealizowane -->
|
||||
<ul class="nav nav-pills mb-3" id="adminTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="tab-aktywne" data-bs-toggle="tab" data-bs-target="#pane-aktywne"
|
||||
type="button" role="tab" aria-controls="pane-aktywne" aria-selected="true">
|
||||
<i class="bi bi-lightning"></i> Aktywne zbiórki ({{ active_zbiorki|length }})
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="tab-zrealizowane" data-bs-toggle="tab" data-bs-target="#pane-zrealizowane"
|
||||
type="button" role="tab" aria-controls="pane-zrealizowane" aria-selected="false">
|
||||
<i class="bi bi-check-circle"></i> Zrealizowane ({{ completed_zbiorki|length }})
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- PANE: Aktywne -->
|
||||
<div class="tab-pane fade show active" id="pane-aktywne" role="tabpanel" aria-labelledby="tab-aktywne"
|
||||
tabindex="0">
|
||||
|
||||
{% if active_zbiorki and active_zbiorki|length > 0 %}
|
||||
<div class="table-responsive mb-5">
|
||||
<table class="table table-dark table-striped table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:72px;">ID</th>
|
||||
<th>Nazwa</th>
|
||||
<th style="width:140px;">Widoczność</th>
|
||||
<th style="width:1%;">Opcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for z in active_zbiorki %}
|
||||
<tr>
|
||||
<td class="text-muted">{{ z.id }}</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=z.id) }}" class="fw-semibold text-decoration-none">
|
||||
{{ z.nazwa }}
|
||||
</a>
|
||||
{% if z.cel is defined or z.stan is defined %}
|
||||
<small class="text-muted">
|
||||
{% if z.cel is defined and z.cel > 0 %}
|
||||
Cel: {{ z.cel|round(2) }} PLN
|
||||
{% endif %}
|
||||
{% if z.stan is defined %}
|
||||
· Stan: {{ z.stan|round(2) }} PLN
|
||||
{% endif %}
|
||||
{% if z.cel is defined and z.cel > 0 and z.stan is defined %}
|
||||
· {{ ((z.stan / z.cel) * 100)|round(1) }}%
|
||||
{% endif %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{% if z.ukryta %}
|
||||
<span class="badge bg-secondary border"
|
||||
style="border-color: var(--border);"><i class="bi bi-eye-slash"></i> Ukryta</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success"><i class="bi bi-eye"></i> Widoczna</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<!-- Grupa akcji: główne + rozwijane -->
|
||||
<div class="btn-group">
|
||||
<a href="{{ url_for('formularz_zbiorek', zbiorka_id=z.id) }}"
|
||||
class="btn btn-sm btn-outline-light">
|
||||
<i class="bi bi-pencil"></i> Edytuj
|
||||
</a>
|
||||
<button class="btn btn-sm btn-outline-light dropdown-toggle dropdown-toggle-split"
|
||||
data-bs-toggle="dropdown" aria-expanded="false" aria-label="Więcej opcji">
|
||||
<span class="visually-hidden">Więcej</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end shadow">
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ url_for('zbiorka', zbiorka_id=z.id) }}">
|
||||
<i class="bi bi-eye"></i> Podgląd
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('dodaj_wplate', zbiorka_id=z.id) }}">
|
||||
<i class="bi bi-plus-circle text-success"></i> Dodaj wpłatę
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('dodaj_wydatek', zbiorka_id=z.id) }}">
|
||||
<i class="bi bi-dash-circle text-danger"></i> Dodaj wydatek
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('transakcje_zbiorki', zbiorka_id=z.id) }}">
|
||||
<i class="bi bi-list-ul"></i> Transakcje
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('edytuj_stan', zbiorka_id=z.id) }}">
|
||||
<i class="bi bi-currency-dollar"></i> Edytuj stan
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<form action="{{ url_for('oznacz_zrealizowana', zbiorka_id=z.id) }}"
|
||||
method="post" class="m-0">
|
||||
<button type="submit" class="dropdown-item">
|
||||
<i class="bi bi-check-circle text-success"></i> Oznacz jako zrealizowaną
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
<form action="{{ url_for('zmien_widzialnosc', zbiorka_id=z.id) }}"
|
||||
method="post" class="m-0">
|
||||
<button type="submit" class="dropdown-item">
|
||||
{% if z.ukryta %}
|
||||
<i class="bi bi-eye"></i> Pokaż
|
||||
{% else %}
|
||||
<i class="bi bi-eye-slash"></i> Ukryj
|
||||
{% endif %}
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<form action="{{ url_for('usun_zbiorka', zbiorka_id=z.id) }}" method="post"
|
||||
class="m-0"
|
||||
onsubmit="return confirm('Czy na pewno chcesz usunąć tę zbiórkę?');">
|
||||
<button type="submit" class="dropdown-item text-danger">
|
||||
<i class="bi bi-trash"></i> Usuń
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- Empty state -->
|
||||
<div class="card">
|
||||
<div class="card-body text-center py-5">
|
||||
<i class="bi bi-inbox fs-1 text-muted mb-3"></i>
|
||||
<h5 class="mb-2">Brak aktywnych zbiórek</h5>
|
||||
<p class="text-muted mb-4">Wygląda na to, że teraz nic nie zbieramy.</p>
|
||||
<a href="{{ url_for('formularz_zbiorek') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Utwórz nową zbiórkę
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- PANEL: Zrealizowane -->
|
||||
<div class="tab-pane fade" id="pane-zrealizowane" role="tabpanel" aria-labelledby="tab-zrealizowane"
|
||||
tabindex="0">
|
||||
|
||||
{% if completed_zbiorki and completed_zbiorki|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:72px;">ID</th>
|
||||
<th>Nazwa</th>
|
||||
<th style="width:180px;">Status</th>
|
||||
<th style="width:1%;">Opcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for z in completed_zbiorki %}
|
||||
<tr>
|
||||
<td class="text-muted">{{ z.id }}</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=z.id) }}" class="fw-semibold text-decoration-none">
|
||||
{{ z.nazwa }}
|
||||
</a>
|
||||
{% if z.cel is defined or z.stan is defined %}
|
||||
<small class="text-muted">
|
||||
{% if z.cel is defined %} Cel: {{ z.cel|round(2) }} PLN {% endif %}
|
||||
{% if z.stan is defined %} · Zebrano: {{ z.stan|round(2) }} PLN {% endif %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||
<span class="badge rounded-pill"
|
||||
style="background: var(--accent); color:#111;">
|
||||
<i class="bi bi-check-circle"></i> Zrealizowana
|
||||
</span>
|
||||
{% if z.ukryta %}
|
||||
<span class="badge bg-secondary border"
|
||||
style="border-color: var(--border);"><i class="bi bi-eye-slash"></i> Ukryta</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success"><i class="bi bi-eye"></i> Widoczna</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<div class="btn-group">
|
||||
<a href="{{ url_for('formularz_zbiorek', zbiorka_id=z.id) }}"
|
||||
class="btn btn-sm btn-outline-light">
|
||||
<i class="bi bi-pencil"></i> Edytuj
|
||||
</a>
|
||||
<button class="btn btn-sm btn-outline-light dropdown-toggle dropdown-toggle-split"
|
||||
data-bs-toggle="dropdown" aria-expanded="false" aria-label="Więcej opcji">
|
||||
<span class="visually-hidden">Więcej</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end shadow">
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ url_for('zbiorka', zbiorka_id=z.id) }}">
|
||||
<i class="bi bi-eye"></i> Podgląd
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('dodaj_wplate', zbiorka_id=z.id) }}">
|
||||
<i class="bi bi-plus-circle text-success"></i> Dodaj wpłatę
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('dodaj_wydatek', zbiorka_id=z.id) }}">
|
||||
<i class="bi bi-dash-circle text-danger"></i> Dodaj wydatek
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('transakcje_zbiorki', zbiorka_id=z.id) }}">
|
||||
<i class="bi bi-list-ul"></i> Transakcje
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('edytuj_stan', zbiorka_id=z.id) }}">
|
||||
<i class="bi bi-currency-dollar"></i> Edytuj stan
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<form action="{{ url_for('oznacz_niezrealizowana', zbiorka_id=z.id) }}"
|
||||
method="post" class="m-0">
|
||||
<button type="submit" class="dropdown-item">
|
||||
<i class="bi bi-arrow-counterclockwise"></i> Oznacz jako niezrealizowaną
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
<form action="{{ url_for('zmien_widzialnosc', zbiorka_id=z.id) }}"
|
||||
method="post" class="m-0">
|
||||
<button type="submit" class="dropdown-item">
|
||||
{% if z.ukryta %}
|
||||
<i class="bi bi-eye"></i> Pokaż
|
||||
{% else %}
|
||||
<i class="bi bi-eye-slash"></i> Ukryj
|
||||
{% endif %}
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<form action="{{ url_for('usun_zbiorka', zbiorka_id=z.id) }}" method="post"
|
||||
class="m-0"
|
||||
onsubmit="return confirm('Czy na pewno chcesz usunąć tę zbiórkę?');">
|
||||
<button type="submit" class="dropdown-item text-danger">
|
||||
<i class="bi bi-trash"></i> Usuń
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<div class="card-body text-center py-5">
|
||||
<i class="bi bi-trophy fs-1 text-muted mb-3"></i>
|
||||
<h5 class="mb-2">Brak zbiórek zrealizowanych</h5>
|
||||
<p class="text-muted mb-3">Gdy jakaś zbiórka osiągnie 100%, pojawi się tutaj.</p>
|
||||
<a href="{{ url_for('formularz_zbiorek') }}" class="btn btn-outline-light">
|
||||
<i class="bi bi-plus-circle"></i> Utwórz nową zbiórkę
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
85
zbiorka_app/templates/admin/dodaj_przesuniecie.html
Normal file
85
zbiorka_app/templates/admin/dodaj_przesuniecie.html
Normal file
@@ -0,0 +1,85 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Przesuń środki - {{ zbiorka.nazwa }}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="container my-5">
|
||||
<!-- Nagłówek -->
|
||||
<div class="mb-4">
|
||||
<h1 class="mb-2">
|
||||
<i class="bi bi-arrow-left-right text-primary"></i> Przesuń środki z: {{ zbiorka.nazwa }}
|
||||
</h1>
|
||||
<p class="text-muted">Dostępne środki: <strong class="text-success">{{ zbiorka.stan|round(2) }} PLN</strong></p>
|
||||
</div>
|
||||
|
||||
<!-- Formularz -->
|
||||
<form method="POST">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header bg-transparent">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-box-arrow-in-down text-success"></i> Cel przesunięcia
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="zbiorka_cel_id" class="form-label fw-semibold">
|
||||
Docelowa zbiórka <span class="text-danger">*</span>
|
||||
</label>
|
||||
<select class="form-select" id="zbiorka_cel_id" name="zbiorka_cel_id" required>
|
||||
<option value="">-- Wybierz zbiórkę docelową --</option>
|
||||
{% for zb in dostepne_zbiorki %}
|
||||
<option value="{{ zb.id }}">
|
||||
{% if zb.typ_zbiorki == 'rezerwa' %}
|
||||
{{ zb.nazwa }} (Rezerwa)
|
||||
{% else %}
|
||||
{{ zb.nazwa }}
|
||||
{% endif %}
|
||||
· Stan: {{ zb.stan|round(2) }} PLN
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="kwota" class="form-label fw-semibold">
|
||||
Kwota (PLN) <span class="text-danger">*</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="kwota" name="kwota"
|
||||
placeholder="np. 100.00" required>
|
||||
<span class="input-group-text">PLN</span>
|
||||
</div>
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-info-circle"></i> Można użyć przecinka lub kropki
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-0">
|
||||
<label for="opis" class="form-label fw-semibold">
|
||||
Opis <span class="text-muted">(opcjonalny)</span>
|
||||
</label>
|
||||
<textarea class="form-control" id="opis" name="opis" rows="3"
|
||||
placeholder="Dodatkowe informacje o przesunięciu"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alert informacyjny -->
|
||||
<div class="alert alert-info d-flex align-items-start mb-3">
|
||||
<i class="bi bi-lightbulb fs-5 me-2"></i>
|
||||
<div class="small">
|
||||
<strong>Jak to działa:</strong> Kwota zostanie odjęta ze źródłowej zbiórki i dodana do docelowej.
|
||||
W obu zbiórkach pojawi się wpis o przesunięciu w historii transakcji.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-arrow-left-right"></i> Przesuń środki
|
||||
</button>
|
||||
<a href="{{ url_for('transakcje_zbiorki', zbiorka_id=zbiorka.id) }}"
|
||||
class="btn btn-secondary">Anuluj</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
104
zbiorka_app/templates/admin/dodaj_wplate.html
Normal file
104
zbiorka_app/templates/admin/dodaj_wplate.html
Normal file
@@ -0,0 +1,104 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Dodaj wpłatę{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}" class="btn btn-sm btn-outline-light">← Powrót do zbiórki</a>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-secondary text-white d-flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<h3 class="card-title mb-0">Dodaj wpłatę: <span class="fw-semibold">{{ zbiorka.nazwa }}</span></h3>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
{% if zbiorka.cel and zbiorka.typ_zbiorki != 'rezerwa' %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Cel: {{ zbiorka.cel|round(2) }} PLN
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Stan: {{ zbiorka.stan|round(2) }} PLN
|
||||
</span>
|
||||
|
||||
{% if zbiorka.cel and zbiorka.cel > 0 and zbiorka.typ_zbiorki != 'rezerwa' %}
|
||||
{% set delta = zbiorka.cel - zbiorka.stan %}
|
||||
{% if delta > 0 %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Brakuje: {{ delta|round(2) }} PLN
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Nadwyżka: {{ (-delta)|round(2) }} PLN
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if zbiorka.typ_zbiorki != 'rezerwa' %}
|
||||
{% set progress = (zbiorka.stan / zbiorka.cel * 100) if zbiorka.cel and zbiorka.cel > 0 else 0 %}
|
||||
{% set progress_clamped = 100 if progress > 100 else (0 if progress < 0 else progress) %}
|
||||
<div class="px-3 pt-3">
|
||||
<div class="progress" role="progressbar" aria-valuenow="{{ progress_clamped|round(2) }}" aria-valuemin="0"
|
||||
aria-valuemax="100" aria-label="Postęp zbiórki {{ progress_clamped|round(0) }} procent">
|
||||
<div class="progress-bar" style="width: {{ progress_clamped }}%;"></div>
|
||||
</div>
|
||||
<small class="text-muted d-block mt-1 mb-2">{{ progress|round(1) }}%</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card-body {% if zbiorka.typ_zbiorki == 'rezerwa' %}pt-3{% else %}pt-0{% endif %}">
|
||||
<form method="post" novalidate>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="kwota" class="form-label">Kwota wpłaty</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">PLN</span>
|
||||
<input type="number" step="0.01" min="0.01" inputmode="decimal" class="form-control" id="kwota" name="kwota"
|
||||
placeholder="0,00" required aria-describedby="kwotaHelp">
|
||||
</div>
|
||||
<div id="kwotaHelp" class="form-text">Podaj kwotę w złotówkach (min. 0,01).</div>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2 mt-2">
|
||||
{% for preset in [5,10,20,25,30,35,40,50,60,100,150,200] %}
|
||||
<button type="button" class="btn btn-sm btn-outline-light btn-kwota" data-amount="{{ preset }}">
|
||||
{{ preset }} PLN
|
||||
</button>
|
||||
{% endfor %}
|
||||
{% if zbiorka.cel and zbiorka.cel > 0 and zbiorka.typ_zbiorki != 'rezerwa' %}
|
||||
{% set brakujace = (zbiorka.cel - zbiorka.stan) if (zbiorka.cel - zbiorka.stan) > 0 else 0 %}
|
||||
{% if brakujace > 0 %}
|
||||
<button type="button" class="btn btn-sm btn-outline-light btn-kwota"
|
||||
data-amount="{{ brakujace|round(2) }}">
|
||||
Do celu: {{ brakujace|round(2) }} PLN
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="opis" class="form-label">Opis (opcjonalnie)</label>
|
||||
<textarea class="form-control" id="opis" name="opis" rows="3" maxlength="300"
|
||||
aria-describedby="opisHelp"></textarea>
|
||||
<div class="d-flex justify-content-between">
|
||||
<small id="opisHelp" class="form-text text-muted">Krótka notatka do wpłaty (widoczna w systemie).</small>
|
||||
<small class="text-muted"><span id="opisCount">0</span>/300</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button type="submit" class="btn btn-success">Dodaj wpłatę</button>
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}" class="btn btn-outline-light">Anuluj</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block extra_scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ asset_url('js/dodaj_wplate.js') }}"></script>
|
||||
{% endblock %}
|
||||
79
zbiorka_app/templates/admin/dodaj_wydatek.html
Normal file
79
zbiorka_app/templates/admin/dodaj_wydatek.html
Normal file
@@ -0,0 +1,79 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Dodaj wydatek{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}" class="btn btn-sm btn-outline-light">← Powrót
|
||||
do zbiórki</a>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div
|
||||
class="card-header bg-secondary text-white d-flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<h3 class="card-title mb-0">Dodaj wydatek: <span class="fw-semibold">{{ zbiorka.nazwa }}</span></h3>
|
||||
<div class="d-flex align-items-center flex-wrap gap-2">
|
||||
{% if has_cel %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Cel: {{ zbiorka.cel|round(2) }} PLN
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Obecnie: {{ zbiorka.stan|round(2) }} PLN
|
||||
</span>
|
||||
|
||||
{% if has_cel %}
|
||||
{% set delta = zbiorka.cel - zbiorka.stan %}
|
||||
{% if delta > 0 %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Brakuje: {{ delta|round(2) }} PLN
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Nadwyżka: {{ (-delta)|round(2) }} PLN
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<form method="post" novalidate>
|
||||
<div class="mb-3">
|
||||
<label for="kwota" class="form-label">Kwota wydatku</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">PLN</span>
|
||||
<input type="number" step="0.01" min="0.01" inputmode="decimal" class="form-control" id="kwota"
|
||||
name="kwota" placeholder="0,00" required aria-describedby="kwotaHelp">
|
||||
</div>
|
||||
<div id="kwotaHelp" class="form-text">Podaj kwotę w złotówkach (min. 0,01).</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="opis" class="form-label">Opis (opcjonalnie)</label>
|
||||
<textarea class="form-control" id="opis" name="opis" rows="3" maxlength="300"
|
||||
aria-describedby="opisHelp"></textarea>
|
||||
<div class="d-flex justify-content-between">
|
||||
<small id="opisHelp" class="form-text text-muted">Krótka notatka do wydatku (widoczna w
|
||||
systemie).</small>
|
||||
<small class="text-muted"><span id="opisCount">0</span>/300</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button type="submit" class="btn btn-success">Dodaj wydatek</button>
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}"
|
||||
class="btn btn-outline-light">Anuluj</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ asset_url('js/dodaj_wydatek.js') }}"></script>
|
||||
{% endblock %}
|
||||
141
zbiorka_app/templates/admin/edytuj_stan.html
Normal file
141
zbiorka_app/templates/admin/edytuj_stan.html
Normal file
@@ -0,0 +1,141 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Edytuj stan zbiórki{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
|
||||
<!-- Nawigacja -->
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}" class="btn btn-sm btn-outline-light">← Szczegóły zbiórki</a>
|
||||
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-sm btn-outline-light">← Panel Admina</a>
|
||||
</div>
|
||||
|
||||
{# Obliczenia wstępne (do inicjalnego podglądu) #}
|
||||
{% set has_cel = (zbiorka.cel is defined and zbiorka.cel and zbiorka.cel > 0 and zbiorka.typ_zbiorki != 'rezerwa') %}
|
||||
{% set progress = (zbiorka.stan / zbiorka.cel * 100) if has_cel else 0 %}
|
||||
{% set progress_clamped = 100 if progress > 100 else (0 if progress < 0 else progress) %}
|
||||
|
||||
<div class="card shadow-sm" data-module="edit-stan"
|
||||
data-cel="{{ (zbiorka.cel|round(2)) if has_cel else 0 }}"
|
||||
data-typ-zbiorki="{{ zbiorka.typ_zbiorki }}">
|
||||
<div class="card-header bg-secondary text-white d-flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<h3 class="card-title mb-0">Edytuj stan: <span class="fw-semibold">{{ zbiorka.nazwa }}</span></h3>
|
||||
<div class="d-flex align-items-center flex-wrap gap-2">
|
||||
{% if has_cel %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Cel: {{ zbiorka.cel|round(2) }} PLN
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Obecnie: {{ zbiorka.stan|round(2) }} PLN
|
||||
</span>
|
||||
|
||||
{% if has_cel %}
|
||||
{% set delta = zbiorka.cel - zbiorka.stan %}
|
||||
{% if delta > 0 %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Brakuje: {{ delta|round(2) }} PLN
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Nadwyżka: {{ (-delta)|round(2) }} PLN
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mini progress (aktualny) - tylko dla standardowych zbiórek -->
|
||||
{% if zbiorka.typ_zbiorki != 'rezerwa' %}
|
||||
<div class="px-3 pt-3">
|
||||
<div class="progress" role="progressbar" aria-valuenow="{{ progress_clamped|round(2) }}" aria-valuemin="0"
|
||||
aria-valuemax="100" aria-label="Postęp zbiórki {{ progress_clamped|round(0) }} procent">
|
||||
<div class="progress-bar" style="width: {{ progress_clamped }}%;"></div>
|
||||
</div>
|
||||
<small class="text-muted d-block mt-1 mb-2">Aktualnie: {{ progress|round(1) }}%</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card-body {% if zbiorka.typ_zbiorki == 'rezerwa' %}pt-3{% else %}pt-0{% endif %}">
|
||||
<form method="post" novalidate>
|
||||
{# {{ form.csrf_token }} #}
|
||||
|
||||
<!-- Nowy stan -->
|
||||
<div class="mb-3">
|
||||
<label for="stan" class="form-label">Nowy stan zbiórki</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">PLN</span>
|
||||
<input type="number" step="0.01" min="0" class="form-control" id="stan" name="stan"
|
||||
value="{{ zbiorka.stan|round(2) }}" required aria-describedby="stanHelp">
|
||||
</div>
|
||||
<div id="stanHelp" class="form-text">
|
||||
Wpisz łączną zebraną kwotę po zmianie (nie przyrost). Skorzystaj z szybkich korekt poniżej.
|
||||
</div>
|
||||
|
||||
<!-- Szybkie korekty -->
|
||||
<div class="d-flex flex-wrap gap-2 mt-2">
|
||||
{% for delta in [10,50,100,200] %}
|
||||
<button type="button" class="btn btn-sm btn-outline-light btn-delta" data-delta="{{ delta }}">+{{ delta }} PLN</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-light btn-delta" data-delta="-{{ delta }}">-{{ delta }} PLN</button>
|
||||
{% endfor %}
|
||||
{% if has_cel %}
|
||||
<button type="button" class="btn btn-sm btn-outline-light btn-set"
|
||||
data-value="{{ zbiorka.cel|round(2) }}">Ustaw: do celu</button>
|
||||
{% endif %}
|
||||
<button type="button" class="btn btn-sm btn-outline-light btn-set" data-value="0">Ustaw: 0</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Podgląd po zmianie - tylko dla standardowych zbiórek -->
|
||||
{% if zbiorka.typ_zbiorki != 'rezerwa' %}
|
||||
<div class="mb-3">
|
||||
<div class="card bg-dark border" style="border-color: var(--border);">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
|
||||
<div>
|
||||
<div class="small text-muted">Podgląd po zapisaniu</div>
|
||||
<div class="fw-semibold">
|
||||
Procent realizacji:
|
||||
<span id="previewPct">{{ progress|round(1) }}</span>%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<div class="progress" aria-hidden="true">
|
||||
<div id="previewBar" class="progress-bar" style="width: {{ progress_clamped }}%;"></div>
|
||||
</div>
|
||||
<small class="text-muted d-block mt-1" id="previewNote">
|
||||
{% if has_cel %}
|
||||
{% set brakujace = (zbiorka.cel - zbiorka.stan) %}
|
||||
{% if brakujace > 0 %}
|
||||
Do celu brakuje: {{ brakujace|round(2) }} PLN
|
||||
{% elif brakujace == 0 %}
|
||||
Cel osiągnięty.
|
||||
{% else %}
|
||||
Przekroczono cel o: {{ (brakujace * -1)|round(2) }} PLN
|
||||
{% endif %}
|
||||
{% else %}
|
||||
Brak zdefiniowanego celu — procent nie jest wyliczany.
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- CTA -->
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button type="submit" class="btn btn-success">Aktualizuj stan</button>
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}" class="btn btn-outline-light">Anuluj</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ asset_url('js/edytuj_stan.js') }}"></script>
|
||||
{% endblock %}
|
||||
170
zbiorka_app/templates/admin/formularz_rezerwy.html
Normal file
170
zbiorka_app/templates/admin/formularz_rezerwy.html
Normal file
@@ -0,0 +1,170 @@
|
||||
{# templates/admin/formularz_rezerwy.html #}
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% set has_obj = zbiorka is not none %}
|
||||
{% set is_edit = has_obj and zbiorka.id is not none %}
|
||||
|
||||
{% block title %}{{ 'Edytuj listę rezerwową' if is_edit else 'Dodaj listę rezerwową' }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
|
||||
<!-- Nawigacja / powrót -->
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
{% if is_edit and zbiorka and zbiorka.id %}
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}" class="btn btn-sm btn-outline-light">← Szczegóły
|
||||
listy</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('lista_rezerwowych') }}" class="btn btn-sm btn-outline-light">← Listy rezerwowe</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div
|
||||
class="card-header bg-secondary text-white d-flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<h3 class="card-title mb-0">
|
||||
{{ 'Edytuj listę rezerwową' if is_edit else 'Dodaj nową listę rezerwową' }}
|
||||
</h3>
|
||||
|
||||
{% if is_edit %}
|
||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Stan: {{ (zbiorka.stan or 0)|round(2) }} PLN
|
||||
</span>
|
||||
<span class="badge bg-info">Lista rezerwowa</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<small class="opacity-75">Utwórz dedykowaną listę do zarządzania środkami</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<form method="post" novalidate id="{{ 'form-edit-rezerwa' if is_edit else 'form-add-rezerwa' }}">
|
||||
|
||||
<!-- ======================================== -->
|
||||
<!-- PODSTAWOWE DANE -->
|
||||
<!-- ======================================== -->
|
||||
<h5 class="mb-3">Podstawowe dane</h5>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="nazwa" class="form-label">Nazwa listy <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="nazwa" name="nazwa" required
|
||||
value="{{ zbiorka.nazwa if is_edit else '' }}"
|
||||
placeholder="np. Nadpłaty, Środki rezerwowe">
|
||||
<small class="form-text text-muted">Unikalny identyfikator tej listy rezerwowej</small>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="opis" class="form-label">Opis (opcjonalny)</label>
|
||||
<textarea class="form-control" id="opis" name="opis" rows="3"
|
||||
placeholder="Krótki opis przeznaczenia tej listy">{{ zbiorka.opis if is_edit else '' }}</textarea>
|
||||
<small class="form-text text-muted">Krótki opis, który pomoże w identyfikacji</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<!-- ======================================== -->
|
||||
<!-- KANAŁY PŁATNOŚCI -->
|
||||
<!-- ======================================== -->
|
||||
<h5 class="mb-3">Kanały płatności</h5>
|
||||
<p class="text-muted small mb-3">
|
||||
Określ, czy ta lista ma akceptować bezpośrednie wpłaty od użytkowników.
|
||||
</p>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<!-- Przelew -->
|
||||
<div class="col-md-6">
|
||||
<div class="border rounded p-3 h-100">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="uzyj_konta" name="uzyj_konta"
|
||||
{% if is_edit and zbiorka.uzyj_konta %}checked{% endif %}>
|
||||
<label class="form-check-label fw-bold" for="uzyj_konta">
|
||||
Włącz wpłaty przelewem
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="konto-field" style="display: {% if is_edit and zbiorka.uzyj_konta %}block{% else %}none{% endif %};">
|
||||
<label for="numer_konta" class="form-label small">Numer konta</label>
|
||||
<input type="text" class="form-control form-control-sm" id="numer_konta"
|
||||
name="numer_konta"
|
||||
value="{{ zbiorka.numer_konta if is_edit else (global_settings.numer_konta if global_settings else '') }}"
|
||||
placeholder="26 cyfr numeru konta">
|
||||
<small class="form-text text-muted">
|
||||
{% if global_settings and global_settings.numer_konta %}
|
||||
Domyślnie: {{ global_settings.numer_konta }}
|
||||
{% else %}
|
||||
Zostaw puste dla globalnego numeru
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BLIK -->
|
||||
<div class="col-md-6">
|
||||
<div class="border rounded p-3 h-100">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="uzyj_blik" name="uzyj_blik"
|
||||
{% if is_edit and zbiorka.uzyj_blik %}checked{% endif %}>
|
||||
<label class="form-check-label fw-bold" for="uzyj_blik">
|
||||
Włącz wpłaty przez BLIK
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="blik-field" style="display: {% if is_edit and zbiorka.uzyj_blik %}block{% else %}none{% endif %};">
|
||||
<label for="numer_telefonu_blik" class="form-label small">Numer telefonu BLIK</label>
|
||||
<input type="text" class="form-control form-control-sm" id="numer_telefonu_blik"
|
||||
name="numer_telefonu_blik"
|
||||
value="{{ zbiorka.numer_telefonu_blik if is_edit else (global_settings.numer_telefonu_blik if global_settings else '') }}"
|
||||
placeholder="9 cyfr numeru telefonu">
|
||||
<small class="form-text text-muted">
|
||||
{% if global_settings and global_settings.numer_telefonu_blik %}
|
||||
Domyślnie: {{ global_settings.numer_telefonu_blik }}
|
||||
{% else %}
|
||||
Zostaw puste dla globalnego numeru
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if not is_edit %}
|
||||
<!-- Alert informacyjny tylko przy tworzeniu -->
|
||||
<div class="alert alert-info mb-4">
|
||||
<strong>Wskazówka:</strong> Lista rezerwowa to pomocnicze miejsce do gromadzenia środków,
|
||||
które mogą być później przesuwane do konkretnych zbiórek. Jest ukryta dla użytkowników
|
||||
nieadministracyjnych i nie pojawia się na stronie głównej.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<!-- Przyciski akcji -->
|
||||
<div class="d-flex flex-wrap gap-2 justify-content-between align-items-center">
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{{ 'Zapisz zmiany' if is_edit else 'Utwórz listę rezerwową' }}
|
||||
</button>
|
||||
<a href="{{ url_for('lista_rezerwowych') }}" class="btn btn-outline-light">Anuluj</a>
|
||||
</div>
|
||||
|
||||
{% if is_edit %}
|
||||
<div>
|
||||
<a href="{{ url_for('transakcje_zbiorki', zbiorka_id=zbiorka.id) }}"
|
||||
class="btn btn-outline-light btn-sm">Zobacz transakcje</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ asset_url('js/formularz_rezerwy.js') }}"></script>
|
||||
{% endblock %}
|
||||
358
zbiorka_app/templates/admin/formularz_zbiorek.html
Normal file
358
zbiorka_app/templates/admin/formularz_zbiorek.html
Normal file
@@ -0,0 +1,358 @@
|
||||
{# templates/formularz_zbiorek.html #}
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% set has_obj = zbiorka is not none %}
|
||||
{% set is_edit = has_obj and zbiorka.id is not none %}
|
||||
|
||||
{% block title %}{{ 'Edytuj zbiórkę' if is_edit else 'Dodaj zbiórkę' }}{% endblock %}
|
||||
|
||||
{% block extra_head %}
|
||||
{{ super() }}
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
|
||||
<!-- Nawigacja / powrót -->
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
{% if is_edit and zbiorka and zbiorka.id %}
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}" class="btn btn-sm btn-outline-light">← Szczegóły
|
||||
zbiórki</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-sm btn-outline-light">← Panel Admina</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div
|
||||
class="card-header bg-secondary text-white d-flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<h3 class="card-title mb-0">
|
||||
{{ 'Edytuj zbiórkę' if is_edit else 'Dodaj nową zbiórkę' }}
|
||||
</h3>
|
||||
|
||||
{% if is_edit %}
|
||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||
{% if zbiorka.cel %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Cel: {{ zbiorka.cel|round(2) }} PLN
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if not zbiorka.ukryj_kwote %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Stan: {{ (zbiorka.stan or 0)|round(2) }} PLN
|
||||
</span>
|
||||
|
||||
{% if zbiorka.cel %}
|
||||
{% set delta = (zbiorka.cel or 0) - (zbiorka.stan or 0) %}
|
||||
{% if delta > 0 %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Brakuje: {{ delta|round(2) }} PLN
|
||||
</span>
|
||||
{% elif delta < 0 %} <span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Nadwyżka: {{ (-delta)|round(2) }} PLN
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if zbiorka.ukryj_kwote %}
|
||||
<span class="badge bg-secondary">Kwoty niepubliczne</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">Kwoty widoczne</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<small class="opacity-75">Uzupełnij podstawowe dane i dane płatności</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<form method="post" novalidate id="{{ 'form-edit-zbiorka' if is_edit else 'form-add-zbiorka' }}">
|
||||
{# {{ form.csrf_token }} jeśli używasz Flask-WTF #}
|
||||
|
||||
<!-- SEKCJA: Podstawowe -->
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted mb-2">Podstawowe</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label for="nazwa" class="form-label">Nazwa zbiórki</label>
|
||||
<input type="text" class="form-control" id="nazwa" name="nazwa" maxlength="120"
|
||||
placeholder="{{ 'Krótki, zrozumiały tytuł' if is_edit else 'Np. Wsparcie dla schroniska Azor' }}"
|
||||
value="{{ (zbiorka.nazwa if zbiorka else request.form.get('nazwa','')) }}" required
|
||||
aria-describedby="nazwaHelp">
|
||||
<div id="nazwaHelp" class="form-text">Krótki, zrozumiały tytuł. Max 120 znaków.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label for="opis" class="form-label">Opis (Markdown)</label>
|
||||
<textarea class="form-control" id="opis" name="opis" rows="8" required
|
||||
aria-describedby="opisHelp">{{ (zbiorka.opis if zbiorka else request.form.get('opis','')) }}</textarea>
|
||||
<div class="d-flex justify-content-between">
|
||||
<small id="opisHelp" class="form-text text-muted">
|
||||
Możesz używać Markdown (nagłówki, listy, linki). W edytorze włącz podgląd 👁️.
|
||||
</small>
|
||||
<small class="text-muted"><span id="opisCount">0</span> znaków</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4" />
|
||||
|
||||
<!-- SEKCJA: Lista produktów -->
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted mb-2">Lista produktów / Pozycje / Cele</h6>
|
||||
<p class="text-muted small mb-3">
|
||||
Wypunktuj dokładnie produkty do zakupu — podaj nazwę, opcjonalny link do sklepu i cenę.
|
||||
Status domyślnie <code>Do kupienia</code>; przełącz na <code>Kupione</code> po realizacji.
|
||||
</p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table align-middle" id="produkty-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="min-width:220px;">Produkt</th>
|
||||
<th style="min-width:240px;">Link do sklepu</th>
|
||||
<th style="width:140px;">Cena [PLN]</th>
|
||||
<th style="width:160px;">Status</th>
|
||||
<th style="width:60px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="produkty-body">
|
||||
{% set items = (zbiorka.przedmioty if zbiorka and zbiorka.przedmioty else []) %}
|
||||
{% if items %}
|
||||
{% for it in items %}
|
||||
{% set i = loop.index0 %}
|
||||
<tr>
|
||||
<td>
|
||||
<input type="text" class="form-control" name="item_nazwa[]"
|
||||
value="{{ it.nazwa }}" placeholder="np. Karma Brit 10kg" required>
|
||||
</td>
|
||||
<td>
|
||||
<input type="url" class="form-control" name="item_link[]"
|
||||
value="{{ it.link or '' }}" placeholder="https://...">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" inputmode="decimal" class="form-control text-end"
|
||||
name="item_cena[]"
|
||||
value="{{ (it.cena|round(2)) if it.cena is not none else '' }}"
|
||||
placeholder="0,00">
|
||||
</td>
|
||||
<td>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input kupione-switch" type="checkbox" {% if
|
||||
it.kupione %}checked{% endif %}>
|
||||
<input type="hidden" name="item_kupione_val_{{ i }}"
|
||||
value="{{ 1 if it.kupione else 0 }}">
|
||||
<label class="form-check-label small">{{ 'Kupione' if it.kupione else 'Do
|
||||
kupienia' }}</label>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-light remove-row"
|
||||
title="Usuń wiersz">✕</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<!-- pusty wiersz startowy -->
|
||||
<tr>
|
||||
<td><input type="text" class="form-control" name="item_nazwa[]"
|
||||
placeholder="np. Karma Brit 10kg" required></td>
|
||||
<td><input type="url" class="form-control" name="item_link[]"
|
||||
placeholder="https://..."></td>
|
||||
<td><input type="text" inputmode="decimal" class="form-control text-end"
|
||||
name="item_cena[]" placeholder="0,00"></td>
|
||||
<td>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input kupione-switch" type="checkbox">
|
||||
<input type="hidden" name="item_kupione_val_0" value="0">
|
||||
<label class="form-check-label small">Do kupienia</label>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-light remove-row"
|
||||
title="Usuń wiersz">✕</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-sm btn-outline-light" id="add-row">+ Dodaj pozycję</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-light" id="clear-empty">Usuń puste
|
||||
wiersze</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4" />
|
||||
|
||||
<!-- SEKCJA: Dane płatności -->
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted mb-2">Dane płatności</h6>
|
||||
|
||||
<div class="row g-3">
|
||||
<!-- Przełączniki kanałów -->
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="uzyj_konta" name="uzyj_konta" {% if
|
||||
zbiorka is none or zbiorka.uzyj_konta %}checked{% endif %}>
|
||||
<label class="form-check-label" for="uzyj_konta">Przelew na konto</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="uzyj_blik" name="uzyj_blik" {% if
|
||||
zbiorka is none or zbiorka.uzyj_blik %}checked{% endif %}>
|
||||
<label class="form-check-label" for="uzyj_blik">BLIK</label>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div id="kanalyWarning" class="alert alert-warning d-none mt-2" role="alert">
|
||||
Musi być włączony co najmniej jeden kanał wpłat (konto lub BLIK).
|
||||
</div>
|
||||
|
||||
<!-- IBAN -->
|
||||
<div class="col-12" id="pole_konto">
|
||||
<label for="numer_konta" class="form-label">Numer konta (IBAN)</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">PL</span>
|
||||
<input type="text" class="form-control" id="numer_konta" name="numer_konta"
|
||||
inputmode="numeric" autocomplete="off"
|
||||
placeholder="12 3456 7890 1234 5678 9012 3456" {% if zbiorka and not
|
||||
zbiorka.uzyj_konta %}disabled{% endif %} {% if zbiorka is none or zbiorka.uzyj_konta
|
||||
%}required{% endif %} aria-describedby="ibanHelp"
|
||||
value="{% if zbiorka and zbiorka.numer_konta %}{{ zbiorka.numer_konta }}{% elif global_settings %}{{ global_settings.numer_konta }}{% else %}{{ request.form.get('numer_konta','') }}{% endif %}">
|
||||
</div>
|
||||
<div id="ibanHelp" class="form-text">Wpisz ciąg cyfr; spacje dodadzą się automatycznie dla
|
||||
czytelności.</div>
|
||||
</div>
|
||||
|
||||
<!-- BLIK -->
|
||||
<div class="col-12 col-md-6" id="pole_blik">
|
||||
<label for="numer_telefonu_blik" class="form-label">Numer telefonu BLIK</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">+48</span>
|
||||
<input type="tel" class="form-control" id="numer_telefonu_blik"
|
||||
name="numer_telefonu_blik" inputmode="tel" pattern="[0-9 ]{9,13}"
|
||||
placeholder="123 456 789" {% if zbiorka and not zbiorka.uzyj_blik %}disabled{% endif
|
||||
%} {% if zbiorka is none or zbiorka.uzyj_blik %}required{% endif %}
|
||||
aria-describedby="blikHelp"
|
||||
value="{% if zbiorka and zbiorka.numer_telefonu_blik %}{{ zbiorka.numer_telefonu_blik }}{% elif global_settings %}{{ global_settings.numer_telefonu_blik }}{% else %}{{ request.form.get('numer_telefonu_blik','') }}{% endif %}">
|
||||
</div>
|
||||
<div id="blikHelp" class="form-text">Dziewięć cyfr telefonu powiązanego z BLIK. Spacje
|
||||
opcjonalne.</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% if is_edit %}
|
||||
<div class="col-12 col-md-12 d-flex align-items-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-light" id="ustaw-globalne"
|
||||
title="Wstaw wartości z ustawień globalnych" {% if global_settings %}
|
||||
data-iban="{{ global_settings.numer_konta }}"
|
||||
data-blik="{{ global_settings.numer_telefonu_blik }}" {% endif %}>
|
||||
Wstaw globalne ustawienia
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4" />
|
||||
|
||||
<!-- SEKCJA: Cel i widoczność -->
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted mb-2">Cel i widoczność</h6>
|
||||
|
||||
<div id="celSyncBox" class="alert d-none py-2 px-3 mb-3" role="alert">
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<div id="celSyncMsg" class="small"></div>
|
||||
<button type="button" id="btnApplyCelFromSum"
|
||||
class="btn btn-sm btn-outline-light d-none"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="cel" class="form-label">Cel zbiórki</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">PLN</span>
|
||||
<input type="text" inputmode="decimal" class="form-control" id="cel" name="cel"
|
||||
placeholder="0,00" required aria-describedby="celHelp"
|
||||
value="{% if zbiorka and zbiorka.cel is not none %}{{ zbiorka.cel }}{% else %}{{ request.form.get('cel','') }}{% endif %}">
|
||||
</div>
|
||||
<div id="celHelp" class="form-text">Minimalnie 0,01 PLN. Można później edytować.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-12 d-flex align-items-end">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="ukryj_kwote" name="ukryj_kwote" {%
|
||||
if zbiorka %}{% if zbiorka.ukryj_kwote %}checked{% endif %}{% endif %}>
|
||||
<label class="form-check-label" for="ukryj_kwote">Ukryj kwoty (cel i stan)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mt-2">
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="pokaz_postep_finanse"
|
||||
name="pokaz_postep_finanse" data-group="postepy" {% if zbiorka %}{% if
|
||||
zbiorka.pokaz_postep_finanse %}checked{% endif %}{% else %}checked{% endif %}>
|
||||
<label class="form-check-label" for="pokaz_postep_finanse">Pokaż postęp: Finanse</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="pokaz_postep_pozycje"
|
||||
name="pokaz_postep_pozycje" data-group="postepy" {% if zbiorka %}{% if
|
||||
zbiorka.pokaz_postep_pozycje %}checked{% endif %}{% else %}checked{% endif %}>
|
||||
<label class="form-check-label" for="pokaz_postep_pozycje">Pokaż postęp: Zakupy
|
||||
(liczba)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="pokaz_postep_kwotowo"
|
||||
name="pokaz_postep_kwotowo" data-group="postepy" {% if zbiorka %}{% if
|
||||
zbiorka.pokaz_postep_kwotowo %}checked{% endif %}{% else %}checked{% endif %}>
|
||||
<label class="form-check-label" for="pokaz_postep_kwotowo">Pokaż postęp: Zakupy
|
||||
(kwotowo)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div><br>
|
||||
<div id="postepyWarning" class="alert alert-warning d-none mt-2" role="alert">
|
||||
Nie można wyłączyć wszystkich wskaźników postępu. Pozostaw przynajmniej jeden włączony.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA -->
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button type="submit" class="btn btn-success">
|
||||
{{ ' Zaktualizuj zbiórkę' if is_edit else 'Dodaj zbiórkę' }}
|
||||
</button>
|
||||
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-outline-light">Anuluj</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_scripts %}
|
||||
{{ super() }}
|
||||
<script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
|
||||
<script src="{{ asset_url('js/mde_custom.js') }}"></script>
|
||||
<script src="{{ asset_url('js/formularz_zbiorek.js') }}"></script>
|
||||
<script src="{{ asset_url('js/produkty_formularz.js') }}"></script>
|
||||
<script src="{{ asset_url('js/kwoty_formularz.js') }}"></script>
|
||||
<script src="{{ asset_url('js/przelaczniki_zabezpieczenie.js') }}"></script>
|
||||
<script src="{{ asset_url('js/sposoby_wplat.js') }}"></script>
|
||||
{% endblock %}
|
||||
199
zbiorka_app/templates/admin/lista_rezerwowych.html
Normal file
199
zbiorka_app/templates/admin/lista_rezerwowych.html
Normal file
@@ -0,0 +1,199 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Listy rezerwowe{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="container my-4">
|
||||
<!-- Nagłówek + akcje globalne -->
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-4">
|
||||
<div>
|
||||
<h2 class="mb-1">Listy rezerwowe</h2>
|
||||
<p class="text-muted mb-0">Zarządzaj środkami rezerwowymi i nadpłatami</p>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="{{ url_for('dodaj_rezerwe') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Dodaj listę
|
||||
</a>
|
||||
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-outline-light">
|
||||
<i class="bi bi-arrow-left"></i> Panel Admina
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Szybkie statystyki dla rezerw -->
|
||||
{% if rezerwy %}
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card border-info">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-wallet2 fs-1 text-info mb-2"></i>
|
||||
<h3 class="mb-0">{{ rezerwy|length }}</h3>
|
||||
<small class="text-muted">Aktywnych list</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card border-success">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-piggy-bank fs-1 text-success mb-2"></i>
|
||||
<h3 class="mb-0">{{ "%.2f"|format(rezerwy|sum(attribute='stan')) }} zł</h3>
|
||||
<small class="text-muted">Łączna rezerwa</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card border-warning">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-eye fs-1 text-warning mb-2"></i>
|
||||
<h3 class="mb-0">{{ rezerwy|selectattr('ukryta', 'equalto', False)|list|length }}</h3>
|
||||
<small class="text-muted">Widocznych publicznie</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if rezerwy %}
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-hover align-middle mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:72px;">ID</th>
|
||||
<th>Nazwa</th>
|
||||
<th class="text-end" style="width:150px;">Stan</th>
|
||||
<th style="width:140px;">Widoczność</th>
|
||||
<th style="width:1%;">Opcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in rezerwy %}
|
||||
<tr>
|
||||
<td class="text-muted">{{ r.id }}</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=r.id) }}" class="fw-semibold text-decoration-none">
|
||||
<i class="bi bi-wallet2"></i> {{ r.nazwa }}
|
||||
</a>
|
||||
{% if r.opis %}
|
||||
<small class="text-muted">
|
||||
{{ r.opis[:60] }}{% if r.opis|length > 60 %}...{% endif %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<strong class="text-success">{{ "%.2f"|format(r.stan) }} zł</strong>
|
||||
</td>
|
||||
<td>
|
||||
{% if r.ukryta %}
|
||||
<span class="badge bg-secondary border" style="border-color: var(--border);">
|
||||
<i class="bi bi-eye-slash"></i> Ukryta
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">
|
||||
<i class="bi bi-eye"></i> Widoczna
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<!-- Grupa akcji: główne + rozwijane -->
|
||||
<div class="btn-group">
|
||||
<a href="{{ url_for('edytuj_rezerwe', rezerwa_id=r.id) }}"
|
||||
class="btn btn-sm btn-outline-light">
|
||||
<i class="bi bi-pencil"></i> Edytuj
|
||||
</a>
|
||||
<button class="btn btn-sm btn-outline-light dropdown-toggle dropdown-toggle-split"
|
||||
data-bs-toggle="dropdown" aria-expanded="false" aria-label="Więcej opcji">
|
||||
<span class="visually-hidden">Więcej</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end shadow">
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('zbiorka', zbiorka_id=r.id) }}">
|
||||
<i class="bi bi-eye"></i> Podgląd
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('dodaj_wplate', zbiorka_id=r.id) }}">
|
||||
<i class="bi bi-plus-circle text-success"></i> Dodaj wpłatę
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('dodaj_wydatek', zbiorka_id=r.id) }}">
|
||||
<i class="bi bi-dash-circle text-danger"></i> Dodaj wydatek
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('dodaj_przesuniecie', zbiorka_id=r.id) }}">
|
||||
<i class="bi bi-arrow-left-right text-info"></i> Przesuń środki
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('transakcje_zbiorki', zbiorka_id=r.id) }}">
|
||||
<i class="bi bi-list-ul"></i> Transakcje
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ url_for('edytuj_stan', zbiorka_id=r.id) }}">
|
||||
<i class="bi bi-currency-dollar"></i> Edytuj stan
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<form action="{{ url_for('zmien_widzialnosc', zbiorka_id=r.id) }}"
|
||||
method="post" class="m-0">
|
||||
<button type="submit" class="dropdown-item">
|
||||
{% if r.ukryta %}
|
||||
<i class="bi bi-eye"></i> Pokaż
|
||||
{% else %}
|
||||
<i class="bi bi-eye-slash"></i> Ukryj
|
||||
{% endif %}
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<form action="{{ url_for('usun_rezerwe', rezerwa_id=r.id) }}" method="post"
|
||||
class="m-0"
|
||||
onsubmit="return confirm('Czy na pewno usunąć listę \'{{ r.nazwa }}\'?\n\nUWAGA: Zostaną usunięte wszystkie transakcje powiązane z tą listą!');">
|
||||
<button type="submit" class="dropdown-item text-danger">
|
||||
<i class="bi bi-trash"></i> Usuń
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<!-- Empty state -->
|
||||
<div class="card">
|
||||
<div class="card-body text-center py-5">
|
||||
<i class="bi bi-wallet2 fs-1 text-muted mb-3"></i>
|
||||
<h5 class="mb-2">Brak list rezerwowych</h5>
|
||||
<p class="text-muted mb-4">
|
||||
Nie masz jeszcze żadnych list rezerwowych.<br>
|
||||
Utwórz pierwszą, aby zarządzać nadpłatami i środkami rezerwowymi.
|
||||
</p>
|
||||
<a href="{{ url_for('dodaj_rezerwe') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Dodaj listę rezerwową
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
119
zbiorka_app/templates/admin/przesun_wplate.html
Normal file
119
zbiorka_app/templates/admin/przesun_wplate.html
Normal file
@@ -0,0 +1,119 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Przesuń wpłatę - {{ zbiorka.nazwa }}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="container my-5">
|
||||
<!-- Nagłówek -->
|
||||
<div class="mb-4">
|
||||
<h1 class="mb-2">
|
||||
<i class="bi bi-arrow-left-right text-primary"></i> Przesuń konkretną wpłatę
|
||||
</h1>
|
||||
<p class="text-muted">Przenieś wybraną wpłatę do innej zbiórki</p>
|
||||
</div>
|
||||
|
||||
<!-- Szczegóły wpłaty -->
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header bg-transparent">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-receipt text-success"></i> Szczegóły wpłaty
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="border-start border-primary border-3 ps-3">
|
||||
<small class="text-muted d-block">Źródło</small>
|
||||
<strong class="fs-6">{{ zbiorka.nazwa }}</strong>
|
||||
{% if zbiorka.typ_zbiorki == 'rezerwa' %}
|
||||
<span class="badge bg-info ms-2">Lista rezerwowa</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="border-start border-success border-3 ps-3">
|
||||
<small class="text-muted d-block">Kwota</small>
|
||||
<strong class="fs-4 text-success">{{ wplata.kwota|round(2) }} PLN</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="border-start border-secondary border-3 ps-3">
|
||||
<small class="text-muted d-block">Data wpłaty</small>
|
||||
<strong>{{ wplata.data|dt("%d.%m.%Y %H:%M") }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
{% if wplata.opis %}
|
||||
<div class="col-md-6">
|
||||
<div class="border-start border-secondary border-3 ps-3">
|
||||
<small class="text-muted d-block">Opis oryginalny</small>
|
||||
<strong>{{ wplata.opis }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Formularz przesunięcia -->
|
||||
<form method="POST">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header bg-transparent">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-box-arrow-right text-warning"></i> Cel przesunięcia
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="zbiorka_cel_id" class="form-label fw-semibold">
|
||||
Docelowa zbiórka <span class="text-danger">*</span>
|
||||
</label>
|
||||
<select class="form-select" id="zbiorka_cel_id" name="zbiorka_cel_id" required>
|
||||
<option value="">-- Wybierz zbiórkę docelową --</option>
|
||||
{% for zb in dostepne_zbiorki %}
|
||||
<option value="{{ zb.id }}">
|
||||
{% if zb.typ_zbiorki == 'rezerwa' %}
|
||||
[Rezerwa] {{ zb.nazwa }} · Stan: {{ zb.stan|round(2) }} PLN
|
||||
{% else %}
|
||||
{{ zb.nazwa }} · Stan: {{ zb.stan|round(2) }} PLN
|
||||
{% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-0">
|
||||
<label for="opis" class="form-label fw-semibold">
|
||||
Dodatkowy opis przesunięcia <span class="text-muted">(opcjonalny)</span>
|
||||
</label>
|
||||
<textarea class="form-control" id="opis" name="opis" rows="3"
|
||||
placeholder="np. Powód przesunięcia, notatki..."></textarea>
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-info-circle"></i> Oryginalny opis wpłaty zostanie zachowany
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alert informacyjny -->
|
||||
<div class="alert alert-info d-flex align-items-start mb-3">
|
||||
<i class="bi bi-lightbulb fs-5 me-2"></i>
|
||||
<div class="small">
|
||||
<strong>Jak to działa:</strong>
|
||||
<ul class="mb-0 mt-1">
|
||||
<li>Wpłata zostanie przeniesiona do wybranej zbiórki wraz z całą historią</li>
|
||||
<li>Zostanie utworzony wpis o przesunięciu w obu zbiórkach</li>
|
||||
<li>Stany finansowe zostaną automatycznie zaktualizowane</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-arrow-left-right"></i> Wykonaj przesunięcie
|
||||
</button>
|
||||
<a href="{{ url_for('transakcje_zbiorki', zbiorka_id=zbiorka.id) }}"
|
||||
class="btn btn-secondary">Anuluj</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
461
zbiorka_app/templates/admin/statystyki.html
Normal file
461
zbiorka_app/templates/admin/statystyki.html
Normal file
@@ -0,0 +1,461 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Statystyki - Panel Admina{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
|
||||
<!-- Nagłówek + akcje globalne -->
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-4">
|
||||
<h2 class="mb-0">Statystyki systemu</h2>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-outline-light">
|
||||
← Powrót do panelu
|
||||
</a>
|
||||
<a href="{{ url_for('admin_ustawienia') }}" class="btn btn-outline-light">
|
||||
Ustawienia główne
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KARTY PODSUMOWANIA -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card border-success">
|
||||
<div class="card-body text-center">
|
||||
<h6 class="text-success mb-2">Suma wpłat</h6>
|
||||
<h3 class="mb-1">{{ "%.2f"|format(total_wplaty) }} zł</h3>
|
||||
<small class="text-muted">{{ liczba_wplat }} wpłat</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="card border-danger">
|
||||
<div class="card-body text-center">
|
||||
<h6 class="text-danger mb-2">Suma wydatków</h6>
|
||||
<h3 class="mb-1">{{ "%.2f"|format(total_wydatki) }} zł</h3>
|
||||
<small class="text-muted">{{ liczba_wydatkow }} wydatków</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="card border-info">
|
||||
<div class="card-body text-center">
|
||||
<h6 class="text-info mb-2">Suma przesunięć</h6>
|
||||
<h3 class="mb-1">{{ "%.2f"|format(total_przesuniec) }} zł</h3>
|
||||
<small class="text-muted">{{ liczba_przesuniec }} operacji</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="card {% if bilans >= 0 %}border-success{% else %}border-danger{% endif %}">
|
||||
<div class="card-body text-center">
|
||||
<h6 class="{% if bilans >= 0 %}text-success{% else %}text-danger{% endif %} mb-2">Bilans</h6>
|
||||
<h3 class="mb-1 {% if bilans >= 0 %}text-success{% else %}text-danger{% endif %}">
|
||||
{{ "%.2f"|format(bilans) }} zł
|
||||
</h3>
|
||||
<small class="text-muted">{{ liczba_zbiorek }} zbiórek</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pigułki: Podsumowanie / Aktywność / Miesięczne / Roczne -->
|
||||
<ul class="nav nav-pills mb-3" id="statsTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="tab-podsumowanie" data-bs-toggle="tab" data-bs-target="#pane-podsumowanie"
|
||||
type="button" role="tab" aria-controls="pane-podsumowanie" aria-selected="true">
|
||||
Podsumowanie
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="tab-aktywnosc" data-bs-toggle="tab" data-bs-target="#pane-aktywnosc"
|
||||
type="button" role="tab" aria-controls="pane-aktywnosc" aria-selected="false">
|
||||
Aktywność 7/30 dni
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="tab-miesieczne" data-bs-toggle="tab" data-bs-target="#pane-miesieczne"
|
||||
type="button" role="tab" aria-controls="pane-miesieczne" aria-selected="false">
|
||||
Miesięczne
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="tab-roczne" data-bs-toggle="tab" data-bs-target="#pane-roczne"
|
||||
type="button" role="tab" aria-controls="pane-roczne" aria-selected="false">
|
||||
Roczne
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- PANE: Podsumowanie -->
|
||||
<div class="tab-pane fade show active" id="pane-podsumowanie" role="tabpanel" aria-labelledby="tab-podsumowanie" tabindex="0">
|
||||
|
||||
<!-- Rekordy -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h6 class="mb-0">Najwyższa wpłata</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if najwyzsza_wplata %}
|
||||
<h4 class="mb-2">{{ "%.2f"|format(najwyzsza_wplata.kwota) }} zł</h4>
|
||||
<p class="mb-1"><strong>Opis:</strong> {{ najwyzsza_wplata.opis or "Brak opisu" }}</p>
|
||||
<p class="mb-1"><strong>Data:</strong> {{ najwyzsza_wplata.data.strftime('%d.%m.%Y') }}</p>
|
||||
<p class="mb-0"><strong>Zbiórka:</strong>
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=najwyzsza_wplata.zbiorka_id) }}" class="text-decoration-none">
|
||||
{{ najwyzsza_wplata.zbiorka.nazwa }}
|
||||
</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="text-muted mb-0">Brak wpłat</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h6 class="mb-0">Najwyższy wydatek</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if najwyzszy_wydatek %}
|
||||
<h4 class="mb-2">{{ "%.2f"|format(najwyzszy_wydatek.kwota) }} zł</h4>
|
||||
<p class="mb-1"><strong>Opis:</strong> {{ najwyzszy_wydatek.opis or "Brak opisu" }}</p>
|
||||
<p class="mb-1"><strong>Data:</strong> {{ najwyzszy_wydatek.data.strftime('%d.%m.%Y') }}</p>
|
||||
<p class="mb-0"><strong>Zbiórka:</strong>
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=najwyzszy_wydatek.zbiorka_id) }}" class="text-decoration-none">
|
||||
{{ najwyzszy_wydatek.zbiorka.nazwa }}
|
||||
</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="text-muted mb-0">Brak wydatków</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h6 class="mb-0">Średnie wartości</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="mb-2"><strong>Średnia wpłata:</strong> {{ "%.2f"|format(srednia_wplata) }} zł</p>
|
||||
<p class="mb-0"><strong>Średni wydatek:</strong> {{ "%.2f"|format(sredni_wydatek) }} zł</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Top 10 wpłat -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header" style="background: var(--accent); color: #111;">
|
||||
<h6 class="mb-0">Top 10 najwyższych wpłat</h6>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if top_10_wplat %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-hover align-middle mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:60px;">#</th>
|
||||
<th>Kwota</th>
|
||||
<th>Opis</th>
|
||||
<th>Data</th>
|
||||
<th>Zbiórka</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for wplata in top_10_wplat %}
|
||||
<tr>
|
||||
<td class="text-muted">{{ loop.index }}</td>
|
||||
<td><strong class="text-success">{{ "%.2f"|format(wplata.kwota) }} zł</strong></td>
|
||||
<td>{{ wplata.opis or "Brak opisu" }}</td>
|
||||
<td class="text-muted">{{ wplata.data.strftime('%d.%m.%Y') }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=wplata.zbiorka_id) }}" class="text-decoration-none">
|
||||
{{ wplata.zbiorka.nazwa }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card-body text-center py-4">
|
||||
<p class="text-muted mb-0">Brak danych</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Top 5 zbiórek -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h6 class="mb-0">Top 5 zbiórek (największe wpłaty)</h6>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if top_zbiorki %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-hover align-middle mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:60px;">#</th>
|
||||
<th>Nazwa zbiórki</th>
|
||||
<th class="text-end" style="width:180px;">Suma wpłat</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for zbiorka, suma in top_zbiorki %}
|
||||
<tr>
|
||||
<td class="text-muted">{{ loop.index }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}" class="text-decoration-none">
|
||||
{{ zbiorka.nazwa }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-end"><strong>{{ "%.2f"|format(suma) }} zł</strong></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card-body text-center py-4">
|
||||
<p class="text-muted mb-0">Brak danych</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Top 5 źródeł przesunięć -->
|
||||
<div class="card">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h6 class="mb-0">Top 5 źródeł przesunięć</h6>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if top_zrodla_przesuniec %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-hover align-middle mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:60px;">#</th>
|
||||
<th>Zbiórka źródłowa</th>
|
||||
<th class="text-end">Liczba przesunięć</th>
|
||||
<th class="text-end" style="width:180px;">Suma</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for nazwa, liczba, suma in top_zrodla_przesuniec %}
|
||||
<tr>
|
||||
<td class="text-muted">{{ loop.index }}</td>
|
||||
<td>{{ nazwa }}</td>
|
||||
<td class="text-end text-muted">{{ liczba }}</td>
|
||||
<td class="text-end"><strong>{{ "%.2f"|format(suma) }} zł</strong></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card-body text-center py-4">
|
||||
<p class="text-muted mb-0">Brak przesunięć</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- PANE: Aktywność 7/30 dni -->
|
||||
<div class="tab-pane fade" id="pane-aktywnosc" role="tabpanel" aria-labelledby="tab-aktywnosc" tabindex="0">
|
||||
|
||||
<div class="row g-3">
|
||||
<!-- Ostatnie 7 dni -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h6 class="mb-0">Ostatnie 7 dni</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="mb-3">Wpłaty</h5>
|
||||
<p class="mb-1"><strong>Liczba:</strong> {{ wplaty_7dni.liczba or 0 }}</p>
|
||||
<p class="mb-3"><strong>Suma:</strong> {{ "%.2f"|format(wplaty_7dni.suma or 0) }} zł</p>
|
||||
|
||||
<h5 class="mb-3">Wydatki</h5>
|
||||
<p class="mb-1"><strong>Liczba:</strong> {{ wydatki_7dni.liczba or 0 }}</p>
|
||||
<p class="mb-0"><strong>Suma:</strong> {{ "%.2f"|format(wydatki_7dni.suma or 0) }} zł</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ostatnie 30 dni -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h6 class="mb-0">Ostatnie 30 dni</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="mb-3">Wpłaty</h5>
|
||||
<p class="mb-1"><strong>Liczba:</strong> {{ wplaty_30dni.liczba or 0 }}</p>
|
||||
<p class="mb-3"><strong>Suma:</strong> {{ "%.2f"|format(wplaty_30dni.suma or 0) }} zł</p>
|
||||
|
||||
<h5 class="mb-3">Wydatki</h5>
|
||||
<p class="mb-1"><strong>Liczba:</strong> {{ wydatki_30dni.liczba or 0 }}</p>
|
||||
<p class="mb-0"><strong>Suma:</strong> {{ "%.2f"|format(wydatki_30dni.suma or 0) }} zł</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- PANE: Statystyki miesięczne -->
|
||||
<div class="tab-pane fade" id="pane-miesieczne" role="tabpanel" aria-labelledby="tab-miesieczne" tabindex="0">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header" style="background: var(--accent); color: #111;">
|
||||
<h6 class="mb-0">Ostatnie 12 miesięcy</h6>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if wplaty_miesieczne or wydatki_miesieczne or przesuniecia_miesieczne %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-hover align-middle mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Miesiąc</th>
|
||||
<th class="text-end">Wpłaty (suma)</th>
|
||||
<th class="text-end">Wpłaty (liczba)</th>
|
||||
<th class="text-end">Wydatki (suma)</th>
|
||||
<th class="text-end">Wydatki (liczba)</th>
|
||||
<th class="text-end">Przesunięcia (suma)</th>
|
||||
<th class="text-end">Bilans</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% set wplaty_dict = {} %}
|
||||
{% set wydatki_dict = {} %}
|
||||
{% set przesuniecia_dict = {} %}
|
||||
|
||||
{% for rok, miesiac, suma, liczba in wplaty_miesieczne %}
|
||||
{% set klucz = "%d-%02d"|format(rok|int, miesiac|int) %}
|
||||
{% set _ = wplaty_dict.update({klucz: {'suma': suma, 'liczba': liczba}}) %}
|
||||
{% endfor %}
|
||||
|
||||
{% for rok, miesiac, suma, liczba in wydatki_miesieczne %}
|
||||
{% set klucz = "%d-%02d"|format(rok|int, miesiac|int) %}
|
||||
{% set _ = wydatki_dict.update({klucz: {'suma': suma, 'liczba': liczba}}) %}
|
||||
{% endfor %}
|
||||
|
||||
{% for rok, miesiac, suma, liczba in przesuniecia_miesieczne %}
|
||||
{% set klucz = "%d-%02d"|format(rok|int, miesiac|int) %}
|
||||
{% set _ = przesuniecia_dict.update({klucz: {'suma': suma, 'liczba': liczba}}) %}
|
||||
{% endfor %}
|
||||
|
||||
{% set miesiace = (wplaty_dict.keys() | list + wydatki_dict.keys() | list + przesuniecia_dict.keys() | list) | unique | sort | reverse %}
|
||||
|
||||
{% for miesiac_key in miesiace %}
|
||||
{% set wp = wplaty_dict.get(miesiac_key, {'suma': 0, 'liczba': 0}) %}
|
||||
{% set wy = wydatki_dict.get(miesiac_key, {'suma': 0, 'liczba': 0}) %}
|
||||
{% set pr = przesuniecia_dict.get(miesiac_key, {'suma': 0, 'liczba': 0}) %}
|
||||
{% set bilans_m = wp.suma - wy.suma %}
|
||||
<tr>
|
||||
<td><strong>{{ miesiac_key }}</strong></td>
|
||||
<td class="text-end text-success">{{ "%.2f"|format(wp.suma) }} zł</td>
|
||||
<td class="text-end text-muted">{{ wp.liczba }}</td>
|
||||
<td class="text-end text-danger">{{ "%.2f"|format(wy.suma) }} zł</td>
|
||||
<td class="text-end text-muted">{{ wy.liczba }}</td>
|
||||
<td class="text-end text-info">{{ "%.2f"|format(pr.suma) }} zł</td>
|
||||
<td class="text-end {% if bilans_m >= 0 %}text-success{% else %}text-danger{% endif %}">
|
||||
<strong>{{ "%.2f"|format(bilans_m) }} zł</strong>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card-body text-center py-4">
|
||||
<p class="text-muted mb-0">Brak danych</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- PANE: Statystyki roczne -->
|
||||
<div class="tab-pane fade" id="pane-roczne" role="tabpanel" aria-labelledby="tab-roczne" tabindex="0">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header bg-secondary text-white">
|
||||
<h6 class="mb-0">Zestawienie roczne</h6>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if wplaty_roczne or wydatki_roczne %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-hover align-middle mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rok</th>
|
||||
<th class="text-end">Wpłaty (suma)</th>
|
||||
<th class="text-end">Wpłaty (liczba)</th>
|
||||
<th class="text-end">Wydatki (suma)</th>
|
||||
<th class="text-end">Wydatki (liczba)</th>
|
||||
<th class="text-end">Bilans</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% set wplaty_dict = {} %}
|
||||
{% set wydatki_dict = {} %}
|
||||
|
||||
{% for rok, suma, liczba in wplaty_roczne %}
|
||||
{% set _ = wplaty_dict.update({rok|int: {'suma': suma, 'liczba': liczba}}) %}
|
||||
{% endfor %}
|
||||
|
||||
{% for rok, suma, liczba in wydatki_roczne %}
|
||||
{% set _ = wydatki_dict.update({rok|int: {'suma': suma, 'liczba': liczba}}) %}
|
||||
{% endfor %}
|
||||
|
||||
{% set lata = (wplaty_dict.keys() | list + wydatki_dict.keys() | list) | unique | sort | reverse %}
|
||||
|
||||
{% for rok in lata %}
|
||||
{% set wp = wplaty_dict.get(rok, {'suma': 0, 'liczba': 0}) %}
|
||||
{% set wy = wydatki_dict.get(rok, {'suma': 0, 'liczba': 0}) %}
|
||||
{% set bilans_rok = wp.suma - wy.suma %}
|
||||
<tr>
|
||||
<td><strong>{{ rok|int }}</strong></td>
|
||||
<td class="text-end text-success">{{ "%.2f"|format(wp.suma) }} zł</td>
|
||||
<td class="text-end text-muted">{{ wp.liczba }}</td>
|
||||
<td class="text-end text-danger">{{ "%.2f"|format(wy.suma) }} zł</td>
|
||||
<td class="text-end text-muted">{{ wy.liczba }}</td>
|
||||
<td class="text-end {% if bilans_rok >= 0 %}text-success{% else %}text-danger{% endif %}">
|
||||
<strong>{{ "%.2f"|format(bilans_rok) }} zł</strong>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card-body text-center py-4">
|
||||
<p class="text-muted mb-0">Brak danych</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
223
zbiorka_app/templates/admin/transakcje.html
Normal file
223
zbiorka_app/templates/admin/transakcje.html
Normal file
@@ -0,0 +1,223 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Transakcje – {{ zbiorka.nazwa }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-start gap-3 mb-3">
|
||||
<div>
|
||||
<h3 class="mb-1">Transakcje: {{ zbiorka.nazwa }}</h3>
|
||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||
{% if zbiorka.typ_zbiorki == 'rezerwa' %}
|
||||
<span class="badge bg-info">Lista rezerwowa</span>
|
||||
{% endif %}
|
||||
|
||||
{% if zbiorka.cel and zbiorka.typ_zbiorki != 'rezerwa' %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Cel: {{ zbiorka.cel|round(2) }} PLN
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
<span class="badge bg-dark border border-success" style="border-color: var(--border);">
|
||||
Stan: {{ zbiorka.stan|round(2) }} PLN
|
||||
</span>
|
||||
|
||||
{% if zbiorka.cel and zbiorka.cel > 0 and zbiorka.typ_zbiorki != 'rezerwa' %}
|
||||
{% set delta = zbiorka.cel - zbiorka.stan %}
|
||||
{% if delta > 0 %}
|
||||
<span class="badge bg-dark border border-warning">
|
||||
Brakuje: {{ delta|round(2) }} PLN
|
||||
</span>
|
||||
{% elif delta < 0 %}
|
||||
<span class="badge bg-dark border" style="border-color: var(--border);">
|
||||
Nadwyżka: {{ (-delta)|round(2) }} PLN
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge rounded-pill" style="background: var(--accent); color:#111;">Zrealizowana</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group" role="group" aria-label="Akcje zbiórki">
|
||||
<a class="btn btn-sm btn-outline-light" href="{{ url_for('dodaj_wplate', zbiorka_id=zbiorka.id) }}">
|
||||
Dodaj wpłatę
|
||||
</a>
|
||||
<a class="btn btn-sm btn-outline-light" href="{{ url_for('dodaj_wydatek', zbiorka_id=zbiorka.id) }}">
|
||||
Dodaj wydatek
|
||||
</a>
|
||||
<a class="btn btn-sm btn-outline-light" href="{{ url_for('dodaj_przesuniecie', zbiorka_id=zbiorka.id) }}">
|
||||
Przesuń środki
|
||||
</a>
|
||||
<a class="btn btn-sm btn-outline-light" href="{{ url_for('edytuj_stan', zbiorka_id=zbiorka.id) }}">
|
||||
Edytuj stan
|
||||
</a>
|
||||
<a class="btn btn-sm btn-outline-light" href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}">
|
||||
Otwórz ↗
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Data</th>
|
||||
<th>Typ</th>
|
||||
<th>Widoczność</th>
|
||||
<th class="text-end">Kwota</th>
|
||||
<th>Opis</th>
|
||||
<th class="text-end"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for a in aktywnosci %}
|
||||
<tr data-tx-id="{{ a.id }}" data-tx-typ="{{ a.typ }}">
|
||||
<td>{{ a.data|dt("%d.%m.%Y %H:%M") }}</td>
|
||||
<td>
|
||||
<span class="badge {{ 'bg-success' if a.typ=='wpłata' else 'bg-danger' }}">{{ a.typ }}</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{% if a.ukryta %}
|
||||
<span class="badge bg-warning ms-1">ukryta</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success ms-1">widoczna</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end">{{ '%.2f'|format(a.kwota) }} PLN</td>
|
||||
<td class="text-muted">{{ a.opis or '—' }}</td>
|
||||
<td class="text-end">
|
||||
|
||||
<div class="d-inline-flex flex-nowrap align-items-center gap-2">
|
||||
{% if a.typ == 'wpłata' %}
|
||||
<a class="btn btn btn-sm btn-outline-light btn-edit-wplata"
|
||||
href="{{ url_for('przesun_wplate', zbiorka_id=zbiorka.id, wplata_id=a.id) }}"
|
||||
title="Przesuń tę wpłatę"> Przesuń
|
||||
</a>
|
||||
<button class="btn btn-sm btn-outline-light btn-edit-wplata" data-id="{{ a.id }}"
|
||||
data-kwota="{{ '%.2f'|format(a.kwota) }}" data-opis="{{ a.opis|e if a.opis }}"
|
||||
data-action="{{ url_for('zapisz_wplate', wplata_id=a.id) }}">
|
||||
Edytuj
|
||||
</button>
|
||||
|
||||
{% if a.ukryta %}
|
||||
<form class="d-inline" method="post"
|
||||
action="{{ url_for('odkryj_wplate', wplata_id=a.id) }}">
|
||||
<button class="btn btn-sm btn-outline-success">Odkryj</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<form class="d-inline" method="post"
|
||||
action="{{ url_for('ukryj_wplate', wplata_id=a.id) }}">
|
||||
<button class="btn btn-sm btn-outline-warning">Ukryj</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<form class="d-inline" method="post"
|
||||
action="{{ url_for('usun_wplate', wplata_id=a.id) }}"
|
||||
onsubmit="return confirm('Usunąć wpłatę? Cofnie to wpływ na stan.');">
|
||||
<button class="btn btn-sm btn-outline-danger">Usuń</button>
|
||||
</form>
|
||||
|
||||
{% else %}
|
||||
<button class="btn btn-sm btn-outline-light btn-edit-wydatek" data-id="{{ a.id }}"
|
||||
data-kwota="{{ '%.2f'|format(a.kwota) }}" data-opis="{{ a.opis|e if a.opis }}"
|
||||
data-action="{{ url_for('zapisz_wydatek', wydatek_id=a.id) }}">
|
||||
Edytuj
|
||||
</button>
|
||||
|
||||
{% if a.ukryta %}
|
||||
<form class="d-inline" method="post"
|
||||
action="{{ url_for('odkryj_wydatek', wydatek_id=a.id) }}">
|
||||
<button class="btn btn-sm btn-outline-success">Odkryj</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<form class="d-inline" method="post"
|
||||
action="{{ url_for('ukryj_wydatek', wydatek_id=a.id) }}">
|
||||
<button class="btn btn-sm btn-outline-warning">Ukryj</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<form class="d-inline" method="post"
|
||||
action="{{ url_for('usun_wydatek', wydatek_id=a.id) }}"
|
||||
onsubmit="return confirm('Usunąć wydatek? Cofnie to wpływ na stan.');">
|
||||
<button class="btn btn-sm btn-outline-danger">Usuń</button>
|
||||
</form>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-center text-muted py-4">Brak transakcji.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# === MODAL: Edycja wpłaty === #}
|
||||
<div class="modal fade" id="modalWplata" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<form class="modal-content" method="post" id="formWplata">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Edytuj wpłatę</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kwota (PLN)</label>
|
||||
<input class="form-control text-end" name="kwota" step="0.01" min="0.01" id="wplataKwota"
|
||||
inputmode="decimal" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Opis</label>
|
||||
<textarea class="form-control" name="opis" id="wplataOpis" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-success">Zapisz</button>
|
||||
<button type="button" class="btn btn-outline-light" data-bs-dismiss="modal">Anuluj</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# === MODAL: Edycja wydatku === #}
|
||||
<div class="modal fade" id="modalWydatek" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<form class="modal-content" method="post" id="formWydatek">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Edytuj wydatek</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kwota (PLN)</label>
|
||||
<input class="form-control text-end" name="kwota" step="0.01" min="0.01" id="wydatekKwota"
|
||||
inputmode="decimal" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Opis</label>
|
||||
<textarea class="form-control" name="opis" id="wydatekOpis" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-success">Zapisz</button>
|
||||
<button type="button" class="btn btn-outline-light" data-bs-dismiss="modal">Anuluj</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ asset_url('js/transakcje.js') }}"></script>
|
||||
{% endblock %}
|
||||
223
zbiorka_app/templates/admin/ustawienia.html
Normal file
223
zbiorka_app/templates/admin/ustawienia.html
Normal file
@@ -0,0 +1,223 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Ustawienia globalne{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
|
||||
<!-- Nagłówek -->
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-4">
|
||||
<div>
|
||||
<h2 class="mb-1">Ustawienia globalne</h2>
|
||||
<p class="text-muted mb-0">Konfiguracja systemu, płatności i wyglądu</p>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="{{ url_for('admin_statystyki') }}" class="btn btn-outline-light">
|
||||
Statystyki
|
||||
</a>
|
||||
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-outline-light">
|
||||
← Panel Admina
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" novalidate id="form-global-settings">
|
||||
|
||||
<!-- SEKCJA: Dane płatności -->
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header bg-secondary text-white d-flex align-items-center justify-content-between gap-2">
|
||||
<h3 class="card-title mb-0">Dane płatności</h3>
|
||||
<small class="opacity-75">Wartości domyślne dla zbiórek</small>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label for="numer_konta" class="form-label">Globalny numer konta (IBAN)</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">PL</span>
|
||||
<input type="text" class="form-control" id="numer_konta" name="numer_konta"
|
||||
value="{{ settings.numer_konta if settings else '' }}" inputmode="numeric" autocomplete="off"
|
||||
placeholder="12 3456 7890 1234 5678 9012 3456" required aria-describedby="ibanHelp">
|
||||
</div>
|
||||
<div id="ibanHelp" class="form-text">Wpisz ciąg cyfr — spacje dodadzą się automatycznie co 4 znaki</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="numer_telefonu_blik" class="form-label">Globalny numer telefonu BLIK</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">+48</span>
|
||||
<input type="tel" class="form-control" id="numer_telefonu_blik" name="numer_telefonu_blik"
|
||||
value="{{ settings.numer_telefonu_blik if settings else '' }}" inputmode="tel" pattern="[0-9 ]{9,13}"
|
||||
placeholder="123 456 789" required aria-describedby="blikHelp">
|
||||
</div>
|
||||
<div id="blikHelp" class="form-text">9 cyfr. Format 3-3-3 dla czytelności</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SEKCJA: Dostępy / biała lista IP -->
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header bg-secondary text-white d-flex align-items-center justify-content-between gap-2">
|
||||
<h3 class="card-title mb-0">Kontrola dostępu</h3>
|
||||
<small class="opacity-75">Biała lista IP/hostów dla logowania</small>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<!-- Wiersz z inputem i przyciskiem dodawania -->
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-12 col-lg-8">
|
||||
<label for="host_input" class="form-label">Dodaj IP/host</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="host_input"
|
||||
placeholder="np. 203.0.113.42 lub corp.example.com" aria-describedby="hostAddHelp">
|
||||
<button type="button" class="btn btn-outline-light" id="btn-add-host">
|
||||
➕ Dodaj
|
||||
</button>
|
||||
</div>
|
||||
<div id="hostAddHelp" class="form-text">Po wpisaniu kliknij „Dodaj". Duplikaty są pomijane</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-4">
|
||||
<div class="d-flex flex-wrap gap-2 justify-content-lg-end">
|
||||
<button type="button" class="btn btn-light text-dark" id="btn-add-my-ip" data-my-ip="{{ client_ip }}">
|
||||
➕ Dodaj moje IP ({{ client_ip }})
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-light" id="btn-dedupe">
|
||||
Usuń duplikaty
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<label for="dozwolone_hosty_logowania" class="form-label mb-0">
|
||||
Dozwolone hosty logowania (jeden na linię lub rozdzielone przecinkami)
|
||||
</label>
|
||||
<span class="badge text-bg-secondary">Pozycji: <span id="hostsCount">0</span></span>
|
||||
</div>
|
||||
|
||||
<textarea class="form-control" id="dozwolone_hosty_logowania" name="dozwolone_hosty_logowania" rows="6"
|
||||
placeholder="Adresy IP lub nazwy domen — każdy w osobnej linii lub rozdzielony przecinkiem">{{ settings.dozwolone_hosty_logowania if settings and settings.dozwolone_hosty_logowania else '' }}</textarea>
|
||||
|
||||
<small class="text-muted d-block mt-1">
|
||||
Akceptowane separatory: przecinek (`,`), średnik (`;`) i nowa linia
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SEKCJA: Kolejność list rezerwowych -->
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header bg-secondary text-white d-flex align-items-center justify-content-between gap-2">
|
||||
<h3 class="card-title mb-0">Kolejność wyświetlania</h3>
|
||||
<small class="opacity-75">Pozycja list rezerwowych na stronie głównej</small>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="kolejnosc_rezerwowych" class="form-label fw-semibold">Kolejność list rezerwowych</label>
|
||||
<select class="form-select" id="kolejnosc_rezerwowych" name="kolejnosc_rezerwowych">
|
||||
<option value="id" {% if settings and settings.kolejnosc_rezerwowych == 'id' %}selected{% endif %}>
|
||||
Według ID (domyślnie)
|
||||
</option>
|
||||
<option value="first" {% if settings and settings.kolejnosc_rezerwowych == 'first' %}selected{% endif %}>
|
||||
Jako pierwsze
|
||||
</option>
|
||||
<option value="last" {% if settings and settings.kolejnosc_rezerwowych == 'last' %}selected{% endif %}>
|
||||
Jako ostatnie
|
||||
</option>
|
||||
</select>
|
||||
<small class="text-muted d-block mt-1">Określa, gdzie na stronie głównej będą wyświetlane listy rezerwowe względem standardowych zbiórek</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SEKCJA: Branding -->
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header bg-secondary text-white d-flex align-items-center justify-content-between gap-2">
|
||||
<h3 class="card-title mb-0">Branding</h3>
|
||||
<small class="opacity-75">Logo i tytuły wyświetlane w menu i stopce</small>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<!-- Wspólne zasoby: logo + tytuł serwisu -->
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-6">
|
||||
<label for="logo_url" class="form-label">Logo (URL PNG/SVG)</label>
|
||||
<input type="text" class="form-control" id="logo_url" name="logo_url"
|
||||
value="{{ settings.logo_url if settings else '' }}" placeholder="https://example.com/logo.svg">
|
||||
<div class="form-text">Transparentne, do ~60px wysokości</div>
|
||||
{% if settings and settings.logo_url %}
|
||||
<div class="mt-2">
|
||||
<img src="{{ settings.logo_url }}" alt="Logo preview" style="max-height:50px">
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="tytul_strony" class="form-label">Tytuł serwisu</label>
|
||||
<input type="text" class="form-control" id="tytul_strony" name="tytul_strony"
|
||||
value="{{ settings.tytul_strony if settings else '' }}" placeholder="Np. Zbiórki unitraklub.pl">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<!-- NAVBAR -->
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<h6 class="mb-2">Menu (navbar)</h6>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="typ_navbar" id="navbar_mode_logo" value="logo" {% if
|
||||
settings and settings.typ_navbar=='logo' or (settings and settings.pokaz_logo_w_navbar) %}checked{%
|
||||
endif %}>
|
||||
<label class="form-check-label" for="navbar_mode_logo">Pokaż logo</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="typ_navbar" id="navbar_mode_text" value="text" {% if
|
||||
not settings or (settings and settings.typ_navbar !='logo' and not settings.pokaz_logo_w_navbar)
|
||||
%}checked{% endif %}>
|
||||
<label class="form-check-label" for="navbar_mode_text">Pokaż tekst</label>
|
||||
</div>
|
||||
<div class="form-text mt-1">Jeśli wybierzesz logo, użyjemy adresu z pola "Logo URL"</div>
|
||||
</div>
|
||||
|
||||
<!-- STOPKA -->
|
||||
<div class="col-md-6">
|
||||
<h6 class="mb-2">Stopka</h6>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="typ_stopka" id="footer_mode_logo" value="logo" {% if
|
||||
settings and settings.typ_stopka=='logo' %}checked{% endif %}>
|
||||
<label class="form-check-label" for="footer_mode_logo">Logo</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="typ_stopka" id="footer_mode_text" value="text" {% if
|
||||
not settings or (settings and settings.typ_stopka !='logo' ) %}checked{% endif %}>
|
||||
<label class="form-check-label" for="footer_mode_text">Tekst</label>
|
||||
</div>
|
||||
|
||||
<label for="stopka_text" class="form-label mt-2">Tekst w stopce (gdy wybrano „Tekst")</label>
|
||||
<input type="text" class="form-control" id="stopka_text" name="stopka_text"
|
||||
value="{{ settings.stopka_text if settings and settings.stopka_text else '' }}"
|
||||
placeholder="Np. © {{ now().year if now else '2025' }} Zbiórki">
|
||||
<div class="form-text">Pozostaw pusty, by użyć domyślnego</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA -->
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-outline-light">Powrót</a>
|
||||
<button type="submit" class="btn btn-success">Zapisz ustawienia</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ asset_url('js/ustawienia.js') }}"></script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user