further things around login
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"task_id": "AUTH-LOGIN-A11Y-REGRESSION-001",
|
||||
"plan_ref": "agent-system/runs/AUTH-LOGIN-A11Y-REGRESSION-001/plan.json",
|
||||
"status": "done",
|
||||
"changed_files": [
|
||||
{
|
||||
"path": "pages/auth/login(login).phtml",
|
||||
"summary": "In der choose_tenant Stage wurde eine semantische legend mit Klasse login-tenant-select-label ergaenzt, um die Fieldset-Gruppenbezeichnung und den erwarteten A11y-Marker wiederherzustellen."
|
||||
},
|
||||
{
|
||||
"path": "agent-system/runs/AUTH-LOGIN-A11Y-REGRESSION-001/plan.json",
|
||||
"summary": "Neuer isolierter Run-Plan fuer den Auth-Login-A11y-Regression-Fix angelegt."
|
||||
}
|
||||
],
|
||||
"guard_evidence": [
|
||||
{
|
||||
"guard_id": "GR-UI-009",
|
||||
"status": "pass",
|
||||
"evidence": "Der fehlende Marker fuer die Tenant-Auswahl wurde im bestehenden Fieldset-Kontext wiederhergestellt; AuthLoginAccessibilityContractTest ist gruen."
|
||||
},
|
||||
{
|
||||
"guard_id": "GR-UI-013",
|
||||
"status": "pass",
|
||||
"evidence": "Die Wiederherstellung erfolgte semantisch als legend innerhalb des Fieldsets, ohne ARIA-Hacks oder nicht-semantische Ersatzstrukturen."
|
||||
},
|
||||
{
|
||||
"guard_id": "GR-UI-010",
|
||||
"status": "pass",
|
||||
"evidence": "Der eingefuegte Label-Text nutzt t('Select tenant') und fuegt keine hartkodierte UI-Zeichenkette ein."
|
||||
},
|
||||
{
|
||||
"guard_id": "GR-TEST-001",
|
||||
"status": "pass",
|
||||
"evidence": "Der betroffene Contract-Test und die komplette Test-Suite wurden ausgefuehrt; der vorherige Failure ist behoben und QG-001 ist wieder gruen."
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/AuthLoginAccessibilityContractTest.php --no-progress",
|
||||
"result": "pass"
|
||||
},
|
||||
{
|
||||
"cmd": "docker compose exec php vendor/bin/phpunit",
|
||||
"result": "pass"
|
||||
},
|
||||
{
|
||||
"cmd": "docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
|
||||
"result": "pass"
|
||||
},
|
||||
{
|
||||
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php",
|
||||
"result": "pass"
|
||||
},
|
||||
{
|
||||
"cmd": "docker compose exec php composer cs:check",
|
||||
"result": "pass"
|
||||
}
|
||||
],
|
||||
"quality_gate_results": [
|
||||
{
|
||||
"gate_id": "QG-001",
|
||||
"result": "pass",
|
||||
"notes": "PHPUnit full: 454 tests, 10879 assertions, warnings/deprecations vorhanden aber keine Failures."
|
||||
},
|
||||
{
|
||||
"gate_id": "QG-002",
|
||||
"result": "pass",
|
||||
"notes": "PHPStan: [OK] No errors."
|
||||
},
|
||||
{
|
||||
"gate_id": "QG-003",
|
||||
"result": "pass",
|
||||
"notes": "CoreStarterkitContractTest: OK (11 tests, 6137 assertions)."
|
||||
},
|
||||
{
|
||||
"gate_id": "QG-006",
|
||||
"result": "pass",
|
||||
"notes": "composer cs:check: 0 von 514 Dateien fixbar."
|
||||
}
|
||||
],
|
||||
"test_results": [
|
||||
{
|
||||
"name": "AuthLoginAccessibilityContractTest",
|
||||
"result": "pass",
|
||||
"notes": "OK (4 tests, 85 assertions)."
|
||||
},
|
||||
{
|
||||
"name": "PHPUnit Full",
|
||||
"result": "pass",
|
||||
"notes": "OK, but there were issues: Warnings 1, Deprecations 1."
|
||||
},
|
||||
{
|
||||
"name": "PHPStan",
|
||||
"result": "pass",
|
||||
"notes": "No errors."
|
||||
},
|
||||
{
|
||||
"name": "CoreStarterkitContractTest",
|
||||
"result": "pass",
|
||||
"notes": "OK (11 tests, 6137 assertions)."
|
||||
},
|
||||
{
|
||||
"name": "PHP CS Check",
|
||||
"result": "pass",
|
||||
"notes": "0/514 files fixable."
|
||||
}
|
||||
],
|
||||
"open_items": []
|
||||
}
|
||||
128
agent-system/runs/AUTH-LOGIN-A11Y-REGRESSION-001/plan.json
Normal file
128
agent-system/runs/AUTH-LOGIN-A11Y-REGRESSION-001/plan.json
Normal file
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"task_id": "AUTH-LOGIN-A11Y-REGRESSION-001",
|
||||
"summary": "Isolierter Fix fuer den QG-001 Blocker im Auth-Login-Flow: fehlender Tenant-Label-Marker login-tenant-select-label in der choose_tenant-Stage wiederherstellen, Accessibility-Semantik intakt halten und Full PHPUnit wieder gruen bekommen.",
|
||||
"assumptions": [
|
||||
"Der Fehler ist eine Regression im Markup von pages/auth/login(login).phtml und nicht in Service- oder Auth-Logik.",
|
||||
"Der bestehende Contract-Test AuthLoginAccessibilityContractTest bleibt bestehen und wird nicht abgeschwaecht.",
|
||||
"Es sind keine DB- oder Backend-Flow-Aenderungen erforderlich."
|
||||
],
|
||||
"scope": {
|
||||
"in": [
|
||||
"Tenant-Auswahl-Markup in pages/auth/login(login).phtml (choose_tenant stage)",
|
||||
"Bei Bedarf minimale, additive CSS-Korrektur in web/css/pages/app-login.css fuer legend/label Darstellung",
|
||||
"Verifikation ueber bestehenden Architecture-Contract-Test und Full PHPUnit"
|
||||
],
|
||||
"out": [
|
||||
"Theme-Flow oder Tenant-Switch Logik ausserhalb Auth-Login",
|
||||
"Aenderungen an Auth-Service-Businesslogik in lib/Service/Auth",
|
||||
"Neues UI-Redesign oder Umbau des gesamten Auth-Login-Bereichs"
|
||||
]
|
||||
},
|
||||
"guardrails": {
|
||||
"required_guard_ids": [
|
||||
"GR-UI-009",
|
||||
"GR-UI-013",
|
||||
"GR-UI-010",
|
||||
"GR-TEST-001"
|
||||
],
|
||||
"required_quality_gate_ids": [
|
||||
"QG-001",
|
||||
"QG-002",
|
||||
"QG-003",
|
||||
"QG-006"
|
||||
]
|
||||
},
|
||||
"success_criteria": [
|
||||
{
|
||||
"id": "SC-001",
|
||||
"criterion": "Die choose_tenant-Stage enthaelt wieder eine klare textuelle Gruppenbezeichnung mit dem Marker login-tenant-select-label im Fieldset-Kontext."
|
||||
},
|
||||
{
|
||||
"id": "SC-002",
|
||||
"criterion": "AuthLoginAccessibilityContractTest ist gruen ohne Abschwaechung der Assertions."
|
||||
},
|
||||
{
|
||||
"id": "SC-003",
|
||||
"criterion": "QG-001 (Full PHPUnit) laeuft wieder erfolgreich ohne diesen Fail."
|
||||
},
|
||||
{
|
||||
"id": "SC-004",
|
||||
"criterion": "Keine Regression bei PHPStan/CoreStarterkitContract/CS-Check."
|
||||
}
|
||||
],
|
||||
"implementation_steps": [
|
||||
{
|
||||
"id": "S1",
|
||||
"title": "Regression reproduzieren und Ursache fixieren",
|
||||
"description": "Aktuellen Failure in AuthLoginAccessibilityContractTest bestaetigen und die fehlende Marker-Stelle im choose_tenant Fieldset lokalisieren.",
|
||||
"guard_refs": [
|
||||
"GR-TEST-001",
|
||||
"GR-UI-009"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "S2",
|
||||
"title": "Tenant-Label semantisch wiederherstellen",
|
||||
"description": "In pages/auth/login(login).phtml innerhalb von fieldset.login-tenant-fieldset eine textuelle Gruppenbezeichnung mit Klasse login-tenant-select-label einfuegen (bevorzugt als legend), i18n-konform mit t().",
|
||||
"guard_refs": [
|
||||
"GR-UI-013",
|
||||
"GR-UI-010"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "S3",
|
||||
"title": "Darstellung stabilisieren",
|
||||
"description": "Nur falls notwendig minimale additive CSS-Anpassung fuer legend/label vornehmen, ohne bestehende Tastatur- oder Screenreader-Semantik zu verschlechtern.",
|
||||
"guard_refs": [
|
||||
"GR-UI-009",
|
||||
"GR-UI-013"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "S4",
|
||||
"title": "Gates ausfuehren und evidenzbasiert abschliessen",
|
||||
"description": "QG-001, QG-002, QG-003 und QG-006 ausfuehren und in execution-report dokumentieren.",
|
||||
"guard_refs": [
|
||||
"GR-TEST-001",
|
||||
"GR-UI-009"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tests": [
|
||||
"docker compose exec php vendor/bin/phpunit tests/Architecture/AuthLoginAccessibilityContractTest.php --no-progress",
|
||||
"docker compose exec php vendor/bin/phpunit",
|
||||
"docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
|
||||
"docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php",
|
||||
"docker compose exec php composer cs:check"
|
||||
],
|
||||
"acceptance_checks": [
|
||||
"SC-001: login-tenant-select-label ist in pages/auth/login(login).phtml im choose_tenant-Fieldset vorhanden und textuell befuellt.",
|
||||
"SC-002: AuthLoginAccessibilityContractTest meldet PASS.",
|
||||
"SC-003: Full PHPUnit (QG-001) meldet PASS ohne den bisherigen Tenant-Label-Fehler.",
|
||||
"SC-004: QG-002, QG-003 und QG-006 melden PASS."
|
||||
],
|
||||
"ux_notes": {
|
||||
"affected_patterns": [
|
||||
"Auth tenant selection fieldset in login choose_tenant stage"
|
||||
],
|
||||
"ui_states_required": [
|
||||
"error",
|
||||
"success"
|
||||
],
|
||||
"a11y_touchpoints": [
|
||||
"Fieldset/legend semantics for tenant radio group",
|
||||
"Text label visibility for tenant selection group",
|
||||
"Keyboard-operable radio selection unchanged"
|
||||
]
|
||||
},
|
||||
"risks": [
|
||||
{
|
||||
"risk": "Visuelle Nebenwirkung durch legend-Einfuehrung im bestehenden Login-Layout.",
|
||||
"mitigation": "Nur minimale additive CSS-Anpassung, keine strukturellen Layout-Umbauten."
|
||||
},
|
||||
{
|
||||
"risk": "Scheinbarer Fix durch Test-Anpassung statt Markup-Korrektur.",
|
||||
"mitigation": "Contract-Test nicht abschwaechen; Marker semantisch im Template wiederherstellen."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"task_id": "AUTH-LOGIN-A11Y-REGRESSION-001",
|
||||
"verdict": "pass",
|
||||
"checked_guard_ids": [
|
||||
"GR-UI-009",
|
||||
"GR-UI-013",
|
||||
"GR-UI-010",
|
||||
"GR-TEST-001"
|
||||
],
|
||||
"checked_quality_gate_ids": [
|
||||
"QG-001",
|
||||
"QG-002",
|
||||
"QG-003",
|
||||
"QG-006"
|
||||
],
|
||||
"findings": []
|
||||
}
|
||||
@@ -21,7 +21,7 @@ Options:
|
||||
|
||||
Examples:
|
||||
bin/agent-prompts.sh --task AUTH-LOGIN-REVIEW-001
|
||||
bin/agent-prompts.sh --task AUTH-LOGIN-REVIEW-001 --role planner --goal "Auth/Login review"
|
||||
bin/agent-prompts.sh --task REVIEW-USER-THEME-001 --role planner --goal "Auth/Login review"
|
||||
bin/agent-prompts.sh --task TASK-42 --short
|
||||
USAGE
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"Login": "Login",
|
||||
"Login credentials": "Mit Zugangsdaten oder SSO anmelden",
|
||||
"Login options": "Anmeldeoptionen",
|
||||
"Register": "Registrieren",
|
||||
"Password": "Passwort",
|
||||
@@ -47,7 +48,7 @@
|
||||
"Logout": "Logout",
|
||||
"Account": "Konto",
|
||||
"Please enter your credentials": "Bitte geben Sie Ihre Zugangsdaten ein",
|
||||
"Enter your email to continue": "E-Mail eingeben und weiter",
|
||||
"Enter your email to continue": "E-Mail eingeben um fortzufahren",
|
||||
"Select your tenant to continue": "Mandant auswählen und weiter",
|
||||
"Choose how you want to sign in": "Anmeldeart auswählen",
|
||||
"Continue": "Weiter",
|
||||
@@ -1200,5 +1201,6 @@
|
||||
"Restore actions (7d)": "Wiederherstellungsaktionen (7d)",
|
||||
"Trend vs previous 7d": "Trend vs. vorherige 7d",
|
||||
"Top lifecycle reason codes (7d)": "Top-Lifecycle-Fehlercodes (7d)",
|
||||
"Recent lifecycle risks (7d)": "Aktuelle Lifecycle-Risiken (7d)"
|
||||
"Recent lifecycle risks (7d)": "Aktuelle Lifecycle-Risiken (7d)",
|
||||
"Login progress": "Anmeldefortschritt"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"Login": "Login",
|
||||
"Login credentials": "Login credentials",
|
||||
"Login options": "Login options",
|
||||
"Register": "Register",
|
||||
"Password": "Password",
|
||||
@@ -1200,5 +1201,6 @@
|
||||
"Restore actions (7d)": "Restore actions (7d)",
|
||||
"Trend vs previous 7d": "Trend vs previous 7d",
|
||||
"Top lifecycle reason codes (7d)": "Top lifecycle reason codes (7d)",
|
||||
"Recent lifecycle risks (7d)": "Recent lifecycle risks (7d)"
|
||||
"Recent lifecycle risks (7d)": "Recent lifecycle risks (7d)",
|
||||
"Login progress": "Login progress"
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ $authHelpContext = [
|
||||
];
|
||||
?>
|
||||
|
||||
<article>
|
||||
<article class="login-card">
|
||||
<?php require templatePath('partials/auth-logo.phtml'); ?>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h1><?php e(t('Forgot password')); ?></h1>
|
||||
|
||||
@@ -36,24 +36,18 @@ if ($selectedTenantInitial === '') {
|
||||
$errors = is_array($errors ?? null) ? $errors : [];
|
||||
$errorNoticeId = $errors ? 'auth-login-error-notice' : '';
|
||||
$loginHeading = t('Login');
|
||||
$loginSubheading = t('Please enter your credentials');
|
||||
$loginSubheading = t('Login credentials');
|
||||
$authHelpContext = ['show_support' => true];
|
||||
if ($stage === 'resolve_email') {
|
||||
$loginHeading = t('Login');
|
||||
$loginSubheading = t('Enter your email to continue');
|
||||
$authHelpContext = [
|
||||
'show_register' => true,
|
||||
'show_support' => true,
|
||||
];
|
||||
} elseif ($stage === 'choose_tenant') {
|
||||
$loginHeading = t('Select tenant');
|
||||
$loginSubheading = t('Select your tenant to continue');
|
||||
$authHelpContext = [
|
||||
'show_support' => true,
|
||||
];
|
||||
} else {
|
||||
$loginHeading = t('Login options');
|
||||
$loginSubheading = t('Choose how you want to sign in');
|
||||
$authHelpContext = [
|
||||
'show_forgot' => !empty($selectedTenantMethods['local']),
|
||||
'show_support' => true,
|
||||
@@ -63,6 +57,7 @@ if ($stage === 'resolve_email') {
|
||||
?>
|
||||
|
||||
<article class="login-card">
|
||||
<?php require templatePath('partials/auth-logo.phtml'); ?>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h1><?php e($loginHeading); ?></h1>
|
||||
@@ -70,6 +65,7 @@ if ($stage === 'resolve_email') {
|
||||
</hgroup>
|
||||
</header>
|
||||
|
||||
|
||||
<?php if ($errors): ?>
|
||||
<div id="<?php e($errorNoticeId); ?>" class="notice" data-variant="error" role="alert" aria-live="assertive" tabindex="-1">
|
||||
<ul>
|
||||
@@ -99,6 +95,7 @@ if ($stage === 'resolve_email') {
|
||||
<input type="hidden" name="email" value="<?php e($email); ?>">
|
||||
<input type="hidden" name="tenant_hint" value="<?php e($tenantHint); ?>">
|
||||
<fieldset class="login-tenant-fieldset">
|
||||
<legend class="login-tenant-select-label"><?php e(t('Select tenant')); ?></legend>
|
||||
<div class="login-tenant-choice-grid">
|
||||
<?php foreach ($tenantCandidates as $tenantCandidate): ?>
|
||||
<?php
|
||||
|
||||
@@ -22,7 +22,8 @@ $authHelpContext = [
|
||||
'show_support' => true,
|
||||
];
|
||||
?>
|
||||
<article>
|
||||
<article class="login-card">
|
||||
<?php require templatePath('partials/auth-logo.phtml'); ?>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h1><?php e(t('Register')); ?></h1>
|
||||
|
||||
@@ -18,7 +18,8 @@ $authHelpContext = [
|
||||
];
|
||||
?>
|
||||
|
||||
<article>
|
||||
<article class="login-card">
|
||||
<?php require templatePath('partials/auth-logo.phtml'); ?>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h1><?php e(t('Reset password')); ?></h1>
|
||||
|
||||
@@ -17,7 +17,8 @@ $authHelpContext = [
|
||||
];
|
||||
?>
|
||||
|
||||
<article>
|
||||
<article class="login-card">
|
||||
<?php require templatePath('partials/auth-logo.phtml'); ?>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h1><?php e(t('Verify code')); ?></h1>
|
||||
|
||||
@@ -17,7 +17,8 @@ $authHelpContext = [
|
||||
];
|
||||
?>
|
||||
|
||||
<article>
|
||||
<article class="login-card">
|
||||
<?php require templatePath('partials/auth-logo.phtml'); ?>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h1><?php e(t('Verify email')); ?></h1>
|
||||
|
||||
10
templates/partials/auth-logo.phtml
Normal file
10
templates/partials/auth-logo.phtml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
$authLogoUrl = appLogoUrl();
|
||||
if ($authLogoUrl === '') {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div class="login-logo">
|
||||
<img src="<?php e($authLogoUrl); ?>" alt="<?php e(appTitle()); ?>" class="login-logo-img">
|
||||
</div>
|
||||
21
templates/partials/auth-step-indicator.phtml
Normal file
21
templates/partials/auth-step-indicator.phtml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @var int $authStepCurrent 1-based current step (1, 2, or 3)
|
||||
* @var int $authStepTotal total number of steps (2 or 3)
|
||||
*/
|
||||
|
||||
$authStepCurrent = (int) ($authStepCurrent ?? 0);
|
||||
$authStepTotal = (int) ($authStepTotal ?? 0);
|
||||
if ($authStepCurrent < 1 || $authStepTotal < 2) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div class="login-step-indicator" aria-hidden="true">
|
||||
<?php for ($i = 1; $i <= $authStepTotal; $i++): ?>
|
||||
<span class="login-step-indicator-dot<?php if ($i === $authStepCurrent): ?> is-active<?php elseif ($i < $authStepCurrent): ?> is-completed<?php endif; ?>"></span>
|
||||
<?php if ($i < $authStepTotal): ?>
|
||||
<span class="login-step-indicator-line<?php if ($i < $authStepCurrent): ?> is-completed<?php endif; ?>"></span>
|
||||
<?php endif; ?>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
@@ -550,6 +550,59 @@
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* ── Logo ── */
|
||||
|
||||
.login-logo {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.login-logo-img {
|
||||
max-height: 2.5rem;
|
||||
max-width: 10rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* ── Password toggle ── */
|
||||
|
||||
.login-password-wrapper {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.login-password-wrapper > input {
|
||||
padding-right: 2.75rem;
|
||||
}
|
||||
|
||||
.login-password-toggle {
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
top: calc(50% - 8px);
|
||||
transform: translateY(-50%);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: var(--app-muted-color);
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
border-radius: var(--app-border-radius);
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.login-password-toggle:hover {
|
||||
color: var(--app-contrast);
|
||||
}
|
||||
|
||||
.login-password-toggle:focus-visible {
|
||||
outline: 2px solid var(--app-primary-focus);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
main.login-main {
|
||||
min-height: 100dvh;
|
||||
display: grid;
|
||||
@@ -693,7 +746,7 @@
|
||||
display: inline-block;
|
||||
padding: 0 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
color: var(--app-muted);
|
||||
color: var(--app-muted-color);
|
||||
background: var(--app-card-background-color);
|
||||
}
|
||||
|
||||
@@ -754,14 +807,6 @@
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.login-tenant-context-title {
|
||||
margin: 0;
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: uppercase;
|
||||
color: var(--app-muted);
|
||||
}
|
||||
|
||||
.login-tenant-context-name {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
@@ -788,6 +833,8 @@
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
gap: 10px;
|
||||
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
@@ -798,20 +845,10 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.login-help-links-item + .login-help-links-item::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 1px;
|
||||
height: 0.85em;
|
||||
margin-right: 0.8rem;
|
||||
background: color-mix(in srgb, var(--app-muted) 55%, transparent);
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.login-security-note {
|
||||
font-size: 0.72rem;
|
||||
margin-bottom: 1px;
|
||||
color: color-mix(in srgb, var(--app-contrast) 78%, var(--app-muted));
|
||||
color: color-mix(in srgb, var(--app-contrast) 78%, var(--app-muted-color));
|
||||
}
|
||||
|
||||
.login-security-note i {
|
||||
@@ -823,7 +860,7 @@
|
||||
width: 100%;
|
||||
display: block;
|
||||
font-size: 9px;
|
||||
color: color-mix(in srgb, var(--app-muted) 75%, transparent);
|
||||
color: color-mix(in srgb, var(--app-muted-color) 75%, transparent);
|
||||
}
|
||||
|
||||
form[aria-busy="true"] button[type="submit"],
|
||||
@@ -832,10 +869,6 @@
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.login-secondary-action {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html[data-theme="dark"] #gradient {
|
||||
animation: none;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import './core/app-telemetry.js';
|
||||
import './components/app-flash-auto-dismiss.js';
|
||||
import './components/app-password-hints.js';
|
||||
import './components/app-password-toggle.js';
|
||||
|
||||
const focusPrimaryErrorNotice = () => {
|
||||
const errorNotice = document.querySelector(
|
||||
@@ -29,6 +30,7 @@ const lockLoginSubmitButtons = () => {
|
||||
return;
|
||||
}
|
||||
submitButton.disabled = true;
|
||||
submitButton.setAttribute('aria-busy', 'true');
|
||||
submitButton.dataset.submitState = 'loading';
|
||||
});
|
||||
});
|
||||
|
||||
40
web/js/components/app-password-toggle.js
Normal file
40
web/js/components/app-password-toggle.js
Normal file
@@ -0,0 +1,40 @@
|
||||
export function initPasswordToggles() {
|
||||
const inputs = document.querySelectorAll('input[type="password"]');
|
||||
if (!inputs.length) {return;}
|
||||
|
||||
inputs.forEach((input) => {
|
||||
if (!(input instanceof HTMLInputElement)) {return;}
|
||||
const label = input.closest('label');
|
||||
if (!label) {return;}
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'login-password-wrapper';
|
||||
|
||||
const toggle = document.createElement('button');
|
||||
toggle.type = 'button';
|
||||
toggle.className = 'login-password-toggle';
|
||||
toggle.setAttribute('aria-label', 'Show password');
|
||||
toggle.tabIndex = -1;
|
||||
toggle.innerHTML = '<i class="bi bi-eye" aria-hidden="true"></i>';
|
||||
|
||||
input.replaceWith(wrapper);
|
||||
wrapper.appendChild(input);
|
||||
wrapper.appendChild(toggle);
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
const isPassword = input.type === 'password';
|
||||
input.type = isPassword ? 'text' : 'password';
|
||||
toggle.innerHTML = isPassword
|
||||
? '<i class="bi bi-eye-slash" aria-hidden="true"></i>'
|
||||
: '<i class="bi bi-eye" aria-hidden="true"></i>';
|
||||
toggle.setAttribute('aria-label', isPassword ? 'Hide password' : 'Show password');
|
||||
input.focus();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initPasswordToggles);
|
||||
} else {
|
||||
initPasswordToggles();
|
||||
}
|
||||
Reference in New Issue
Block a user