feat(atm-plataforma): tela de login Athletic Map

This commit is contained in:
deploy
2026-10-08 21:23:53 +00:00
parent ed805b4287
commit f0adecb124
+190 -1
View File
@@ -1,5 +1,5 @@
# GERADO por scripts/tema_login_escolinha.py — nao edite na mao.
# Fonte: keycloak-theme/escolinha/*.ftl (login.ftl, login-update-password.ftl, estilo.ftl, template.ftl, error.ftl, info.ftl, logout-confirm.ftl, login-page-expired.ftl, login-reset-password.ftl, login-config-totp.ftl, login-otp.ftl, webauthn-register.ftl, webauthn-authenticate.ftl, select-authenticator.ftl)
# Fonte: keycloak-theme/escolinha/*.ftl (login.ftl, login-update-password.ftl, estilo.ftl, template.ftl, error.ftl, info.ftl, logout-confirm.ftl, login-page-expired.ftl, login-reset-password.ftl, login-config-totp.ftl, login-otp.ftl, webauthn-register.ftl, webauthn-authenticate.ftl, select-authenticator.ftl, login-username.ftl, login-password.ftl)
apiVersion: v1
kind: ConfigMap
metadata:
@@ -616,6 +616,26 @@ data:
.credenciais input:checked+label.cartao{border-color:var(--orange);box-shadow:0 0 0 3px rgba(245,130,32,.16)}
.conteudo .cartao.estatico{cursor:default}
/* "Primeiro o usuario, depois a senha" (login-username, login-password —
2026-10-08): os mesmos campos do login, agora DENTRO de `.conteudo`, cujo
`button` laranja de largura toda e `a` sublinhado engoliriam o olho da
senha, o "Esqueceu sua senha?" e o botao de passkey da macro do base. */
.conteudo button.olho{width:40px;height:40px;margin:0;padding:0;background:transparent;
color:#8F8F8B;border-radius:9px}
.conteudo button.olho:hover{background:#F2F0EC;color:var(--navy)}
.conteudo .esqueci{text-decoration:none}
.conteudo .esqueci:hover{text-decoration:underline}
.sub .trocar-conta{color:var(--blue);text-decoration:none;white-space:nowrap}
.sub .trocar-conta:hover{text-decoration:underline}
.sociais{display:flex;flex-direction:column;gap:12px;margin-top:16px}
.conteudo a#authenticateWebAuthnButton{
display:flex;align-items:center;justify-content:center;gap:10px;
width:100%;height:56px;margin-top:12px;border-radius:12px;box-sizing:border-box;
font-size:16px;font-weight:700;text-decoration:none;
background:#fff;color:var(--navy);border:1.5px solid var(--line);
}
.conteudo a#authenticateWebAuthnButton:hover{background:#F6F4F1;color:var(--navy)}
/* ---------- Telas estreitas ---------- */
@media (max-width:980px){
.stage{grid-template-columns:1fr}
@@ -1438,3 +1458,172 @@ data:
</#if>
</@layout.registrationLayout>
login-username.ftl: |-
<#--
PRIMEIRO O USUARIO — a etapa "Username Form" de um fluxo que pede o e-mail
numa tela e a senha (ou a passkey) na seguinte. A senha e a login-password.ftl.
Nenhum realm usa esse fluxo hoje (o login de todos e a login.ftl, usuario e
senha juntos). Esta pagina existe para que, se um fluxo passar a usa-lo — o
caminho natural da passkey sem senha —, a tela nao caia no tema de fabrica no
meio do login (2026-10-08).
NAO MUDE OS NOMES: sao os do tema base do Keycloak 26.x. O authenticator le
`username` e `rememberMe` do POST; o form e `kc-form-login` e o botao
`kc-login` (name `login`, que o `onsubmit` desliga); o erro do campo e
`input-error-username`. Com `usernameHidden` (o usuario ja foi identificado
por outra etapa), o campo e o "Lembrar-me" saem, como no base.
A passkey e a macro do tema BASE (`passkeys.ftl`), como no login.ftl: o
`webauthn` no autocomplete faz o navegador sugerir a passkey no proprio campo,
e sem a politica "Passkeys" no realm a macro nao desenha nada.
-->
<#import "template.ftl" as layout>
<#import "passkeys.ftl" as passkeys>
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username') displayInfo=(realm.password && realm.registrationAllowed && !registrationDisabled??); section>
<#if section = "header">
<#if (realm.name!"") == "athleticmap">
Painel do <span class="hl">dono</span>
<#elseif (realm.name!"") == "clientes">
Painel da sua <span class="hl">conta</span>
<#else>
Bem-vindo de <span class="hl">volta!</span>
</#if>
<#elseif section = "sub">
<#if (realm.name!"") == "athleticmap">
&Aacute;rea restrita &agrave; administra&ccedil;&atilde;o da Athletic Map
<#else>
Acesse sua conta para continuar
</#if>
<#elseif section = "form">
<#if realm.password>
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}" method="post" novalidate>
<#if !usernameHidden??>
<div class="campo">
<label class="rotulo" for="username"><#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>
<div class="caixa">
<span class="icone">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5" width="18" height="14" rx="2.5"/><path d="m3.5 7 8.5 6 8.5-6"/></svg>
</span>
<input tabindex="1" id="username" name="username" type="text" autofocus dir="ltr"
placeholder="seu@email.com" value="${(login.username!'')}"
autocomplete="${(enableWebAuthnConditionalUI?has_content)?then('username webauthn', 'username')}"
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
class="<#if messagesPerField.existsError('username')>erro</#if>"/>
</div>
<#if messagesPerField.existsError('username')>
<span id="input-error-username" class="erro-campo" aria-live="polite">${kcSanitize(messagesPerField.get('username'))?no_esc}</span>
</#if>
</div>
</#if>
<#if realm.rememberMe && !usernameHidden??>
<div class="opcoes">
<label class="lembrar">
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox"
<#if login.rememberMe??>checked</#if>/> ${msg("rememberMe")}
</label>
</div>
</#if>
<button tabindex="4" class="botao entrar" name="login" id="kc-login" type="submit">
Continuar
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m13 6 6 6-6 6"/></svg>
</button>
</form>
<#-- FORA do form: a macro traz os proprios forms (`webauth`, `authn_select`). -->
<@passkeys.conditionalUIData />
</#if>
<#elseif section = "info">
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
<div id="kc-registration">
<span>${msg("noAccount")} <a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
</div>
</#if>
<#elseif section = "socialProviders">
<#-- Mesmo desenho do login.ftl. Hoje nenhum realm tem provedor. -->
<#if realm.password && social?? && social.providers?? && social.providers?has_content>
<div id="kc-social-providers" class="sociais">
<div class="ou">ou</div>
<#list social.providers as p>
<a id="social-${p.alias!''}" class="botao social" href="${p.loginUrl}">
<#if p.providerId?? && p.providerId == "google">
<svg width="20" height="20" viewBox="0 0 48 48" aria-hidden="true"><path fill="#4285F4" d="M45.1 24.5c0-1.6-.1-3.1-.4-4.5H24v8.5h11.8c-.5 2.8-2.1 5.1-4.4 6.7v5.6h7.1c4.2-3.8 6.6-9.5 6.6-16.3z"/><path fill="#34A853" d="M24 46c6 0 11-2 14.6-5.4l-7.1-5.6c-2 1.3-4.5 2.1-7.5 2.1-5.8 0-10.7-3.9-12.4-9.1H4.3v5.8C7.9 41.1 15.4 46 24 46z"/><path fill="#FBBC05" d="M11.6 28c-.4-1.3-.7-2.6-.7-4s.3-2.7.7-4v-5.8H4.3C2.8 17.1 2 20.4 2 24s.8 6.9 2.3 9.8L11.6 28z"/><path fill="#EA4335" d="M24 10.9c3.3 0 6.2 1.1 8.5 3.3l6.3-6.3C35 4.4 30 2 24 2 15.4 2 7.9 6.9 4.3 14.2l7.3 5.8c1.7-5.2 6.6-9.1 12.4-9.1z"/></svg>
<#else>
<svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3 4 6v6c0 5 3.4 8 8 9 4.6-1 8-4 8-9V6l-8-3Z"/></svg>
</#if>
Entrar com ${p.displayName!'SSO'}
</a>
</#list>
</div>
</#if>
</#if>
</@layout.registrationLayout>
login-password.ftl: |-
<#--
DEPOIS A SENHA — a etapa "Password Form" de um fluxo que pediu o e-mail
antes (login-username.ftl, ou o usuario ja identificado por outra etapa).
Nenhum realm usa esse fluxo hoje; ver o cabecalho da login-username.ftl.
NAO MUDE OS NOMES: sao os do tema base do Keycloak 26.x. O authenticator le
`password` do POST; o form e `kc-form-login`, o botao `kc-login` (name
`login`) e o erro do campo `input-error-password`.
O tema base mostra QUEM esta entrando (`kc-attempted-username`) com o link
para recomecar (`reset-login` -> `url.loginRestartFlowUrl`) na moldura dele.
A moldura deste tema nao faz isso, entao a pagina faz, no subtitulo: sem o
link, quem digitou o e-mail errado so sai fechando a aba. O "Tente outra
forma" (passkey, 2FA) e da moldura e aparece sozinho quando o fluxo oferece.
-->
<#import "template.ftl" as layout>
<#import "passkeys.ftl" as passkeys>
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('password'); section>
<#if section = "header">
Digite sua <span class="hl">senha</span>
<#elseif section = "sub">
<#if (auth.attemptedUsername)?has_content>
Entrando como <b id="kc-attempted-username">${auth.attemptedUsername}</b>.
<a id="reset-login" class="trocar-conta" href="${url.loginRestartFlowUrl}" aria-label="${msg("restartLoginTooltip")}">N&atilde;o &eacute; voc&ecirc;?</a>
<#else>
Acesse sua conta para continuar
</#if>
<#elseif section = "form">
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}" method="post" novalidate>
<div class="campo">
<label class="rotulo" for="password">${msg("password")}</label>
<div class="caixa">
<span class="icone">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="10" width="16" height="11" rx="2.4"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg>
</span>
<input tabindex="2" id="password" name="password" type="password" autofocus dir="ltr"
placeholder="••••••••" autocomplete="current-password"
aria-invalid="<#if messagesPerField.existsError('password')>true</#if>"
class="<#if messagesPerField.existsError('password')>erro</#if>"/>
<button type="button" class="olho" aria-label="Mostrar senha" aria-controls="password"
onclick="var p=document.getElementById('password');p.type=p.type==='password'?'text':'password';this.setAttribute('aria-label',p.type==='password'?'Mostrar senha':'Ocultar senha');">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>
</button>
</div>
<#if messagesPerField.existsError('password')>
<span id="input-error-password" class="erro-campo" aria-live="polite">${kcSanitize(messagesPerField.get('password'))?no_esc}</span>
</#if>
</div>
<#if realm.resetPasswordAllowed>
<div class="opcoes">
<a tabindex="5" class="esqueci" href="${url.loginResetCredentialsUrl}">Esqueceu sua senha?</a>
</div>
</#if>
<button tabindex="4" class="botao entrar" name="login" id="kc-login" type="submit">
Entrar
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m13 6 6 6-6 6"/></svg>
</button>
</form>
<#-- Como no keycloak.v2: a macro so desenha com a politica "Passkeys" ligada. -->
<@passkeys.conditionalUIData />
</#if>
</@layout.registrationLayout>