zmiany w logice i endpoitach
This commit is contained in:
175
app.py
175
app.py
@@ -13,7 +13,7 @@ from datetime import datetime
|
|||||||
from markupsafe import Markup
|
from markupsafe import Markup
|
||||||
from sqlalchemy import event
|
from sqlalchemy import event
|
||||||
from sqlalchemy.engine import Engine
|
from sqlalchemy.engine import Engine
|
||||||
|
from decimal import Decimal, InvalidOperation
|
||||||
import markdown as md
|
import markdown as md
|
||||||
from flask import request, flash, abort
|
from flask import request, flash, abort
|
||||||
import os
|
import os
|
||||||
@@ -93,6 +93,7 @@ class GlobalSettings(db.Model):
|
|||||||
footer_brand_mode = db.Column(db.String(10), default="text")
|
footer_brand_mode = db.Column(db.String(10), default="text")
|
||||||
footer_text = db.Column(db.String(200), nullable=True)
|
footer_text = db.Column(db.String(200), nullable=True)
|
||||||
|
|
||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(user_id):
|
def load_user(user_id):
|
||||||
return User.query.get(int(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/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/<int:zbiorka_id>", methods=["GET", "POST"])
|
@app.route("/admin/zbiorka/edytuj/<int:zbiorka_id>", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def edytuj_zbiorka(zbiorka_id):
|
def formularz_zbiorek(zbiorka_id=None):
|
||||||
if not current_user.is_admin:
|
if not current_user.is_admin:
|
||||||
flash("Brak uprawnień", "danger")
|
flash("Brak uprawnień", "danger")
|
||||||
return redirect(url_for("index"))
|
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":
|
if request.method == "POST":
|
||||||
zb.nazwa = request.form["nazwa"]
|
# Pola wspólne
|
||||||
zb.opis = request.form["opis"]
|
nazwa = request.form.get("nazwa", "").strip()
|
||||||
zb.numer_konta = request.form["numer_konta"]
|
opis = request.form.get("opis", "").strip()
|
||||||
zb.numer_telefonu_blik = request.form["numer_telefonu_blik"]
|
|
||||||
|
# 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:
|
try:
|
||||||
zb.cel = float(request.form["cel"])
|
cel_str = request.form.get("cel", "").replace(",", ".").strip()
|
||||||
except ValueError:
|
cel = Decimal(cel_str)
|
||||||
|
if cel <= Decimal("0"):
|
||||||
|
raise InvalidOperation
|
||||||
|
except (InvalidOperation, ValueError):
|
||||||
flash("Podano nieprawidłową wartość dla celu zbiórki", "danger")
|
flash("Podano nieprawidłową wartość dla celu zbiórki", "danger")
|
||||||
return render_template(
|
# render z dotychczasowo wpisanymi danymi (w trybie dodawania tworzymy tymczasowy obiekt)
|
||||||
"admin/edytuj_zbiorke.html", zbiorka=zb, global_settings=global_settings
|
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
|
return render_template(
|
||||||
db.session.commit()
|
"admin/formularz_zbiorek.html",
|
||||||
flash("Zbiórka została zaktualizowana", "success")
|
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"))
|
return redirect(url_for("admin_dashboard"))
|
||||||
|
|
||||||
|
# GET
|
||||||
return render_template(
|
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()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
|
||||||
@app.after_request
|
@app.after_request
|
||||||
def apply_headers(response):
|
def apply_headers(response):
|
||||||
# Nagłówki niestandardowe
|
# --- STATIC: jak wcześniej ---
|
||||||
custom_headers = app.config.get("ADD_HEADERS", {})
|
|
||||||
if isinstance(custom_headers, dict):
|
|
||||||
for header, value in custom_headers.items():
|
|
||||||
response.headers[header] = str(value)
|
|
||||||
|
|
||||||
if request.path.startswith("/static/"):
|
if request.path.startswith("/static/"):
|
||||||
response.headers.pop("Content-Disposition", None)
|
response.headers.pop("Content-Disposition", None)
|
||||||
response.headers["Vary"] = "Accept-Encoding"
|
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:
|
if app.config.get("USE_ETAGS", True) and "ETag" not in response.headers:
|
||||||
response.add_etag()
|
response.add_etag()
|
||||||
response.make_conditional(request)
|
response.make_conditional(request)
|
||||||
return response
|
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):
|
if response.status_code in (301, 302, 303, 307, 308):
|
||||||
response.headers.pop("Vary", None)
|
response.headers.pop("Vary", None)
|
||||||
return response
|
return response
|
||||||
@@ -452,16 +478,16 @@ def apply_headers(response):
|
|||||||
response.headers["Content-Type"] = "text/html; charset=utf-8"
|
response.headers["Content-Type"] = "text/html; charset=utf-8"
|
||||||
response.headers["Retry-After"] = "120"
|
response.headers["Retry-After"] = "120"
|
||||||
response.headers.pop("Vary", None)
|
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:
|
else:
|
||||||
response.headers["Vary"] = "Cookie, Accept-Encoding"
|
response.headers["Vary"] = "Cookie, Accept-Encoding"
|
||||||
default_cache = app.config.get("CACHE_CONTROL_HEADER") or "private, max-age=0"
|
default_cache = app.config.get("CACHE_CONTROL_HEADER") or "private, max-age=0"
|
||||||
response.headers["Cache-Control"] = default_cache
|
response.headers["Cache-Control"] = default_cache
|
||||||
|
|
||||||
# Blokowanie botów (ale NIE dla /static/)
|
if (
|
||||||
if app.config.get("BLOCK_BOTS", False) and not request.path.startswith("/static/"):
|
app.config.get("BLOCK_BOTS", False)
|
||||||
|
and not is_admin
|
||||||
|
and not request.path.startswith("/static/")
|
||||||
|
):
|
||||||
cc_override = app.config.get("CACHE_CONTROL_HEADER")
|
cc_override = app.config.get("CACHE_CONTROL_HEADER")
|
||||||
if cc_override:
|
if cc_override:
|
||||||
response.headers["Cache-Control"] = 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")
|
navbar_brand_mode = request.form.get("navbar_brand_mode", "text")
|
||||||
footer_brand_mode = request.form.get("footer_brand_mode", "text")
|
footer_brand_mode = request.form.get("footer_brand_mode", "text")
|
||||||
footer_text = request.form.get("footer_text") or None
|
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:
|
if settings is None:
|
||||||
settings = GlobalSettings(
|
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
|
@login_required
|
||||||
def oznacz_zbiorka(zbiorka_id):
|
def oznacz_zbiorka(zbiorka_id):
|
||||||
if not current_user.is_admin:
|
if not current_user.is_admin:
|
||||||
flash("Brak uprawnień do wykonania tej operacji", "danger")
|
flash("Brak uprawnień do wykonania tej operacji", "danger")
|
||||||
return redirect(url_for("index"))
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
zb = Zbiorka.query.get_or_404(zbiorka_id)
|
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()
|
db.session.commit()
|
||||||
flash("Zbiórka została oznaczona jako zrealizowana", "success")
|
flash(msg, "success")
|
||||||
return redirect(url_for("admin_dashboard"))
|
return redirect(url_for("admin_dashboard"))
|
||||||
|
|
||||||
|
|
||||||
|
@@ -292,4 +292,15 @@ select.form-select:focus {
|
|||||||
border-color: var(--accent-600);
|
border-color: var(--accent-600);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 50%, transparent);
|
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 opis = document.getElementById('opis');
|
||||||
const opisCount = document.getElementById('opisCount');
|
const opisCount = document.getElementById('opisCount');
|
||||||
if (opis && opisCount) {
|
if (opis && opisCount) {
|
||||||
const updateCount = () => opisCount.textContent = opis.value.length.toString();
|
const updateCount = () => (opisCount.textContent = String(opis.value.length));
|
||||||
opis.addEventListener('input', updateCount);
|
opis.addEventListener('input', updateCount);
|
||||||
updateCount();
|
updateCount();
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
const iban = document.getElementById('numer_konta');
|
const iban = document.getElementById('numer_konta');
|
||||||
if (iban) {
|
if (iban) {
|
||||||
iban.addEventListener('input', () => {
|
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();
|
const chunked = digits.replace(/(.{4})/g, '$1 ').trim();
|
||||||
iban.value = chunked;
|
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');
|
const setGlobalBtn = document.getElementById('ustaw-globalne');
|
||||||
if (setGlobalBtn && iban && tel) {
|
if (setGlobalBtn && iban && tel) {
|
||||||
setGlobalBtn.addEventListener('click', () => {
|
setGlobalBtn.addEventListener('click', () => {
|
||||||
const gIban = setGlobalBtn.dataset.iban || '';
|
const gIban = setGlobalBtn.dataset.iban || '';
|
||||||
const gBlik = setGlobalBtn.dataset.blik || '';
|
const gBlik = setGlobalBtn.dataset.blik || '';
|
||||||
|
|
||||||
if (gIban) {
|
if (gIban) {
|
||||||
iban.value = gIban.replace(/\D/g, '').replace(/(.{4})/g, '$1 ').trim();
|
iban.value = gIban.replace(/\D/g, '').replace(/(.{4})/g, '$1 ').trim();
|
||||||
|
iban.dispatchEvent(new Event('input'));
|
||||||
}
|
}
|
||||||
if (gBlik) {
|
if (gBlik) {
|
||||||
const d = gBlik.replace(/\D/g, '').slice(0, 9);
|
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.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';
|
if (cel.value && Number(cel.value) < 0.01) cel.value = '0.01';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})();
|
})();
|
@@ -8,7 +8,7 @@
|
|||||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-4">
|
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-4">
|
||||||
<h2 class="mb-0">Panel Admina</h2>
|
<h2 class="mb-0">Panel Admina</h2>
|
||||||
<div class="d-flex flex-wrap gap-2">
|
<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ę
|
➕ Dodaj zbiórkę
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ url_for('admin_ustawienia') }}" class="btn btn-outline-light border">
|
<a href="{{ url_for('admin_ustawienia') }}" class="btn btn-outline-light border">
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<!-- Grupa akcji: główne + rozwijane -->
|
<!-- Grupa akcji: główne + rozwijane -->
|
||||||
<div class="btn-group">
|
<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>
|
class="btn btn-sm btn-outline-light">Edytuj</a>
|
||||||
<button class="btn btn-sm btn-outline-light dropdown-toggle dropdown-toggle-split"
|
<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">
|
data-bs-toggle="dropdown" aria-expanded="false" aria-label="Więcej opcji">
|
||||||
@@ -98,10 +98,10 @@
|
|||||||
<hr class="dropdown-divider">
|
<hr class="dropdown-divider">
|
||||||
</li>
|
</li>
|
||||||
<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">
|
method="post" class="m-0">
|
||||||
<button type="submit" class="dropdown-item">Oznacz jako
|
<button type="submit" class="dropdown-item">Oznacz jako
|
||||||
zrealizowana</button>
|
zrealizowaną</button>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
<div class="card-body text-center py-5">
|
<div class="card-body text-center py-5">
|
||||||
<h5 class="mb-2">Brak aktywnych zbiórek</h5>
|
<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>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -186,7 +186,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<div class="btn-group">
|
<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>
|
class="btn btn-sm btn-outline-light">Edytuj</a>
|
||||||
<button class="btn btn-sm btn-outline-light dropdown-toggle dropdown-toggle-split"
|
<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">
|
data-bs-toggle="dropdown" aria-expanded="false" aria-label="Więcej opcji">
|
||||||
@@ -205,6 +205,13 @@
|
|||||||
<li>
|
<li>
|
||||||
<hr class="dropdown-divider">
|
<hr class="dropdown-divider">
|
||||||
</li>
|
</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>
|
<li>
|
||||||
<form action="{{ url_for('zmien_widzialnosc', zbiorka_id=z.id) }}"
|
<form action="{{ url_for('zmien_widzialnosc', zbiorka_id=z.id) }}"
|
||||||
method="post" class="m-0">
|
method="post" class="m-0">
|
||||||
@@ -236,7 +243,7 @@
|
|||||||
<div class="card-body text-center py-5">
|
<div class="card-body text-center py-5">
|
||||||
<h5 class="mb-2">Brak zbiórek zrealizowanych</h5>
|
<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>
|
<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>
|
zbiórkę</a>
|
||||||
</div>
|
</div>
|
||||||
</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