From 62e40d5aeea3b380c1c2caa2678f7f8cd2b3c39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Thu, 28 Aug 2025 12:42:57 +0200 Subject: [PATCH] zmiany w logice i endpoitach --- app.py | 175 +++++++++++------- static/css/custom.css | 11 ++ static/js/dodaj_zbiorke.js | 37 ---- ...edytuj_zbiorke.js => formularz_zbiorek.js} | 17 +- templates/admin/dashboard.html | 21 ++- templates/admin/dodaj_zbiorke.html | 125 ------------- templates/admin/edytuj_zbiorke.html | 154 --------------- templates/admin/formularz_zbiorek.html | 172 +++++++++++++++++ 8 files changed, 316 insertions(+), 396 deletions(-) delete mode 100644 static/js/dodaj_zbiorke.js rename static/js/{edytuj_zbiorke.js => formularz_zbiorek.js} (84%) delete mode 100644 templates/admin/dodaj_zbiorke.html delete mode 100644 templates/admin/edytuj_zbiorke.html create mode 100644 templates/admin/formularz_zbiorek.html diff --git a/app.py b/app.py index 1752f33..55a1a1b 100644 --- a/app.py +++ b/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"]) -@login_required -def dodaj_zbiorke(): - if not current_user.is_admin: - flash("Brak uprawnień", "danger") - return redirect(url_for("index")) - - global_settings = GlobalSettings.query.first() # Pobieramy globalne ustawienia - - 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 - - nowa_zbiorka = Zbiorka( - nazwa=nazwa, - opis=opis, - numer_konta=numer_konta, - numer_telefonu_blik=numer_telefonu_blik, - cel=cel, - ukryj_kwote=ukryj_kwote, - ) - 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/", methods=["GET", "POST"]) @login_required -def edytuj_zbiorka(zbiorka_id): +def formularz_zbiorek(zbiorka_id=None): 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 + + # 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": - 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"] + # Pola wspólne + nazwa = request.form.get("nazwa", "").strip() + opis = request.form.get("opis", "").strip() + + # 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: - zb.cel = float(request.form["cel"]) - except ValueError: + 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") - return render_template( - "admin/edytuj_zbiorke.html", zbiorka=zb, global_settings=global_settings + # 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=None, + ukryj_kwote=("ukryj_kwote" in request.form), ) - zb.ukryj_kwote = "ukryj_kwote" in request.form - db.session.commit() - flash("Zbiórka została zaktualizowana", "success") + return render_template( + "admin/formularz_zbiorek.html", + zbiorka=temp_zb if is_edit else None if zb is None else temp_zb, + global_settings=global_settings, + ) + + 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/", methods=["POST"]) +@app.route( + "/admin/zbiorka/oznacz/niezrealizowana/", + methods=["POST"], + endpoint="oznacz_niezrealizowana", +) +@app.route( + "/admin/zbiorka/oznacz/zrealizowana/", + 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) - zb.zrealizowana = True + + 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")) diff --git a/static/css/custom.css b/static/css/custom.css index 2a4dcfc..9d0e379 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -292,4 +292,15 @@ select.form-select:focus { border-color: var(--accent-600); 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; } \ No newline at end of file diff --git a/static/js/dodaj_zbiorke.js b/static/js/dodaj_zbiorke.js deleted file mode 100644 index b36c5c0..0000000 --- a/static/js/dodaj_zbiorke.js +++ /dev/null @@ -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'; - }); - } -})(); \ No newline at end of file diff --git a/static/js/edytuj_zbiorke.js b/static/js/formularz_zbiorek.js similarity index 84% rename from static/js/edytuj_zbiorke.js rename to static/js/formularz_zbiorek.js index eb35f9e..ad3913a 100644 --- a/static/js/edytuj_zbiorke.js +++ b/static/js/formularz_zbiorek.js @@ -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; + tel.dispatchEvent(new Event('input')); } - iban.dispatchEvent(new Event('input')); - tel.dispatchEvent(new Event('input')); }); } @@ -57,4 +60,4 @@ if (cel.value && Number(cel.value) < 0.01) cel.value = '0.01'; }); } -})(); \ No newline at end of file +})(); diff --git a/templates/admin/dashboard.html b/templates/admin/dashboard.html index a244f35..f0015e0 100644 --- a/templates/admin/dashboard.html +++ b/templates/admin/dashboard.html @@ -8,7 +8,7 @@

Panel Admina

- + ➕ Dodaj zbiórkę @@ -78,7 +78,7 @@
- Edytuj + zrealizowaną
  • @@ -136,7 +136,7 @@
    Brak aktywnych zbiórek

    Wygląda na to, że teraz nic nie zbieramy.

    - Utwórz nową zbiórkę + Utwórz nową zbiórkę
  • {% endif %} @@ -186,7 +186,7 @@
    - Edytuj + +
  • @@ -236,7 +243,7 @@
    Brak zbiórek zrealizowanych

    Gdy jakaś zbiórka osiągnie 100%, pojawi się tutaj.

    - Utwórz nową + Utwórz nową zbiórkę
  • diff --git a/templates/admin/dodaj_zbiorke.html b/templates/admin/dodaj_zbiorke.html deleted file mode 100644 index ebe45b3..0000000 --- a/templates/admin/dodaj_zbiorke.html +++ /dev/null @@ -1,125 +0,0 @@ -{% extends 'base.html' %} -{% block title %}Dodaj zbiórkę{% endblock %} - -{% block extra_head %} -{{ super() }} - -{% endblock %} - -{% block content %} -
    - - - - -
    -
    -

    Dodaj nową zbiórkę

    - Uzupełnij podstawowe dane i dane płatności -
    - -
    - - {# {{ form.csrf_token }} jeśli używasz Flask-WTF #} - - -
    -
    Podstawowe
    -
    -
    - - -
    Krótki, zrozumiały tytuł. Max 120 znaków.
    -
    - -
    - - -
    - - Możesz używać **Markdown** (nagłówki, listy, linki). W edytorze włącz podgląd 👁️. - - 0 znaków -
    -
    -
    -
    - -
    - - -
    -
    Dane płatności
    -
    -
    - -
    - PL - -
    -
    Wpisz ciąg cyfr; spacje dodadzą się automatycznie dla czytelności. -
    -
    - -
    - -
    - +48 - -
    -
    Dziewięć cyfr telefonu powiązanego z BLIK. Spacje opcjonalne.
    -
    -
    -
    - -
    - - -
    -
    Cel i widoczność
    -
    -
    - -
    - PLN - -
    -
    Minimalnie 0,01 PLN. Możesz to później edytować.
    -
    - -
    -
    - - -
    -
    -
    -
    - - -
    - - Anuluj -
    - -
    -
    -
    -{% endblock %} - -{% block extra_scripts %} -{{ super() }} - - - -{% endblock %} \ No newline at end of file diff --git a/templates/admin/edytuj_zbiorke.html b/templates/admin/edytuj_zbiorke.html deleted file mode 100644 index 7a5e5bb..0000000 --- a/templates/admin/edytuj_zbiorke.html +++ /dev/null @@ -1,154 +0,0 @@ -{% extends 'base.html' %} -{% block title %}Edytuj zbiórkę{% endblock %} - -{% block extra_head %} -{{ super() }} - -{% endblock %} - -{% block content %} -
    - - - - - -
    -
    -

    Edytuj zbiórkę

    -
    - {% if zbiorka.cel %} - Cel: {{ zbiorka.cel|round(2) }} - PLN - {% endif %} - {% if zbiorka.ukryj_kwote %} - Kwoty ukryte - {% else %} - Kwoty widoczne - {% endif %} -
    -
    - -
    - -
    - {# {{ form.csrf_token }} jeśli używasz Flask-WTF #} - - -
    -
    Podstawowe
    -
    -
    - - -
    Max 120 znaków. Użyj konkretów.
    -
    - -
    - - -
    - Wspieramy **Markdown** — użyj nagłówków, list, linków. - Włącz podgląd w edytorze 👁️. - 0 znaków -
    -
    -
    -
    - -
    - - -
    -
    Dane płatności
    -
    -
    - -
    - PL - -
    -
    Wpisz same cyfry — spacje dodadzą się automatycznie co 4 znaki.
    -
    - -
    - -
    - +48 - -
    -
    9 cyfr. Spacje dodadzą się automatycznie (format 3-3-3).
    -
    - -
    - -
    -
    -
    - -
    - - -
    -
    Cel i widoczność
    -
    -
    - -
    - PLN - -
    -
    Minimalnie 0,01 PLN. W razie potrzeby zmienisz to później.
    -
    - -
    -
    - - -
    -
    -
    -
    - - -
    - - Anuluj - - - - - -
    - - -
    -
    -
    -{% endblock %} - -{% block extra_scripts %} -{{ super() }} - - - -{% endblock %} \ No newline at end of file diff --git a/templates/admin/formularz_zbiorek.html b/templates/admin/formularz_zbiorek.html new file mode 100644 index 0000000..87afbf0 --- /dev/null +++ b/templates/admin/formularz_zbiorek.html @@ -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() }} + +{% endblock %} + +{% block content %} +
    + + +
    + {% if is_edit %} + ← + Szczegóły zbiórki + {% else %} + ← Panel Admina + {% endif %} +
    + +
    +
    +

    + {{ 'Edytuj zbiórkę' if is_edit else 'Dodaj nową zbiórkę' }} +

    + + {% if is_edit %} +
    + {% if zbiorka.cel %} + + Cel: {{ zbiorka.cel|round(2) }} PLN + + {% endif %} + {% if zbiorka.ukryj_kwote %} + Kwoty ukryte + {% else %} + Kwoty widoczne + {% endif %} +
    + {% else %} + Uzupełnij podstawowe dane i dane płatności + {% endif %} +
    + +
    +
    + {# {{ form.csrf_token }} jeśli używasz Flask-WTF #} + + +
    +
    Podstawowe
    +
    +
    + + +
    Krótki, zrozumiały tytuł. Max 120 znaków.
    +
    + +
    + + +
    + + Możesz używać **Markdown** (nagłówki, listy, linki). W edytorze włącz podgląd 👁️. + + 0 znaków +
    +
    +
    +
    + +
    + + +
    +
    Dane płatności
    +
    +
    + +
    + PL + +
    +
    + Wpisz ciąg cyfr; spacje dodadzą się automatycznie dla czytelności. +
    +
    + +
    + +
    + +48 + +
    +
    Dziewięć cyfr telefonu powiązanego z BLIK. Spacje + opcjonalne.
    +
    + + {% if is_edit %} +
    + +
    + {% endif %} +
    +
    + +
    + + +
    +
    Cel i widoczność
    +
    +
    + +
    + PLN + +
    +
    Minimalnie 0,01 PLN. Możesz to później edytować.
    +
    + +
    +
    + + +
    +
    +
    +
    + + +
    + + + Anuluj + +
    +
    +
    +
    +
    +{% endblock %} + +{% block extra_scripts %} +{{ super() }} + + + +{% endblock %} \ No newline at end of file