Files
RosterChief/templates/allauth/elements/fields.html
Bernard Siebens 2b7b2b64db Fix empty allauth forms, and lay out the 2FA page
The 2FA code input was not invisible -- it was absent, along with the fields of
every other allauth form except login.

Cause: the `fields` element passed `attrs.exclude` straight into a filter. On a
page that never sets it, resolving a filter *argument* raises
VariableDoesNotExist; Django rescues that for the main variable of an expression
but not for a filter argument, and {% if %} then swallows it and reads the
condition as false. So every field was skipped. Login was the one page that
passes `exclude`, which is exactly why it kept working and hid the damage.
`exclude` is now pinned to a real variable first, with tests that render the
login, signup and password-reset forms and assert their inputs exist.

Two dangling buttons fixed while in here: `elements/form.html` dropped the `id`
attribute, so the out-of-band forms allauth generates (webauthn_form,
logout-from-stage) had no id for a button's `form` attribute to point at. "Use a
security key" submitted nothing.

Layout: the code is a daisyUI otp field, Cancel sits beside Sign In as a plain
button, both gain icons, and "Use a security key" becomes an accent button.

The otp boxes yield once more than six characters are typed. allauth accepts a
TOTP code (6) *or* a recovery code (8) in this one field, so hard-boxing it to
six would have locked out every recovery code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 22:59:00 +02:00

67 lines
3.4 KiB
HTML

{% load lucide ui %}
{% comment %}
allauth passes `unlabeled=True` on the entrance forms (login, signup, reset) and
already sets a placeholder on each of their fields ("Email address", "Password"),
so dropping the visible label loses nothing on screen. The label is still emitted
sr-only inside the wrapper: a placeholder is not a label, and it disappears the
moment you start typing.
Fields with an icon use daisyUI's icon-in-field layout, where the `input` class
goes on the *wrapping label* and the input itself carries only `grow`. Putting
`input` on both draws a box inside a box.
{% endcomment %}
{% for field in attrs.form.hidden_fields %}{{ field }}{% endfor %}
{% for error in attrs.form.non_field_errors %}
<div class="alert alert-error my-2">
<span>{{ error }}</span>
</div>
{% endfor %}
{% comment %}
`exclude` lets a page lay a field out itself — the login page puts "remember" on the
button row. It is pinned to a real variable first, and that is not cosmetic: passing
`attrs.exclude` straight into a filter on a page that never set it raises
VariableDoesNotExist, which Django catches for the *main* variable of an expression
but not for a filter *argument*. {% if %} then swallows it and reads the condition as
false — silently dropping every field on every form that doesn't pass `exclude`.
{% endcomment %}
{% with exclude=attrs.exclude|default:"" %}
{% for field in attrs.form.visible_fields %}
{% if not field|excluded:exclude %}
<div class="form-control my-3 w-full">
{% if field.field.widget.input_type == "checkbox" %}
<label class="label cursor-pointer justify-start gap-3" for="{{ field.id_for_label }}">
{{ field|daisy }}
<span class="label-text">{{ field.label }}</span>
</label>
{% else %}
{% if not attrs.unlabeled %}
<label class="label" for="{{ field.id_for_label }}">
<span class="label-text">{{ field.label }}</span>
</label>
{% endif %}
{% with icon=field|field_icon %}
{% if icon %}
<label class="input flex w-full items-center gap-2 {% if field.errors %}input-error{% endif %}" for="{{ field.id_for_label }}">
<span class="opacity-50">{% lucide icon size=16 %}</span>
{% if attrs.unlabeled %}<span class="sr-only">{{ field.label }}</span>{% endif %}
{{ field|daisy:"grow" }}
</label>
{% else %}
{{ field|daisy }}
{% endif %}
{% endwith %}
{% endif %}
{% comment %}
The password field's help_text is allauth's "Forgot your password?" link, so it
gets room to breathe. The id is not decorative: Django points the input's
aria-describedby at `<auto_id>_helptext`, and without it that reference dangles
and the link is never announced.
{% endcomment %}
{% if field.help_text %}<span id="{{ field.auto_id }}_helptext" class="label-text-alt mt-3 block text-base-content/70">{{ field.help_text }}</span>{% endif %}
{% for error in field.errors %}<span class="label-text-alt mt-1 text-error">{{ error }}</span>{% endfor %}
</div>
{% endif %}
{% endfor %}
{% endwith %}