Skip to content
Snippets Groups Projects
Commit 6af1376e authored by Thomas Schneider's avatar Thomas Schneider
Browse files

views/schild: Form design rework

Rather large refactoring:
- A bunch of CSS so that it doesn’t look like ass;
- Roll our own (WT)Forms, as wtforms-sqlalchemy is too limited.  Drop that one
  completely;
- Deduplicate code from /schild/<ident> and /create;
- As SchildForm.{image, template} are now RadioFields, iterate directly via
  their .choices property instead of passing templates/images separately to the
  template;
- … and some minor shenanigans as usual.
parent 6da157fb
No related branches found
No related tags found
No related merge requests found
......@@ -128,7 +128,7 @@ main {
}
.preview {
width: 95vw;
width: min(95vw, 100rem);
/* A4 aspect ratio: √2:1 */
aspect-ratio: 1.4142135623730951;
......@@ -143,3 +143,68 @@ main {
.preview-label {
place-self: center;
}
form {
display: inline-block;
}
@label-padding: 90px;
label.for-text {
display: inline-block;
min-width: @label-padding;
text-align: right;
}
input[type="text"], textarea {
width: 300px;
}
textarea {
height: 5em;
vertical-align: top;
}
fieldset {
display: flex;
flex-wrap: wrap;
}
fieldset > div {
display: flex;
gap: 0.5rem;
flex-direction: column;
}
.imageselect > div {
width: 10rem;
display: flex;
gap: 0.5rem;
}
fieldset label {
flex-grow: 1;
}
.imageselect img {
width: 100%;
background-color: #fff;
}
fieldset input {
display: none;
}
fieldset img,
fieldset iframe,
.preview-small {
border: 3px solid lightgray;
border-radius: 10px;
}
input[type="radio"]:checked {
& + label > img,
& + iframe {
border:3px solid red;
}
}
......@@ -5,7 +5,7 @@
groups = ["default", "dev"]
strategy = ["inherit_metadata"]
lock_version = "4.5.0"
content_hash = "sha256:4254d9a7fba04b352e6ddf4d85e4ce62abbf60c4d39127493dd18cb4b4dbc110"
content_hash = "sha256:618addcae7a6ab39fb74f18ca29c1a64f762aea504459d0db220bc7e886efe59"
[[metadata.targets]]
requires_python = ">=3.12"
......@@ -1304,21 +1304,6 @@ files = [
{file = "wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9"},
]
[[package]]
name = "wtforms-sqlalchemy"
version = "0.4.1"
requires_python = ">=3.8"
summary = "SQLAlchemy tools for WTForms"
groups = ["default"]
dependencies = [
"SQLAlchemy>=1.4",
"WTForms>=3.1",
]
files = [
{file = "WTForms-SQLAlchemy-0.4.1.tar.gz", hash = "sha256:370f52b738527cf6d8ab78d3488afb9342666144da2637e0cf8b5ae522e8a4db"},
{file = "WTForms_SQLAlchemy-0.4.1-py3-none-any.whl", hash = "sha256:df3965015b60de172f4b35e691a47d9913d56bb0abb62c620edd04c1376979e3"},
]
[[package]]
name = "zopfli"
version = "0.2.3"
......
......@@ -15,7 +15,6 @@ dependencies = [
"flask",
"flask-sqlalchemy",
"flask-wtf",
"wtforms-sqlalchemy",
"uuid7",
"psycopg",
"flask-weasyprint",
......
......@@ -2,6 +2,8 @@ from . import db
from .models import Schild
from .helpers import Blueprint, get_template_attribute
from collections import namedtuple
from pathlib import Path
from flask import url_for
......@@ -19,7 +21,6 @@ bp = Blueprint(
@bp.route("/schild/<ident>.html")
def schild_html(ident):
schild = db.get_or_404(Schild, ident)
# raise Exception
return bp.render_template(schild.template, schild=schild)
......@@ -38,13 +39,16 @@ def sample_pdf(template):
return render_pdf(url_for(".sample_html", template=template))
Template = namedtuple("Template", "name description")
def list_templates():
schild = Schild()
loader = bp.jinja_loader
for t in loader.list_templates():
if t.startswith("_"):
continue
yield dict(
yield Template(
name=t,
description=get_template_attribute(
bp.real_template_name(t),
......@@ -53,3 +57,9 @@ def list_templates():
default=None,
),
)
def list_images():
base = Path(bp.static_folder) / "img"
for f in base.iterdir():
yield f.name
......@@ -5,6 +5,10 @@ from uuid_extensions import uuid7
from sqlalchemy import String, Text, Uuid
from sqlalchemy.orm import Mapped, mapped_column
from flask_wtf import FlaskForm
from wtforms import fields, validators
from . import db
......@@ -14,3 +18,25 @@ class Schild(db.Model):
text: Mapped[str] = mapped_column(Text)
image: Mapped[str] = mapped_column(String(255), nullable=True)
template: Mapped[str] = mapped_column(String(255))
class SchildForm(FlaskForm):
title = fields.StringField(
"Titel",
validators=[
validators.InputRequired(),
validators.Length(max=Schild.title.type.length),
],
)
text = fields.TextAreaField(
"Text",
validators=[validators.InputRequired()],
)
image = fields.RadioField(
"Bild",
validators=[validators.Optional()],
)
template = fields.RadioField(
"Vorlage",
validators=[validators.DataRequired()],
)
......@@ -5,7 +5,7 @@
{{ field }}
{%- else -%}
<div class="box">
{{ field.label }}
{{ field.label(class="for-text") }}
{{ field(class="input-dispatch") }}
</div>
{%- endif -%}
......@@ -21,16 +21,57 @@
{% block main -%}
<form method="post" action="">
{%- for field in form -%}
{{ render_field(field) }}
{%- endfor -%}
<select name="template">
{%- for t in templates -%}
<option value="{{ t.name }}">
{{ form.csrf_token }}
{{ render_field(form.title) }}
{{ render_field(form.text) }}
<fieldset class="templateselect">
<legend>Vorlage</legend>
{%- for t in form.template.choices -%}
<div>
<input
type="radio"
name="template"
id="template:{{ t.name }}"
value="{{ t.name }}"
{% if t.name == (schild | default(None)).template -%}
checked
{%- endif -%}
/>
<iframe
class="preview preview-small"
src="{{ url_for('instance.sample_html', template=t.name) }}"
id="template-preview:{{ t.name }}"
>
</iframe>
<label for="template:{{ t.name }}" class="preview-label">
{{ t.description or t.name }}
</option>
</label>
</div>
{% endfor %}
</select>
</fieldset>
<fieldset class="imageselect">
<legend>Bild</legend>
{%- for img in form.image.choices -%}
<div>
<input
type="radio"
name="image"
id="img:{{ img }}"
value="{{ img }}"
{% if img == (schild | default(None)).image -%}
checked
{%- endif -%}
/>
<label for="img:{{ img }}">
<img src="{{ url_for('instance.static', filename='img/'+img) }}" title="{{ img }}" />
</label>
</div>
{%- endfor -%}
</fieldset>
<div class="box">
{%- if schild -%}
<input type="submit" value="Speichern" />
......@@ -44,6 +85,7 @@
{%- endif -%}
</div>
</form>
{%- if schild %}
<div>
<a href="{{ url_for('instance.schild_pdf', ident=schild.ident) }}">
......@@ -56,7 +98,8 @@
<iframe
class="preview"
id="preview"
src="{{ url_for('instance.schild_html', ident=schild.ident) }}" />
src="{{ url_for('instance.schild_html', ident=schild.ident) }}">
</iframe>
</div>
</div>
{% endif %}
......
from . import db
from .instance import list_templates
from .models import Schild
from .instance import list_templates, list_images
from .models import Schild, SchildForm
from flask import Blueprint, render_template, request, redirect, url_for
from flask_wtf import FlaskForm
from wtforms_sqlalchemy.orm import model_form
bp = Blueprint("views", __name__)
SchildForm = model_form(Schild, base_class=FlaskForm)
@bp.route("/")
def index():
schilder = db.session.execute(db.select(Schild)).scalars()
return render_template("index.html.j2", schilder=schilder)
@bp.route("/schild/<ident>", methods=["GET", "POST"])
def schild(ident):
schild = db.get_or_404(Schild, ident)
if request.method == "POST":
def _do_schild_form(schild):
form = SchildForm(request.form, obj=schild)
if form.validate():
form.image.choices = list(list_images())
form.template.choices = list(list_templates())
if request.method == "POST" and form.validate():
form.populate_obj(schild)
db.session.add(schild)
db.session.commit()
else:
form = SchildForm(obj=schild)
return render_template("schild.html.j2", schild=schild, form=form, templates=list_templates())
return form
@bp.route("/schild/<ident>", methods=["GET", "POST"])
def schild(ident):
schild = db.get_or_404(Schild, ident)
form = _do_schild_form(schild)
return render_template(
"schild.html.j2",
schild=schild,
form=form,
)
@bp.route("/create", methods=["GET", "POST"])
def create():
schild = Schild()
form = _do_schild_form(schild)
if request.method == "POST":
form = SchildForm(request.form, obj=schild)
if form.validate():
form.populate_obj(schild)
db.session.add(schild)
db.session.commit()
return redirect(url_for(".schild", ident=schild.ident))
else:
form = SchildForm(obj=schild)
return render_template("schild.html.j2", form=form)
return render_template(
"schild.html.j2",
form=form,
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment