zmiany w logice i endpoitach
This commit is contained in:
157
app.py
157
app.py
@@ -13,7 +13,7 @@ from datetime import datetime
|
||||
from markupsafe import Markup
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.engine import Engine
|
||||
|
||||
from decimal import Decimal, InvalidOperation
|
||||
import markdown as md
|
||||
from flask import request, flash, abort
|
||||
import os
|
||||
@@ -93,6 +93,7 @@ class GlobalSettings(db.Model):
|
||||
footer_brand_mode = db.Column(db.String(10), default="text")
|
||||
footer_text = db.Column(db.String(200), nullable=True)
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return User.query.get(int(user_id))
|
||||
@@ -283,65 +284,81 @@ def admin_dashboard():
|
||||
|
||||
|
||||
@app.route("/admin/zbiorka/dodaj", methods=["GET", "POST"])
|
||||
@app.route("/admin/zbiorka/edytuj/<int:zbiorka_id>", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def dodaj_zbiorke():
|
||||
def formularz_zbiorek(zbiorka_id=None):
|
||||
if not current_user.is_admin:
|
||||
flash("Brak uprawnień", "danger")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
global_settings = GlobalSettings.query.first() # Pobieramy globalne ustawienia
|
||||
# Tryb
|
||||
is_edit = zbiorka_id is not None
|
||||
zb = Zbiorka.query.get_or_404(zbiorka_id) if is_edit else None
|
||||
global_settings = GlobalSettings.query.first()
|
||||
|
||||
if request.method == "POST":
|
||||
nazwa = request.form["nazwa"]
|
||||
opis = request.form["opis"]
|
||||
# Pozyskujemy numer konta i telefon z formularza (mogą być nadpisane ręcznie)
|
||||
numer_konta = request.form["numer_konta"]
|
||||
numer_telefonu_blik = request.form["numer_telefonu_blik"]
|
||||
cel = float(request.form["cel"])
|
||||
ukryj_kwote = "ukryj_kwote" in request.form
|
||||
# Pola wspólne
|
||||
nazwa = request.form.get("nazwa", "").strip()
|
||||
opis = request.form.get("opis", "").strip()
|
||||
|
||||
nowa_zbiorka = Zbiorka(
|
||||
# IBAN/telefon — oczyść z nadmiarowych znaków odstępu (zostaw spacje w prezentacji frontu)
|
||||
numer_konta = request.form.get("numer_konta", "").strip()
|
||||
numer_telefonu_blik = request.form.get("numer_telefonu_blik", "").strip()
|
||||
|
||||
# Cel — walidacja liczby
|
||||
try:
|
||||
cel_str = request.form.get("cel", "").replace(",", ".").strip()
|
||||
cel = Decimal(cel_str)
|
||||
if cel <= Decimal("0"):
|
||||
raise InvalidOperation
|
||||
except (InvalidOperation, ValueError):
|
||||
flash("Podano nieprawidłową wartość dla celu zbiórki", "danger")
|
||||
# render z dotychczasowo wpisanymi danymi (w trybie dodawania tworzymy tymczasowy obiekt)
|
||||
temp_zb = zb or Zbiorka(
|
||||
nazwa=nazwa,
|
||||
opis=opis,
|
||||
numer_konta=numer_konta,
|
||||
numer_telefonu_blik=numer_telefonu_blik,
|
||||
cel=cel,
|
||||
ukryj_kwote=ukryj_kwote,
|
||||
cel=None,
|
||||
ukryj_kwote=("ukryj_kwote" in request.form),
|
||||
)
|
||||
db.session.add(nowa_zbiorka)
|
||||
db.session.commit()
|
||||
flash("Zbiórka została dodana", "success")
|
||||
return redirect(url_for("admin_dashboard"))
|
||||
|
||||
return render_template("admin/dodaj_zbiorke.html", global_settings=global_settings)
|
||||
|
||||
|
||||
@app.route("/admin/zbiorka/edytuj/<int:zbiorka_id>", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def edytuj_zbiorka(zbiorka_id):
|
||||
if not current_user.is_admin:
|
||||
flash("Brak uprawnień", "danger")
|
||||
return redirect(url_for("index"))
|
||||
zb = Zbiorka.query.get_or_404(zbiorka_id)
|
||||
global_settings = GlobalSettings.query.first() # Pobieramy globalne ustawienia
|
||||
if request.method == "POST":
|
||||
zb.nazwa = request.form["nazwa"]
|
||||
zb.opis = request.form["opis"]
|
||||
zb.numer_konta = request.form["numer_konta"]
|
||||
zb.numer_telefonu_blik = request.form["numer_telefonu_blik"]
|
||||
try:
|
||||
zb.cel = float(request.form["cel"])
|
||||
except ValueError:
|
||||
flash("Podano nieprawidłową wartość dla celu zbiórki", "danger")
|
||||
return render_template(
|
||||
"admin/edytuj_zbiorke.html", zbiorka=zb, global_settings=global_settings
|
||||
"admin/formularz_zbiorek.html",
|
||||
zbiorka=temp_zb if is_edit else None if zb is None else temp_zb,
|
||||
global_settings=global_settings,
|
||||
)
|
||||
zb.ukryj_kwote = "ukryj_kwote" in request.form
|
||||
|
||||
ukryj_kwote = "ukryj_kwote" in request.form
|
||||
|
||||
if is_edit:
|
||||
# Aktualizacja istniejącej
|
||||
zb.nazwa = nazwa
|
||||
zb.opis = opis
|
||||
zb.numer_konta = numer_konta
|
||||
zb.numer_telefonu_blik = numer_telefonu_blik
|
||||
zb.cel = float(cel) # jeśli masz Decimal w modelu, przypisz bez konwersji
|
||||
zb.ukryj_kwote = ukryj_kwote
|
||||
db.session.commit()
|
||||
flash("Zbiórka została zaktualizowana", "success")
|
||||
else:
|
||||
# Utworzenie nowej
|
||||
nowa = Zbiorka(
|
||||
nazwa=nazwa,
|
||||
opis=opis,
|
||||
numer_konta=numer_konta,
|
||||
numer_telefonu_blik=numer_telefonu_blik,
|
||||
cel=float(cel),
|
||||
ukryj_kwote=ukryj_kwote,
|
||||
)
|
||||
db.session.add(nowa)
|
||||
db.session.commit()
|
||||
flash("Zbiórka została dodana", "success")
|
||||
|
||||
return redirect(url_for("admin_dashboard"))
|
||||
|
||||
# GET
|
||||
return render_template(
|
||||
"admin/edytuj_zbiorke.html", zbiorka=zb, global_settings=global_settings
|
||||
"admin/formularz_zbiorek.html", zbiorka=zb, global_settings=global_settings
|
||||
)
|
||||
|
||||
|
||||
@@ -421,24 +438,33 @@ def create_admin_account():
|
||||
db.session.commit()
|
||||
|
||||
|
||||
from flask import request
|
||||
|
||||
|
||||
@app.after_request
|
||||
def apply_headers(response):
|
||||
# Nagłówki niestandardowe
|
||||
custom_headers = app.config.get("ADD_HEADERS", {})
|
||||
if isinstance(custom_headers, dict):
|
||||
for header, value in custom_headers.items():
|
||||
response.headers[header] = str(value)
|
||||
|
||||
# --- STATIC: jak wcześniej ---
|
||||
if request.path.startswith("/static/"):
|
||||
response.headers.pop("Content-Disposition", None)
|
||||
response.headers["Vary"] = "Accept-Encoding"
|
||||
response.headers["Cache-Control"] = app.config.get("CACHE_CONTROL_HEADER_STATIC")
|
||||
response.headers["Cache-Control"] = app.config.get(
|
||||
"CACHE_CONTROL_HEADER_STATIC"
|
||||
)
|
||||
if app.config.get("USE_ETAGS", True) and "ETag" not in response.headers:
|
||||
response.add_etag()
|
||||
response.make_conditional(request)
|
||||
return response
|
||||
|
||||
# Wykluczenia
|
||||
path_norm = request.path.lstrip("/")
|
||||
is_admin = path_norm.startswith("admin/") or path_norm == "admin"
|
||||
|
||||
if is_admin:
|
||||
if (response.mimetype or "").startswith("text/html"):
|
||||
response.headers["Cache-Control"] = "no-store, no-cache"
|
||||
response.headers.pop("ETag", None)
|
||||
|
||||
return response
|
||||
|
||||
if response.status_code in (301, 302, 303, 307, 308):
|
||||
response.headers.pop("Vary", None)
|
||||
return response
|
||||
@@ -452,16 +478,16 @@ def apply_headers(response):
|
||||
response.headers["Content-Type"] = "text/html; charset=utf-8"
|
||||
response.headers["Retry-After"] = "120"
|
||||
response.headers.pop("Vary", None)
|
||||
elif request.path.startswith("/admin"):
|
||||
response.headers.pop("Vary", None)
|
||||
response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
|
||||
else:
|
||||
response.headers["Vary"] = "Cookie, Accept-Encoding"
|
||||
default_cache = app.config.get("CACHE_CONTROL_HEADER") or "private, max-age=0"
|
||||
response.headers["Cache-Control"] = default_cache
|
||||
|
||||
# Blokowanie botów (ale NIE dla /static/)
|
||||
if app.config.get("BLOCK_BOTS", False) and not request.path.startswith("/static/"):
|
||||
if (
|
||||
app.config.get("BLOCK_BOTS", False)
|
||||
and not is_admin
|
||||
and not request.path.startswith("/static/")
|
||||
):
|
||||
cc_override = app.config.get("CACHE_CONTROL_HEADER")
|
||||
if cc_override:
|
||||
response.headers["Cache-Control"] = cc_override
|
||||
@@ -490,7 +516,7 @@ def admin_ustawienia():
|
||||
navbar_brand_mode = request.form.get("navbar_brand_mode", "text")
|
||||
footer_brand_mode = request.form.get("footer_brand_mode", "text")
|
||||
footer_text = request.form.get("footer_text") or None
|
||||
show_logo_in_navbar = (navbar_brand_mode == "logo")
|
||||
show_logo_in_navbar = navbar_brand_mode == "logo"
|
||||
|
||||
if settings is None:
|
||||
settings = GlobalSettings(
|
||||
@@ -525,16 +551,33 @@ def admin_ustawienia():
|
||||
)
|
||||
|
||||
|
||||
@app.route("/admin/zbiorka/oznacz/<int:zbiorka_id>", methods=["POST"])
|
||||
@app.route(
|
||||
"/admin/zbiorka/oznacz/niezrealizowana/<int:zbiorka_id>",
|
||||
methods=["POST"],
|
||||
endpoint="oznacz_niezrealizowana",
|
||||
)
|
||||
@app.route(
|
||||
"/admin/zbiorka/oznacz/zrealizowana/<int:zbiorka_id>",
|
||||
methods=["POST"],
|
||||
endpoint="oznacz_zrealizowana",
|
||||
)
|
||||
@login_required
|
||||
def oznacz_zbiorka(zbiorka_id):
|
||||
if not current_user.is_admin:
|
||||
flash("Brak uprawnień do wykonania tej operacji", "danger")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
zb = Zbiorka.query.get_or_404(zbiorka_id)
|
||||
|
||||
if "niezrealizowana" in request.path:
|
||||
zb.zrealizowana = False
|
||||
msg = "Zbiórka została oznaczona jako niezrealizowana"
|
||||
else:
|
||||
zb.zrealizowana = True
|
||||
msg = "Zbiórka została oznaczona jako zrealizowana"
|
||||
|
||||
db.session.commit()
|
||||
flash("Zbiórka została oznaczona jako zrealizowana", "success")
|
||||
flash(msg, "success")
|
||||
return redirect(url_for("admin_dashboard"))
|
||||
|
||||
|
||||
|
@@ -293,3 +293,14 @@ select.form-select:focus {
|
||||
color: var(--text);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 50%, transparent);
|
||||
}
|
||||
|
||||
/* pole edycji ciemne */
|
||||
.CodeMirror {
|
||||
background-color: #1e1e1e !important;
|
||||
color: #e0e0e0 !important;
|
||||
}
|
||||
|
||||
/* kursor */
|
||||
.CodeMirror-cursor {
|
||||
border-left: 1px solid #e0e0e0 !important;
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
(function () {
|
||||
const opis = document.getElementById('opis');
|
||||
const opisCount = document.getElementById('opisCount');
|
||||
if (opis && opisCount) {
|
||||
const updateCount = () => opisCount.textContent = opis.value.length.toString();
|
||||
opis.addEventListener('input', updateCount);
|
||||
updateCount();
|
||||
}
|
||||
|
||||
const iban = document.getElementById('numer_konta');
|
||||
if (iban) {
|
||||
iban.addEventListener('input', () => {
|
||||
const digits = iban.value.replace(/\D/g, '').slice(0, 26);
|
||||
const chunked = digits.replace(/(.{4})/g, '$1 ').trim();
|
||||
iban.value = chunked;
|
||||
});
|
||||
}
|
||||
|
||||
const tel = document.getElementById('numer_telefonu_blik');
|
||||
if (tel) {
|
||||
tel.addEventListener('input', () => {
|
||||
const digits = tel.value.replace(/\D/g, '').slice(0, 9);
|
||||
const parts = [];
|
||||
if (digits.length > 0) parts.push(digits.substring(0, 3));
|
||||
if (digits.length > 3) parts.push(digits.substring(3, 6));
|
||||
if (digits.length > 6) parts.push(digits.substring(6, 9));
|
||||
tel.value = parts.join(' ');
|
||||
});
|
||||
}
|
||||
|
||||
const cel = document.getElementById('cel');
|
||||
if (cel) {
|
||||
cel.addEventListener('change', () => {
|
||||
if (cel.value && Number(cel.value) < 0.01) cel.value = '0.01';
|
||||
});
|
||||
}
|
||||
})();
|
@@ -3,7 +3,7 @@
|
||||
const opis = document.getElementById('opis');
|
||||
const opisCount = document.getElementById('opisCount');
|
||||
if (opis && opisCount) {
|
||||
const updateCount = () => opisCount.textContent = opis.value.length.toString();
|
||||
const updateCount = () => (opisCount.textContent = String(opis.value.length));
|
||||
opis.addEventListener('input', updateCount);
|
||||
updateCount();
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
const iban = document.getElementById('numer_konta');
|
||||
if (iban) {
|
||||
iban.addEventListener('input', () => {
|
||||
const digits = iban.value.replace(/\D/g, '').slice(0, 26); // 26 cyfr po "PL"
|
||||
const digits = iban.value.replace(/\D/g, '').slice(0, 26); // 26 cyfr po PL
|
||||
const chunked = digits.replace(/(.{4})/g, '$1 ').trim();
|
||||
iban.value = chunked;
|
||||
});
|
||||
@@ -31,22 +31,25 @@
|
||||
});
|
||||
}
|
||||
|
||||
// „Ustaw globalne” z data-atrybutów (bez wstrzykiwania wartości w JS)
|
||||
// „Ustaw globalne” – jest tylko w trybie edycji; odpalamy warunkowo
|
||||
const setGlobalBtn = document.getElementById('ustaw-globalne');
|
||||
if (setGlobalBtn && iban && tel) {
|
||||
setGlobalBtn.addEventListener('click', () => {
|
||||
const gIban = setGlobalBtn.dataset.iban || '';
|
||||
const gBlik = setGlobalBtn.dataset.blik || '';
|
||||
|
||||
if (gIban) {
|
||||
iban.value = gIban.replace(/\D/g, '').replace(/(.{4})/g, '$1 ').trim();
|
||||
iban.dispatchEvent(new Event('input'));
|
||||
}
|
||||
if (gBlik) {
|
||||
const d = gBlik.replace(/\D/g, '').slice(0, 9);
|
||||
const p = [d.slice(0, 3), d.slice(3, 6), d.slice(6, 9)].filter(Boolean).join(' ');
|
||||
const p = [d.slice(0, 3), d.slice(3, 6), d.slice(6, 9)]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
tel.value = p;
|
||||
}
|
||||
iban.dispatchEvent(new Event('input'));
|
||||
tel.dispatchEvent(new Event('input'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<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">
|
||||
<a href="{{ url_for('formularz_zbiorek') }}" class="btn btn-primary">
|
||||
➕ Dodaj zbiórkę
|
||||
</a>
|
||||
<a href="{{ url_for('admin_ustawienia') }}" class="btn btn-outline-light border">
|
||||
@@ -78,7 +78,7 @@
|
||||
<td class="text-end">
|
||||
<!-- Grupa akcji: główne + rozwijane -->
|
||||
<div class="btn-group">
|
||||
<a href="{{ url_for('edytuj_zbiorka', zbiorka_id=z.id) }}"
|
||||
<a href="{{ url_for('formularz_zbiorek', 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">
|
||||
@@ -98,10 +98,10 @@
|
||||
<hr class="dropdown-divider">
|
||||
</li>
|
||||
<li>
|
||||
<form action="{{ url_for('oznacz_zbiorka', zbiorka_id=z.id) }}"
|
||||
<form action="{{ url_for('oznacz_zrealizowana', zbiorka_id=z.id) }}"
|
||||
method="post" class="m-0">
|
||||
<button type="submit" class="dropdown-item">Oznacz jako
|
||||
zrealizowana</button>
|
||||
zrealizowaną</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
@@ -136,7 +136,7 @@
|
||||
<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>
|
||||
<a href="{{ url_for('formularz_zbiorek') }}" class="btn btn-primary">Utwórz nową zbiórkę</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -186,7 +186,7 @@
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<div class="btn-group">
|
||||
<a href="{{ url_for('edytuj_zbiorka', zbiorka_id=z.id) }}"
|
||||
<a href="{{ url_for('formularz_zbiorek', 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">
|
||||
@@ -205,6 +205,13 @@
|
||||
<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">Oznacz jako
|
||||
niezrealizowaną</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
<form action="{{ url_for('zmien_widzialnosc', zbiorka_id=z.id) }}"
|
||||
method="post" class="m-0">
|
||||
@@ -236,7 +243,7 @@
|
||||
<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ą
|
||||
<a href="{{ url_for('formularz_zbiorek') }}" class="btn btn-outline-light border">Utwórz nową
|
||||
zbiórkę</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,125 +0,0 @@
|
||||
{% 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 %}
|
@@ -1,154 +0,0 @@
|
||||
{% 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 %}
|
172
templates/admin/formularz_zbiorek.html
Normal file
172
templates/admin/formularz_zbiorek.html
Normal file
@@ -0,0 +1,172 @@
|
||||
{# templates/zbiorka_form.html #}
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% set is_edit = zbiorka 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 %}
|
||||
<a href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}" class="btn btn-sm btn-outline-light border">←
|
||||
Szczegóły zbiórki</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-sm btn-outline-light border">← 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 zbiorka.ukryj_kwote %}
|
||||
<span class="badge bg-secondary">Kwoty ukryte</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 is_edit else '' }}" 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 is_edit else '' }}</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: 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"
|
||||
inputmode="numeric" autocomplete="off"
|
||||
placeholder="12 3456 7890 1234 5678 9012 3456" required aria-describedby="ibanHelp"
|
||||
value="{% if is_edit and zbiorka.numer_konta %}{{ zbiorka.numer_konta }}{% elif global_settings %}{{ global_settings.numer_konta }}{% else %}{% endif %}">
|
||||
</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" inputmode="tel" pattern="[0-9 ]{9,13}"
|
||||
placeholder="123 456 789" required aria-describedby="blikHelp"
|
||||
value="{% if is_edit and zbiorka.numer_telefonu_blik %}{{ zbiorka.numer_telefonu_blik }}{% elif global_settings %}{{ global_settings.numer_telefonu_blik }}{% else %}{% 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 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 %}>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 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"
|
||||
value="{{ zbiorka.cel if is_edit else '' }}">
|
||||
</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-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 is_edit and zbiorka.ukryj_kwote %}checked{% endif %}>
|
||||
<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">
|
||||
{{ ' Zaktualizuj zbiórkę' if is_edit else '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/formularz_zbiorek.js') }}"></script>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user