przebudowa systemu

This commit is contained in:
Mateusz Gruszczyński
2025-08-28 10:27:06 +02:00
parent d71d33cfe0
commit e9db945bb4
36 changed files with 2307 additions and 809 deletions

View File

@@ -1,24 +0,0 @@
{% extends 'base.html' %}
{% block title %}Dodaj wpłatę{% endblock %}
{% block content %}
<div class="container my-4">
<div class="card shadow-sm">
<div class="card-header bg-warning text-dark">
<h3 class="card-title mb-0">Dodaj wpłatę do zbiórki: {{ zbiorka.nazwa }}</h3>
</div>
<div class="card-body">
<form method="post">
<div class="mb-3">
<label for="kwota" class="form-label">Kwota wpłaty (PLN)</label>
<input type="number" step="0.01" class="form-control" id="kwota" name="kwota" required>
</div>
<div class="mb-3">
<label for="opis" class="form-label">Opis wpłaty (opcjonalnie)</label>
<textarea class="form-control" id="opis" name="opis" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-success">Dodaj wpłatę</button>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,51 +0,0 @@
{% extends 'base.html' %}
{% block title %}Dodaj zbiórkę{% endblock %}
{% block content %}
<div class="container my-4">
<div class="card shadow-sm">
<div class="card-header bg-success text-white">
<h3 class="card-title mb-0">Dodaj nową zbiórkę</h3>
</div>
<div class="card-body">
<form method="post">
<div class="mb-3">
<label for="nazwa" class="form-label">Nazwa zbiórki</label>
<input type="text" class="form-control" id="nazwa" name="nazwa" required>
</div>
<div class="mb-3">
<label for="opis" class="form-label">Opis</label>
<textarea class="form-control" id="opis" name="opis" rows="6" required></textarea>
</div>
<div class="mb-3">
<label for="numer_konta" class="form-label">Numer konta</label>
<input type="text" class="form-control" id="numer_konta" name="numer_konta" value="{{ global_settings.numer_konta if global_settings else '' }}" required>
</div>
<div class="mb-3">
<label for="numer_telefonu_blik" class="form-label">Numer telefonu BLIK</label>
<input type="text" class="form-control" id="numer_telefonu_blik" name="numer_telefonu_blik" value="{{ global_settings.numer_telefonu_blik if global_settings else '' }}" required>
</div>
<div class="mb-3">
<label for="cel" class="form-label">Cel zbiórki (PLN)</label>
<input type="number" step="0.01" class="form-control" id="cel" name="cel" required>
</div>
<div class="form-check mb-3">
<input type="checkbox" class="form-check-input" id="ukryj_kwote" name="ukryj_kwote">
<label class="form-check-label" for="ukryj_kwote">Ukryj kwoty (cel i stan)</label>
</div>
<button type="submit" class="btn btn-success">Dodaj zbiórkę</button>
</form>
</div>
</div>
</div>
<!-- Inicjalizacja edytora Markdown (SimpleMDE) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
<script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
<script>
var simplemde = new SimpleMDE({
element: document.getElementById("opis"),
forceSync: true
});
</script>
{% endblock %}

View File

@@ -1,110 +1,247 @@
{% extends 'base.html' %}
{% block title %}Panel Admina{% endblock %}
{% block content %}
<div class="container my-4">
<h2 class="mb-4">Panel Admina</h2>
<div class="mb-3">
<a href="{{ url_for('dodaj_zbiorka') }}" class="btn btn-success">Dodaj zbiórkę</a>
<a href="{{ url_for('admin_settings') }}" class="btn btn-primary">Ustawienia</a>
</div>
<!-- Tabela zbiórek aktywnych -->
<h4>Aktywne zbiórki</h4>
<div class="table-responsive mb-5">
<table class="table table-dark table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Nazwa</th>
<th>Widoczność</th>
<th>Opcje</th>
</tr>
</thead>
<tbody>
{% for z in active_zbiorki %}
<tr>
<td>{{ z.id }}</td>
<td>{{ z.nazwa }}</td>
<td>
{% if z.ukryta %}
<span class="badge bg-secondary">Ukryta</span>
{% else %}
<span class="badge bg-success">Widoczna</span>
{% endif %}
</td>
<td>
<a href="{{ url_for('edytuj_zbiorka', zbiorka_id=z.id) }}" class="btn btn-primary btn-sm">Edytuj</a>
<a href="{{ url_for('admin_dodaj_wplate', zbiorka_id=z.id) }}" class="btn btn-warning btn-sm">Dodaj wpłatę</a>
<a href="{{ url_for('edytuj_stan', zbiorka_id=z.id) }}" class="btn btn-info btn-sm">Edytuj stan</a>
<!-- Przycisk do oznaczenia jako zrealizowana -->
<form action="{{ url_for('oznacz_zbiorka', zbiorka_id=z.id) }}" method="post" style="display: inline;">
<button type="submit" class="btn btn-warning btn-sm">Oznacz jako zrealizowana</button>
</form>
<form action="{{ url_for('toggle_visibility', zbiorka_id=z.id) }}" method="post" style="display: inline;">
<button type="submit" class="btn btn-secondary btn-sm">
{% if z.ukryta %} Pokaż {% else %} Ukryj {% endif %}
</button>
</form>
<form action="{{ url_for('usun_zbiorka', zbiorka_id=z.id) }}" method="post" style="display: inline;">
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Czy na pewno chcesz usunąć tę zbiórkę?');">Usuń</button>
</form>
</td>
</tr>
{% else %}
<tr>
<td colspan="4" class="text-center">Brak aktywnych zbiórek</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Nagłówek + akcje globalne -->
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-4">
<h2 class="mb-0">Panel Admina</h2>
<div class="d-flex flex-wrap gap-2">
<a href="{{ url_for('dodaj_zbiorke') }}" class="btn btn-primary">
Dodaj zbiórkę
</a>
<a href="{{ url_for('admin_ustawienia') }}" class="btn btn-outline-light border">
⚙️ Ustawienia
</a>
</div>
</div>
<!-- Tabela zbiórek zrealizowanych -->
<h4>Zrealizowane zbiórki</h4>
<div class="table-responsive">
<table class="table table-dark table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Nazwa</th>
<th>Widoczność</th>
<th>Opcje</th>
</tr>
</thead>
<tbody>
{% for z in completed_zbiorki %}
<tr>
<td>{{ z.id }}</td>
<td>{{ z.nazwa }}</td>
<td>
{% if z.ukryta %}
<span class="badge bg-secondary">Ukryta</span>
{% else %}
<span class="badge bg-success">Widoczna</span>
{% endif %}
</td>
<td>
<a href="{{ url_for('edytuj_zbiorka', zbiorka_id=z.id) }}" class="btn btn-primary btn-sm">Edytuj</a>
<a href="{{ url_for('admin_dodaj_wplate', zbiorka_id=z.id) }}" class="btn btn-warning btn-sm">Dodaj wpłatę</a>
<a href="{{ url_for('edytuj_stan', zbiorka_id=z.id) }}" class="btn btn-info btn-sm">Edytuj stan</a>
<form action="{{ url_for('toggle_visibility', zbiorka_id=z.id) }}" method="post" style="display: inline;">
<button type="submit" class="btn btn-secondary btn-sm">
{% if z.ukryta %} Pokaż {% else %} Ukryj {% endif %}
</button>
</form>
<form action="{{ url_for('usun_zbiorka', zbiorka_id=z.id) }}" method="post" style="display: inline;">
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Czy na pewno chcesz usunąć tę zbiórkę?');">Usuń</button>
</form>
</td>
</tr>
<!-- Pigułki: Aktywne / Zrealizowane (zakładki w obrębie panelu) -->
<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">
Aktywne zbiórki
</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">
Zrealizowane
</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">
<span class="fw-semibold">{{ z.nazwa }}</span>
{# opcjonalnie: mini-meta z celem/stanem jeśli masz te pola #}
{% 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 %} · Stan: {{ z.stan|round(2) }} PLN {% endif %}
</small>
{% endif %}
</div>
</td>
<td>
{% if z.ukryta %}
<span class="badge bg-secondary border"
style="border-color: var(--border);">Ukryta</span>
{% else %}
<span class="badge rounded-pill"
style="background: var(--accent); color:#111;">Widoczna</span>
{% endif %}
</td>
<td class="text-end">
<!-- Grupa akcji: główne + rozwijane -->
<div class="btn-group">
<a href="{{ url_for('edytuj_zbiorka', zbiorka_id=z.id) }}"
class="btn btn-sm btn-outline-light">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('dodaj_wplate', zbiorka_id=z.id) }}">Dodaj
wpłatę</a>
</li>
<li>
<a class="dropdown-item"
href="{{ url_for('edytuj_stan', zbiorka_id=z.id) }}">Edytuj stan</a>
</li>
<li>
<hr class="dropdown-divider">
</li>
<li>
<form action="{{ url_for('oznacz_zbiorka', zbiorka_id=z.id) }}"
method="post" class="m-0">
<button type="submit" class="dropdown-item">Oznacz jako
zrealizowana</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 %}Pokaż{% else %}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">Usuń</button>
</form>
</li>
</ul>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<tr>
<td colspan="4" class="text-center">Brak zbiórek zrealizowanych</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Empty state -->
<div class="card">
<div class="card-body text-center py-5">
<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('dodaj_zbiorka') }}" class="btn btn-primary">Utwórz nową zbiórkę</a>
</div>
</div>
{% endif %}
</div>
<!-- PANE: 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">
<span class="fw-semibold">{{ z.nazwa }}</span>
{% 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;">Zrealizowana</span>
{% if z.ukryta %}
<span class="badge bg-secondary border"
style="border-color: var(--border);">Ukryta</span>
{% else %}
<span class="badge bg-success">Widoczna</span>
{% endif %}
</div>
</td>
<td class="text-end">
<div class="btn-group">
<a href="{{ url_for('edytuj_zbiorka', zbiorka_id=z.id) }}"
class="btn btn-sm btn-outline-light">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('dodaj_wplate', zbiorka_id=z.id) }}">Dodaj
wpłatę</a>
</li>
<li>
<a class="dropdown-item"
href="{{ url_for('edytuj_stan', zbiorka_id=z.id) }}">Edytuj stan</a>
</li>
<li>
<hr class="dropdown-divider">
</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 %}Pokaż{% else %}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">Usuń</button>
</form>
</li>
</ul>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="card">
<div class="card-body text-center py-5">
<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('dodaj_zbiorka') }}" class="btn btn-outline-light border">Utwórz nową
zbiórkę</a>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@@ -0,0 +1,86 @@
{% 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 border">← 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 %}
<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>
</div>
</div>
{% 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>
<div class="card-body pt-0">
<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 [10,25,50,100,200] %}
<button type="button" class="btn btn-sm btn-outline-light border btn-kwota" data-amount="{{ preset }}">
{{ preset }} PLN
</button>
{% endfor %}
{% if zbiorka.cel and zbiorka.cel > 0 %}
{% set brakujace = (zbiorka.cel - zbiorka.stan) if (zbiorka.cel - zbiorka.stan) > 0 else 0 %}
<button type="button" class="btn btn-sm btn-outline-light border btn-kwota"
data-amount="{{ brakujace|round(2) }}">
Do celu: {{ brakujace|round(2) }} PLN
</button>
{% 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 border">Anuluj</a>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
{{ super() }}
<script src="{{ url_for('static', filename='js/dodaj_wplate.js') }}"></script>
{% endblock %}

View File

@@ -0,0 +1,125 @@
{% extends 'base.html' %}
{% block title %}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">
<!-- Powrót -->
<div class="d-flex align-items-center gap-2 mb-3">
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-sm btn-outline-light border">← Panel Admina</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 nową zbiórkę</h3>
<small class="opacity-75">Uzupełnij podstawowe dane i dane płatności</small>
</div>
<div class="card-body">
<form method="post" novalidate>
{# {{ 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="Np. Wsparcie dla schroniska 'Azor'" 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"></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: Płatności -->
<div class="mb-4">
<h6 class="text-muted mb-2">Dane płatności</h6>
<div class="row g-3">
<div class="col-12">
<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"
value="{{ global_settings.numer_konta if global_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 dla czytelności.
</div>
</div>
<div class="col-12 col-md-6">
<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"
value="{{ global_settings.numer_telefonu_blik if global_settings else '' }}" inputmode="tel"
pattern="[0-9 ]{9,13}" placeholder="123 456 789" required aria-describedby="blikHelp">
</div>
<div id="blikHelp" class="form-text">Dziewięć cyfr telefonu powiązanego z BLIK. Spacje opcjonalne.</div>
</div>
</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 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="number" class="form-control" id="cel" name="cel" step="0.01" min="0.01" placeholder="0,00"
required aria-describedby="celHelp">
</div>
<div id="celHelp" class="form-text">Minimalnie 0,01 PLN. Możesz to później edytować.</div>
</div>
<div class="col-12 col-md-6 d-flex align-items-end">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="ukryj_kwote" name="ukryj_kwote">
<label class="form-check-label" for="ukryj_kwote">Ukryj kwoty (cel i stan)</label>
</div>
</div>
</div>
</div>
<!-- CTA -->
<div class="d-flex flex-wrap gap-2">
<button type="submit" class="btn btn-success">Dodaj zbiórkę</button>
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-outline-light border">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="{{ url_for('static', filename='js/mde_custom.js') }}"></script>
<script src="{{ url_for('static', filename='js/dodaj_zbiorke.js') }}"></script>
{% endblock %}

View File

@@ -1,67 +0,0 @@
{% extends 'base.html' %}
{% block title %}Edytuj zbiórkę{% endblock %}
{% block content %}
<div class="container my-4">
<div class="card shadow-sm">
<div class="card-header bg-success text-white">
<h3 class="card-title mb-0">Edytuj zbiórkę</h3>
</div>
<div class="card-body">
<form method="post">
<div class="mb-3">
<label for="nazwa" class="form-label">Nazwa zbiórki</label>
<input type="text" class="form-control" id="nazwa" name="nazwa" value="{{ zbiorka.nazwa }}" required>
</div>
<div class="mb-3">
<label for="opis" class="form-label">Opis</label>
<textarea class="form-control" id="opis" name="opis" rows="6" required>{{ zbiorka.opis }}</textarea>
</div>
<div class="mb-3">
<label for="numer_konta" class="form-label">Numer konta</label>
<input type="text" class="form-control" id="numer_konta" name="numer_konta"
value="{{ zbiorka.numer_konta if zbiorka.numer_konta else (global_settings.numer_konta if global_settings else '') }}" required>
</div>
<div class="mb-3">
<label for="numer_telefonu_blik" class="form-label">Numer telefonu BLIK</label>
<input type="text" class="form-control" id="numer_telefonu_blik" name="numer_telefonu_blik"
value="{{ zbiorka.numer_telefonu_blik if zbiorka.numer_telefonu_blik else (global_settings.numer_telefonu_blik if global_settings else '') }}" required>
</div>
<div class="mb-3">
<label for="cel" class="form-label">Cel zbiórki (PLN)</label>
<input type="number" step="0.01" class="form-control" id="cel" name="cel" value="{{ zbiorka.cel }}" required>
</div>
<div class="form-check mb-3">
<input type="checkbox" class="form-check-input" id="ukryj_kwote" name="ukryj_kwote" {% if zbiorka.ukryj_kwote %}checked{% endif %}>
<label class="form-check-label" for="ukryj_kwote">Ukryj kwoty (cel i stan)</label>
</div>
<button type="submit" class="btn btn-primary">Zaktualizuj zbiórkę</button>
<button type="button" class="btn btn-primary" id="ustaw-globalne">Ustaw globalne</button>
<form action="{{ url_for('oznacz_zbiorka', zbiorka_id=zbiorka.id) }}" method="post" style="display:inline;">
<button type="submit" class="btn btn-warning">Oznacz jako zrealizowana</button>
</form>
</form>
</div>
</div>
</div>
<!-- Inicjalizacja edytora Markdown (SimpleMDE) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
<script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
<script>
var simplemde = new SimpleMDE({
element: document.getElementById("opis"),
forceSync: true
});
</script>
<script>
document.getElementById('ustaw-globalne').addEventListener('click', function() {
{% if global_settings %}
document.getElementById('numer_konta').value = "{{ global_settings.numer_konta }}";
document.getElementById('numer_telefonu_blik').value = "{{ global_settings.numer_telefonu_blik }}";
{% endif %}
});
</script>
{% endblock %}

View File

@@ -1,24 +1,128 @@
{% extends 'base.html' %}
{% block title %}Edytuj stan zbiórki{% endblock %}
{% block content %}
<div class="container my-4">
<div class="card shadow-sm">
<div class="card-header bg-info text-white">
<h3 class="card-title mb-0">Edytuj stan zbiórki: {{ zbiorka.nazwa }}</h3>
<!-- 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 border">← Szczegóły
zbiórki</a>
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-sm btn-outline-light border">← 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) %}
{% 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 }}">
<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>
</div>
</div>
<div class="card-body">
<form method="post">
<div class="mb-3">
<label for="stan" class="form-label">Nowy stan zbiórki (PLN)</label>
<div class="input-group">
<span class="input-group-text">PLN</span>
<input type="number" step="0.01" class="form-control" id="stan" name="stan" value="{{ zbiorka.stan|round(2) }}" required>
<!-- Mini progress (aktualny) -->
<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>
<div class="card-body pt-0">
<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 border btn-delta" data-delta="{{ delta }}">+{{
delta }} PLN</button>
<button type="button" class="btn btn-sm btn-outline-light border btn-delta" data-delta="-{{ delta }}">-{{
delta }} PLN</button>
{% endfor %}
{% if has_cel %}
<button type="button" class="btn btn-sm btn-outline-light border btn-set"
data-value="{{ zbiorka.cel|round(2) }}">Ustaw: do celu</button>
{% set brakujace = (zbiorka.cel - zbiorka.stan) if (zbiorka.cel - zbiorka.stan) > 0 else 0 %}
{% if brakujace > 0 %}
<span class="badge bg-dark border" style="border-color: var(--border);">Brakuje: {{ brakujace|round(2) }}
PLN</span>
{% endif %}
{% endif %}
<button type="button" class="btn btn-sm btn-outline-light border btn-set" data-value="0">Ustaw: 0</button>
</div>
</div>
<!-- Podgląd po zmianie -->
<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 class="progress-bar" style="width: {{ progress_clamped }}%;"></div>
</div>
<small class="text-muted d-block mt-1" id="previewNote">
{% if has_cel %}
{% 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>
<button type="submit" class="btn btn-info">Aktualizuj stan</button>
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-secondary">Powrót</a>
</div>
</div>
<!-- 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 border">Anuluj</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
{{ super() }}
<script src="{{ url_for('static', filename='js/edytuj_stan.js') }}"></script>
{% endblock %}

View File

@@ -0,0 +1,154 @@
{% extends 'base.html' %}
{% block title %}Edytuj 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 -->
<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 border">← Szczegóły
zbiórki</a>
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-sm btn-outline-light border">← Panel Admina</a>
</div>
<!-- Nagłówek + meta -->
<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ę</h3>
<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 zbiorka.ukryj_kwote %}
<span class="badge bg-secondary">Kwoty ukryte</span>
{% else %}
<span class="badge bg-success">Kwoty widoczne</span>
{% endif %}
</div>
</div>
<div class="card-body">
<!-- GŁÓWNY FORMULARZ -->
<form method="post" novalidate id="form-edit-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" value="{{ zbiorka.nazwa }}"
maxlength="120" placeholder="Krótki, zrozumiały tytuł" required aria-describedby="nazwaHelp">
<div id="nazwaHelp" class="form-text">Max 120 znaków. Użyj konkretó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 }}</textarea>
<div class="d-flex justify-content-between">
<small id="opisHelp" class="form-text text-muted">Wspieramy **Markdown** — użyj nagłówków, list, linków.
Włącz podgląd w edytorze 👁️.</small>
<small class="text-muted"><span id="opisCount">0</span> znaków</small>
</div>
</div>
</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">
<div class="col-12">
<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"
value="{{ zbiorka.numer_konta if zbiorka.numer_konta else (global_settings.numer_konta if global_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 same cyfry — spacje dodadzą się automatycznie co 4 znaki.</div>
</div>
<div class="col-12 col-md-6">
<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"
value="{{ zbiorka.numer_telefonu_blik if zbiorka.numer_telefonu_blik else (global_settings.numer_telefonu_blik if global_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. Spacje dodadzą się automatycznie (format 3-3-3).</div>
</div>
<div class="col-12 col-md-6 d-flex align-items-end">
<button type="button" class="btn btn-sm btn-outline-light border" 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 %}>Ustaw globalne</button>
</div>
</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 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="number" class="form-control" id="cel" name="cel" step="0.01" min="0.01"
value="{{ zbiorka.cel }}" placeholder="0,00" required aria-describedby="celHelp">
</div>
<div id="celHelp" class="form-text">Minimalnie 0,01 PLN. W razie potrzeby zmienisz to później.</div>
</div>
<div class="col-12 col-md-6 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.ukryj_kwote %}checked{% endif %}>
<label class="form-check-label" for="ukryj_kwote">Ukryj kwoty (cel i stan)</label>
</div>
</div>
</div>
</div>
<!-- CTA: zapisz/anuluj + osobna akcja „zrealizowana” -->
<div class="d-flex flex-wrap gap-2">
<button type="submit" class="btn btn-success">Zaktualizuj zbiórkę</button>
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}" class="btn btn-outline-light border">Anuluj</a>
<!-- Osobny formularz dla oznaczenia „zrealizowana” -->
<form action="{{ url_for('oznacz_zbiorka', zbiorka_id=zbiorka.id) }}" method="post" class="ms-auto">
<button type="submit" class="btn btn-outline-light"
onclick="return confirm('Oznaczyć zbiórkę jako zrealizowaną?');">
Oznacz jako zrealizowana
</button>
</form>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
{{ super() }}
<script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
<script src="{{ url_for('static', filename='js/mde_custom.js') }}"></script>
<script src="{{ url_for('static', filename='js/edytuj_zbiorke.js') }}"></script>
{% endblock %}

View File

@@ -1,55 +0,0 @@
{% extends 'base.html' %}
{% block title %}Ustawienia globalne{% endblock %}
{% block content %}
<div class="container my-4">
<form method="post">
<!-- Blok ustawień konta -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-primary text-white">
<h3 class="card-title mb-0">Ustawienia konta</h3>
</div>
<div class="card-body">
<div class="mb-3">
<label for="numer_konta" class="form-label">Globalny numer konta</label>
<input type="text" class="form-control" id="numer_konta" name="numer_konta" value="{{ settings.numer_konta if settings else '' }}" required>
</div>
<div class="mb-3">
<label for="numer_telefonu_blik" class="form-label">Globalny numer telefonu BLIK</label>
<input type="text" class="form-control" id="numer_telefonu_blik" name="numer_telefonu_blik" value="{{ settings.numer_telefonu_blik if settings else '' }}" required>
</div>
</div>
</div>
<!-- Blok dozwolonych adresów IP -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-secondary text-white d-flex justify-content-between align-items-center">
<h3 class="card-title mb-0">Dozwolone adresy IP</h3>
</div>
<div class="card-body">
<div class="mb-3">
<label for="allowed_login_hosts" class="form-label">Dozwolone hosty logowania</label>
<textarea class="form-control" id="allowed_login_hosts" name="allowed_login_hosts" rows="4" placeholder="Podaj adresy IP lub nazwy domen oddzielone przecinkami lub nowymi liniami">{{ settings.allowed_login_hosts if settings and settings.allowed_login_hosts else '' }}</textarea>
</div>
<p class="text-muted">Twój aktualny adres IP: <strong>{{ client_ip }}</strong></p>
<button type="button" class="btn btn-sm btn-light text-dark" onclick="dodajMojeIP()">Dodaj moje IP</button>
</div>
</div>
<div class="d-flex justify-content-between">
<button type="submit" class="btn btn-primary">Zapisz ustawienia</button>
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-secondary">Powrót</a>
</div>
</form>
</div>
<script>
function dodajMojeIP() {
const mojeIP = "{{ client_ip }}";
const textarea = document.getElementById("allowed_login_hosts");
if (!textarea.value.includes(mojeIP)) {
const separator = textarea.value.trim() === "" ? "" : "\n";
textarea.value += separator + mojeIP;
}
}
</script>
{% endblock %}

View File

@@ -0,0 +1,125 @@
{% extends 'base.html' %}
{% block title %}Ustawienia globalne{% endblock %}
{% block content %}
<div class="container my-4">
<form method="post" novalidate id="form-global-settings">
{# {{ form.csrf_token }} jeśli używasz Flask-WTF #}
<!-- 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">Używane jako wartości domyślne przy dodawaniu/edycji 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. Spacje i format 3-3-3 dodajemy 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">Dostęp — dozwolone adresy IP / hosty</h3>
<small class="opacity-75">Zależnie od konfiguracji, logowanie może wymagać dopasowania do białej listy</small>
</div>
<div class="card-body">
<div class="row g-3 align-items-end">
<div class="col-12 col-md-6">
<label for="host_input" class="form-label">Dodaj pojedynczy IP/host</label>
<input type="text" class="form-control" id="host_input" placeholder="np. 203.0.113.42 lub corp.example.com"
aria-describedby="hostAddHelp">
<div id="hostAddHelp" class="form-text">Po wpisaniu kliknij „Dodaj do listy”. Duplikaty są pomijane.</div>
</div>
<div class="col-12 col-md-6 d-flex gap-2">
<button type="button" class="btn btn-outline-light border" id="btn-add-host">Dodaj do listy</button>
<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 border" id="btn-dedupe">Usuń duplikaty</button>
</div>
</div>
<div class="mt-3">
<label for="allowed_login_hosts" class="form-label">Dozwolone hosty logowania (jeden na linię lub rozdzielone
przecinkami)</label>
<textarea class="form-control" id="allowed_login_hosts" name="allowed_login_hosts" rows="6"
placeholder="Adresy IP lub nazwy domen — każdy w osobnej linii lub rozdzielony przecinkiem">{{ settings.allowed_login_hosts if settings and settings.allowed_login_hosts else '' }}</textarea>
<div class="d-flex justify-content-between mt-1">
<small class="text-muted">Akceptowane separatory: przecinek (`,`), średnik (`;`) i nowa linia.</small>
<small class="text-muted">Pozycji na liście: <span id="hostsCount">0</span></small>
</div>
</div>
</div>
</div>
<div class="mb-4">
<h6 class="text-muted mb-2">Branding</h6>
<div class="row g-3 align-items-end">
<!-- Logo -->
<div class="col-md-6">
<label for="logo_url" class="form-label">Logo (adres 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.png">
<div class="form-text">Najlepiej transparentne, do 60px wysokości.</div>
</div>
<!-- Tekst -->
<div class="col-md-6">
<label for="site_title" class="form-label">Tytuł w navbarze</label>
<input type="text" class="form-control" id="site_title" name="site_title"
value="{{ settings.site_title if settings else '' }}" placeholder="Np. Zbiórki unitraklub.pl">
</div>
</div>
<!-- Checkbox: logo w navbarze -->
<div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="show_logo_in_navbar" name="show_logo_in_navbar" {% if
settings and settings.show_logo_in_navbar %}checked{% endif %}>
<label class="form-check-label" for="show_logo_in_navbar">Wyświetlaj logo w navbarze</label>
</div>
{% if settings and settings.logo_url %}
<div class="mt-3">
<span class="form-text d-block mb-1">Podgląd logo:</span>
<img src="{{ settings.logo_url }}" alt="Logo preview" style="max-height:50px;">
</div>
{% endif %}
</div>
<!-- CTA -->
<div class="d-flex justify-content-between">
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-outline-light border">Powrót</a>
<button type="submit" class="btn btn-primary">Zapisz ustawienia</button>
</div>
</form>
</div>
{% endblock %}
{% block extra_scripts %}
{{ super() }}
<script src="{{ url_for('static', filename='js/ustawienia.js') }}"></script>
{% endblock %}

View File

@@ -1,74 +1,83 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>{% block title %}Aplikacja Zbiórek{% endblock %}</title>
<!-- Bootswatch Darkly - atrakcyjny ciemny motyw -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.0/dist/darkly/bootstrap.min.css">
<!-- Opcjonalny plik custom.css dla drobnych modyfikacji -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/custom.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/custom.css') }}" />
{% block extra_head %}{% endblock %}
</head>
<body class="bg-dark text-light">
<nav class="navbar navbar-expand-lg navbar-dark bg-secondary">
<body class="d-flex flex-column min-vh-100">
<nav class="navbar navbar-expand-lg">
<div class="container">
<a class="navbar-brand" href="{{ url_for('index') }}">Zbiórki unitraklub.pl</a>
<!-- Przycisk rozwijania dla urządzeń mobilnych -->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNavbar"
aria-controls="mainNavbar" aria-expanded="false" aria-label="Toggle navigation">
<a class="navbar-brand" href="{{ url_for('index') }}">
{% if global_settings and global_settings.show_logo_in_navbar and global_settings.logo_url %}
<img src="{{ global_settings.logo_url }}" alt="Logo" style="max-height:40px; vertical-align:middle;">
{% endif %}
{{ global_settings.site_title if global_settings and global_settings.site_title else "Zbiórki" }}
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNavbar"
aria-controls="mainNavbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- Nawigacja ukrywana na małych ekranach -->
<div class="collapse navbar-collapse" id="mainNavbar">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a class="nav-link" href="{{ url_for('index') }}">Aktualne zbiórki</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('zbiorki_zrealizowane') }}">Zrealizowane zbiórki</a></li>
{% if current_user.is_authenticated %}
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin_dashboard') }}">Panel Admina</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('logout') }}">Wyloguj</a></li>
{% else %}
{% if is_ip_allowed %}
<li class="nav-item"><a class="nav-link" href="{{ url_for('login') }}">Zaloguj</a></li>
{% endif %}
{% set hide_links = request.path == url_for('index') or request.path ==
url_for('zbiorki_zrealizowane') %}
{% if not hide_links %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('index') }}">Aktualne zbiórki</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('zbiorki_zrealizowane') }}">Zrealizowane zbiórki</a>
</li>
{% endif %}
{% if current_user.is_authenticated %}
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin_dashboard') }}">Panel Admina</a>
</li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('wyloguj') }}">Wyloguj</a></li>
{% else %}
{% if is_ip_allowed %}
<li class="nav-item"><a class="nav-link" href="{{ url_for('zaloguj') }}">Zaloguj</a></li>
{% endif %}
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category|default('secondary') }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
<main class="flex-grow-1">
{% block content %}{% endblock %}
</main>
</div>
<!-- stopka -->
<footer class="mt-auto text-center py-3 border-top" style="background: var(--surface-0);">
{% if global_settings and global_settings.logo_url %}
<img src="{{ global_settings.logo_url }}" alt="Logo" style="max-height:60px; opacity:0.85;">
{% else %}
<small class="text-muted">© linuxiarz.pl</small>
{% endif %}
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Funkcja aktualizująca animację paska postępu
function animateProgressBars() {
document.querySelectorAll('.progress-bar').forEach(bar => {
const progressValue = bar.getAttribute('aria-valuenow');
bar.style.setProperty('--progress-width', progressBarWidth(progressBarValue(progressBar)));
});
}
// Funkcja wywoływana przy ładowaniu strony
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.progress-bar').forEach(bar => {
const width = bar.getAttribute('aria-valuenow') + '%';
bar.style.setProperty('--progress-width', width);
});
});
</script>
<script src="{{ url_for('static', filename='js/progress.js') }}"></script>
{% block extra_scripts %}{% endblock %}
</body>
</html>
</html>

View File

@@ -1,29 +1,88 @@
{% extends 'base.html' %}
{% block title %}{% if request.path == url_for('zbiorki_zrealizowane') %}Zrealizowane zbiórki{% else %}Aktualnie aktywne zbiórki{% endif %}{% endblock %}
{% block title %}{% if request.path == url_for('zbiorki_zrealizowane') %}Zrealizowane zbiórki{% else %}Aktualnie aktywne
zbiórki{% endif %}{% endblock %}
{% block content %}
{% if request.path == url_for('zbiorki_zrealizowane') %}
<h2 class="mb-4">Zrealizowane zbiórki</h2>
{% else %}
<h2 class="mb-4">Aktualnie aktywne zbiórki</h2>
{% endif %}
<div class="row">
{# Ustal kontekst listy #}
{% set is_completed_view = (request.path == url_for('zbiorki_zrealizowane')) %}
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-4">
<h2 class="mb-0">
{% if is_completed_view %}Zrealizowane zbiórki{% else %}Aktualnie aktywne zbiórki{% endif %}
</h2>
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link {% if not is_completed_view %}active{% endif %}"
href="{{ url_for('index') }}">Aktywne</a>
</li>
<li class="nav-item">
<a class="nav-link {% if is_completed_view %}active{% endif %}"
href="{{ url_for('zbiorki_zrealizowane') }}">Zrealizowane</a>
</li>
</ul>
</div>
{% if zbiorki and zbiorki|length > 0 %}
<div class="row g-4">
{% for z in zbiorki %}
<div class="col-sm-12 col-md-6 col-lg-4 mb-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{ z.nazwa }}</h5>
{% set progress = (z.stan / z.cel * 100) if z.cel > 0 else 0 %}
<div class="progress mb-3">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: {{ progress if progress < 100 else 100 }}%;" aria-valuenow="{{ progress }}" aria-valuemin="0" aria-valuemax="100">
{{ progress|round(2) }}%
</div>
{% set progress = (z.stan / z.cel * 100) if z.cel > 0 else 0 %}
{% set progress_clamped = 100 if progress > 100 else (0 if progress < 0 else progress) %} <div
class="col-sm-12 col-md-6 col-lg-4">
<div class="card h-100 position-relative">
<div class="card-body d-flex flex-column">
<div class="d-flex align-items-start justify-content-between gap-2 mb-2">
<h5 class="card-title mb-0">{{ z.nazwa }}</h5>
{# Spójny badge zrealizowania: w zakładce „Zrealizowane” lub gdy >=100% #}
{% if is_completed_view or progress_clamped >= 100 %}
<span class="badge rounded-pill" style="background: var(--accent); color:#111;">Zrealizowana</span>
{% endif %}
</div>
<div class="mb-2 d-flex flex-wrap gap-2">
{% if z.cel > 0 %}
<span class="badge bg-dark border" style="border-color: var(--border);">Cel: {{ z.cel|round(2) }}
PLN</span>
{% endif %}
<span class="badge bg-dark border" style="border-color: var(--border);">Stan: {{ z.stan|round(2) }}
PLN</span>
</div>
<div class="mb-1">
<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">{{ progress|round(1) }}%</small>
</div>
<div class="mt-auto pt-2">
<a href="{{ url_for('zbiorka', zbiorka_id=z.id) }}" class="stretched-link"></a>
<a href="{{ url_for('zbiorka', zbiorka_id=z.id) }}" class="btn btn-primary btn-sm">Szczegóły</a>
</div>
<a href="{{ url_for('zbiorka', zbiorka_id=z.id) }}" class="btn btn-primary btn-sm">Szczegóły</a>
</div>
</div>
</div>
{% else %}
<p>Brak zbiórek</p>
{% endfor %}
</div>
{% endblock %}
{% endfor %}
</div>
{% else %}
<div class="card">
<div class="card-body text-center py-5">
{% if is_completed_view %}
<h5 class="mb-2">Brak zrealizowanych zbiórek</h5>
<p class="text-muted mb-4">Gdy jakaś zbiórka osiągnie 100%, pojawi się tutaj.</p>
<a href="{{ url_for('index') }}" class="btn btn-primary">Zobacz aktywne</a>
{% else %}
<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>
{% if current_user.is_authenticated and current_user.is_admin %}
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-primary">Utwórz nową zbiórkę</a>
{% else %}
<a href="{{ url_for('zbiorki_zrealizowane') }}" class="btn btn-primary">Zobacz zrealizowane</a>
{% endif %}
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -1,19 +1,55 @@
{% extends 'base.html' %}
{% block title %}Logowanie{% endblock %}
{% block content %}
<div class="container my-4">
<h3 class="mb-4">Logowanie</h3>
<form method="post">
<div class="mb-3">
<label for="username" class="form-label">Nazwa użytkownika</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Hasło</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Zaloguj</button>
</form>
<p class="mt-3">Nie masz konta? <a href="{{ url_for('register') }}">Zarejestruj się</a></p>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-sm-10 col-md-8 col-lg-5">
<div class="card shadow-sm">
<div class="card-header">
<h3 class="card-title mb-0">Logowanie</h3>
</div>
<div class="card-body">
<form method="post" class="needs-validation" novalidate>
{% set next_url = request.args.get('next') %}
{% if next_url %}
<input type="hidden" name="next" value="{{ next_url }}">
{% endif %}
<div class="mb-3">
<label for="username" class="form-label">Nazwa użytkownika</label>
<input type="text" class="form-control" id="username" name="username"
autocomplete="username" autocapitalize="none" spellcheck="false" required autofocus>
<div class="invalid-feedback">Podaj nazwę użytkownika.</div>
</div>
<div class="mb-2">
<label for="password" class="form-label d-flex justify-content-between align-items-center">
<span>Hasło</span>
<small id="capsWarning" class="text-muted" style="display:none;">CAPS LOCK
włączony</small>
</label>
<div class="input-group">
<input type="password" class="form-control" id="password" name="password"
autocomplete="current-password" required minlength="5">
<button type="button" class="btn btn-secondary rounded-end" id="togglePw"
aria-label="Pokaż/ukryj hasło">Pokaż</button>
<div class="invalid-feedback">Wpisz hasło (min. 5 znaków).</div>
</div>
</div>
<button type="submit" class="btn btn-primary w-100">Zaloguj</button>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script src="{{ url_for('static', filename='js/walidacja_logowanie.js') }}"></script>
{% endblock %}

View File

@@ -1,18 +1,61 @@
{% extends 'base.html' %}
{% block title %}Rejestracja{% endblock %}
{% block content %}
<div class="container my-4">
<h3 class="mb-4">Rejestracja</h3>
<form method="post">
<div class="mb-3">
<label for="username" class="form-label">Nazwa użytkownika</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Hasło</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Zarejestruj się</button>
</form>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-sm-10 col-md-8 col-lg-5">
<div class="card shadow-sm">
<div class="card-header">
<h3 class="card-title mb-0">Rejestracja</h3>
</div>
<div class="card-body">
<form method="post" class="needs-validation" novalidate>
<div class="mb-3">
<label for="username" class="form-label">Nazwa użytkownika</label>
<input type="text" class="form-control" id="username" name="username"
autocomplete="username" autocapitalize="none" spellcheck="false" required autofocus>
<div class="invalid-feedback">Podaj nazwę użytkownika.</div>
</div>
<div class="mb-3">
<label for="password" class="form-label d-flex justify-content-between align-items-center">
<span>Hasło</span>
<small id="capsWarning" class="text-muted" style="display:none;">CAPS LOCK
włączony</small>
</label>
<div class="input-group">
<input type="password" class="form-control" id="password" name="password"
autocomplete="new-password" required minlength="6">
<button type="button" class="btn btn-secondary" id="togglePw"
aria-label="Pokaż/ukryj hasło">Pokaż</button>
<div class="invalid-feedback">Hasło musi mieć min. 6 znaków.</div>
</div>
</div>
<div class="mb-4">
<label for="password2" class="form-label">Powtórz hasło</label>
<input type="password" class="form-control" id="password2" name="password2"
autocomplete="new-password" required minlength="6">
<div class="invalid-feedback">Powtórz hasło.</div>
</div>
<button type="submit" class="btn btn-primary w-100">Zarejestruj się</button>
</form>
<p class="mt-3 mb-0 text-center">
Masz już konto?
<a href="{{ url_for('zaloguj') }}">Zaloguj się</a>
</p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script src="{{ url_for('static', filename='js/walidacja_rejestracja.js') }}"></script>
{% endblock %}

View File

@@ -1,94 +1,156 @@
{% extends 'base.html' %}
{% block title %}{{ zbiorka.nazwa }}{% endblock %}
{% block content %}
<div class="container my-4">
<!-- Główna karta zbiórki -->
<div class="card mb-4 shadow-sm">
<div class="card-header bg-secondary text-white">
<h3 class="card-title mb-0"></h3>{{ zbiorka.nazwa }}</h3>
</div>
<div class="card-body">
<div class="row">
<!-- Lewa kolumna: opis i postęp -->
<div class="col-md-8">
<h5>Opis:</h5>
<div class="mb-3">
{{ zbiorka.opis | markdown }}
</div>
{% set progress = (zbiorka.stan / zbiorka.cel * 100) if zbiorka.cel > 0 else 0 %}
<h5>Postęp:</h5>
<div class="progress mb-3">
<div class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar"
style="width: {{ progress if progress < 100 else 100 }}%;"
aria-valuenow="{{ progress }}"
aria-valuemin="0"
aria-valuemax="100">
{{ progress|round(2) }}%
</div>
</div>
</div>
<!-- Prawa kolumna: sekcja "Wspomóż" -->
<div class="col-md-4">
<div class="card wspomoz-card mb-3">
<div class="card-body">
<p class="card-text">
<strong>Numer konta:</strong>
<span class="fs-4">{{ zbiorka.numer_konta }}</span>
</p>
<p class="card-text">
<strong>Telefon BLIK:</strong>
<span class="fs-4">{{ zbiorka.numer_telefonu_blik }}</span>
</p>
{% if not zbiorka.ukryj_kwote %}
<hr>
<p class="card-text">
<strong>Cel zbiórki:</strong>
<span class="fs-4">{{ zbiorka.cel|round(2) }} PLN</span>
</p>
<p class="card-text">
<strong>Stan zbiórki:</strong>
<span class="fs-4">{{ zbiorka.stan|round(2) }} PLN</span>
</p>
{% endif %}
</div>
</div>
</div>
{# Postęp 0100 #}
{% set has_cel = (zbiorka.cel is defined and zbiorka.cel and zbiorka.cel > 0) %}
{% 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) %} {% set
is_done=(progress_clamped>= 100) %}
</div>
<div class="d-flex justify-content-between">
{% if current_user.is_authenticated and current_user.is_admin %}
<a href="{{ url_for('admin_dodaj_wplate', zbiorka_id=zbiorka.id) }}" class="btn btn-primary">Dodaj wpłatę</a>
<!-- Nagłówek -->
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-3">
<h2 class="mb-0">{{ zbiorka.nazwa }}</h2>
<div class="d-flex flex-wrap align-items-center gap-2">
{% if is_done %}
<span class="badge rounded-pill" style="background: var(--accent); color:#111;">Zrealizowana</span>
{% endif %}
{% if zbiorka.ukryj_kwote %}
<span class="badge bg-secondary">Kwoty ukryte</span>
{% else %}
<span class="badge bg-success">Kwoty widoczne</span>
{% endif %}
<a href="{{ url_for('index') }}" class="btn btn-primary">Powrót do listy</a>
</div>
</div>
</div>
<!-- Karta z historią wpłat -->
<div class="card shadow-sm">
<div class="card-header bg-secondary text-white">
<h3 class="card-title mb-0">Historia wpłat</h3>
</div>
<div class="card-body">
{% if zbiorka.wplaty|length > 0 %}
<ul class="list-group">
{% for w in zbiorka.wplaty %}
<li class="list-group-item">
<strong>{{ w.data.strftime('%Y-%m-%d %H:%M:%S') }}</strong> {{ w.kwota|round(2) }} PLN
{% if w.opis %}
<em class="text-muted">({{ w.opis }})</em>
<div class="row g-4">
<!-- Kolumna: opis + progress -->
<div class="col-md-8">
<div class="card shadow-sm h-100">
<div class="card-body">
<h5 class="mb-2">Opis</h5>
<div class="mb-4">
{{ zbiorka.opis | markdown }}
</div>
<h5 class="mb-2">Postęp</h5>
<div class="progress mb-2" 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">
{% if zbiorka.ukryj_kwote %}
{% else %}
{{ progress|round(1) }}%
{% endif %}
</li>
</small>
</div>
</div>
</div>
<!-- Kolumna: płatności (sticky) -->
<div class="col-md-4">
<div class="card shadow-sm wspomoz-card position-sticky" style="top: 1rem;">
<div class="card-body">
<div class="mb-3">
<div class="d-flex align-items-center justify-content-between">
<strong>Numer konta</strong>
<button class="btn btn-sm btn-outline-light border" type="button"
data-copy-target="#ibanDisplay">Kopiuj</button>
</div>
<div class="fs-5" id="ibanDisplay">{{ zbiorka.numer_konta }}</div>
</div>
<div class="mb-3">
<div class="d-flex align-items-center justify-content-between">
<strong>Telefon BLIK</strong>
<button class="btn btn-sm btn-outline-light border" type="button"
data-copy-target="#blikDisplay">Kopiuj</button>
</div>
<div class="fs-5" id="blikDisplay">{{ zbiorka.numer_telefonu_blik }}</div>
</div>
{% if not zbiorka.ukryj_kwote %}
<hr class="my-3">
<div class="d-flex flex-column gap-1">
{% if has_cel %}
<div><strong>Cel:</strong> <span class="fs-6">{{ zbiorka.cel|round(2) }} PLN</span></div>
{% endif %}
<div><strong>Stan:</strong> <span class="fs-6">{{ zbiorka.stan|round(2) }} PLN</span></div>
{% if has_cel %}
{% set brak = (zbiorka.cel - zbiorka.stan) %}
<small class="text-muted">
{% if brak > 0 %}
Do celu brakuje: {{ brak|round(2) }} PLN
{% elif brak == 0 %}
Cel osiągnięty.
{% else %}
Przekroczono cel o: {{ (brak * -1)|round(2) }} PLN
{% endif %}
</small>
{% endif %}
</div>
{% endif %}
{% if current_user.is_authenticated and current_user.is_admin %}
<div class="d-grid mt-3">
<a href="{{ url_for('dodaj_wplate', zbiorka_id=zbiorka.id) }}" class="btn btn-primary">Dodaj
wpłatę</a>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Historia wpłat -->
<div class="card shadow-sm mt-4">
<div class="card-header d-flex align-items-center justify-content-between">
<h5 class="card-title mb-0">Historia wpłat</h5>
{% if zbiorka.wplaty|length > 0 %}
<small class="text-muted">Łącznie pozycji: {{ zbiorka.wplaty|length }}</small>
{% endif %}
</div>
<div class="card-body">
{% if zbiorka.wplaty and zbiorka.wplaty|length > 0 %}
<ul class="list-group list-group-flush">
{% for w in zbiorka.wplaty %}
<li class="list-group-item bg-transparent d-flex flex-wrap justify-content-between align-items-center">
<div class="me-3">
<strong>{{ w.data.strftime('%Y-%m-%d %H:%M:%S') }}</strong>
{% if w.opis %}
<span class="text-muted">— {{ w.opis }}</span>
{% endif %}
</div>
<span class="badge bg-dark border ms-auto" style="border-color: var(--border);">
{{ w.kwota|round(2) }} PLN
</span>
</li>
{% endfor %}
</ul>
{% else %}
<p class="text-center">Aktualnie brak wpłat..</p>
{% endif %}
{% else %}
<div class="text-center py-4">
<h6 class="mb-1">Brak wpłat</h6>
<p class="text-muted mb-0">Gdy pojawią się pierwsze wpłaty, zobaczysz je tutaj.</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Akcje dolne -->
<div class="d-flex gap-2 justify-content-between mt-3">
<div></div>
<a href="{{ url_for('index') }}" class="btn btn-outline-light border">Powrót do listy</a>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
{{ super() }}
<script src="{{ url_for('static', filename='js/zbiorka.js') }}"></script>
<script src="{{ url_for('static', filename='js/progress.js') }}"></script>
{% endblock %}