livrare lot 2
This commit is contained in:
commit
8ecc78e729
763 changed files with 164593 additions and 0 deletions
|
|
@ -0,0 +1,50 @@
|
|||
<#assign customVerifyUrl = "https://didi365.eu/api/auth/verify-email?key=" + link?keep_after("key=")>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Verifică adresa de email</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f4f4;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color: #f4f4f4;">
|
||||
<tr>
|
||||
<td align="center" style="padding: 40px 20px;">
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="background-color: #ffffff; border-radius: 12px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);">
|
||||
<tr>
|
||||
<td style="background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); padding: 40px 40px 30px; border-radius: 12px 12px 0 0; text-align: center;">
|
||||
<h1 style="color: #ffffff; margin: 0; font-size: 28px; font-weight: 600;">DIDI</h1>
|
||||
<p style="color: #a0a0a0; margin: 8px 0 0; font-size: 14px;">Misinformation Detection Platform</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 40px;">
|
||||
<h2 style="color: #1a1a2e; margin: 0 0 20px; font-size: 22px; font-weight: 600;">Verifică adresa de email</h2>
|
||||
<p style="color: #555555; font-size: 16px; line-height: 1.6; margin: 0 0 25px;">Salut <strong>${user.firstName!""}</strong>,</p>
|
||||
<p style="color: #555555; font-size: 16px; line-height: 1.6; margin: 0 0 25px;">Mulțumim pentru înregistrare! Pentru a activa contul tău DIDI, te rugăm să confirmi adresa de email apăsând butonul de mai jos:</p>
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="margin: 30px 0;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="${customVerifyUrl}" target="_blank" style="display: inline-block; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: #ffffff; text-decoration: none; padding: 16px 40px; border-radius: 8px; font-size: 16px; font-weight: 600; box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);">Verifică Email</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p style="color: #888888; font-size: 14px; line-height: 1.6; margin: 25px 0 0;">Sau copiază acest link în browser:</p>
|
||||
<p style="color: #4CAF50; font-size: 13px; word-break: break-all; background-color: #f8f8f8; padding: 12px; border-radius: 6px; margin: 10px 0 25px;">${customVerifyUrl}</p>
|
||||
<p style="color: #888888; font-size: 14px; line-height: 1.6; margin: 0;">Link-ul expiră în <strong>${linkExpiration}</strong>.</p>
|
||||
<hr style="border: none; border-top: 1px solid #eeeeee; margin: 30px 0;">
|
||||
<p style="color: #999999; font-size: 13px; line-height: 1.6; margin: 0;">Dacă nu ai creat un cont pe DIDI, poți ignora acest email.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="background-color: #f8f8f8; padding: 25px 40px; border-radius: 0 0 12px 12px; text-align: center;">
|
||||
<p style="color: #999999; font-size: 12px; margin: 0 0 8px;">© 2025 didi - Misinformation Detection Platform</p>
|
||||
<p style="color: #bbbbbb; font-size: 11px; margin: 0;">Acest email a fost trimis automat. Te rugăm să nu răspunzi.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<#assign customVerifyUrl = "https://didi365.eu/api/auth/verify-email?key=" + link?keep_after("key=")>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Verifică adresa de email</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #050510;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color: #050510;">
|
||||
<tr>
|
||||
<td align="center" style="padding: 40px 20px;">
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="background-color: #0f0f1a; border-radius: 16px; box-shadow: 0 8px 32px rgba(124, 58, 237, 0.3); border: 1px solid rgba(124, 58, 237, 0.2);">
|
||||
<tr>
|
||||
<td style="background: linear-gradient(135deg, #7c3aed 0%, #4f46e5 100%); padding: 40px 40px 30px; border-radius: 16px 16px 0 0; text-align: center;">
|
||||
<h1 style="color: #ffffff; margin: 0; font-size: 36px; font-weight: 700; letter-spacing: 2px;">didi</h1>
|
||||
<p style="color: rgba(255,255,255,0.7); margin: 8px 0 0; font-size: 14px; letter-spacing: 1px;">Misinformation Detection Platform</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 40px;">
|
||||
<h2 style="color: #E8E8E8; margin: 0 0 20px; font-size: 22px; font-weight: 600;">Verifică adresa de email</h2>
|
||||
<p style="color: #D1D1D1; font-size: 16px; line-height: 1.6; margin: 0 0 25px;">Salut <strong style="color: #00d4ff;">${user.firstName!""}</strong>,</p>
|
||||
<p style="color: #D1D1D1; font-size: 16px; line-height: 1.6; margin: 0 0 25px;">Pentru a-ți activa contul în platforma <strong style="color: #7c3aed;">didi</strong>, te rugăm să confirmi adresa de email apăsând butonul de mai jos:</p>
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="margin: 30px 0;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="${customVerifyUrl}" target="_blank" style="display: inline-block; background: linear-gradient(135deg, #7c3aed 0%, #4f46e5 100%); color: #ffffff; text-decoration: none; padding: 16px 48px; border-radius: 12px; font-size: 16px; font-weight: 600; box-shadow: 0 4px 20px rgba(124, 58, 237, 0.4); letter-spacing: 0.5px;">Verifică Email</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p style="color: #9CA3AF; font-size: 14px; line-height: 1.6; margin: 25px 0 0;">Sau copiază acest link în browser:</p>
|
||||
<p style="color: #00d4ff; font-size: 12px; word-break: break-all; background-color: rgba(124, 58, 237, 0.1); padding: 14px; border-radius: 8px; margin: 10px 0 25px; border: 1px solid rgba(124, 58, 237, 0.2);">${customVerifyUrl}</p>
|
||||
<p style="color: #9CA3AF; font-size: 14px; line-height: 1.6; margin: 0;">Link-ul expiră în <strong style="color: #E8E8E8;"><#if linkExpiration?number gt 60>${(linkExpiration?number / 60)?round} minute<#else>${linkExpiration} secunde</#if></strong>.</p>
|
||||
<hr style="border: none; border-top: 1px solid rgba(124, 58, 237, 0.2); margin: 30px 0;">
|
||||
<p style="color: #6B7280; font-size: 13px; line-height: 1.6; margin: 0;">Dacă nu ai solicitat această acțiune, poți ignora acest email.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="background-color: rgba(124, 58, 237, 0.05); padding: 25px 40px; border-radius: 0 0 16px 16px; text-align: center; border-top: 1px solid rgba(124, 58, 237, 0.1);">
|
||||
<p style="color: #9CA3AF; font-size: 12px; margin: 0 0 8px;">© 2025 <span style="color: #7c3aed; font-weight: 600;">didi</span> - Misinformation Detection Platform</p>
|
||||
<p style="color: #6B7280; font-size: 11px; margin: 0;">Acest email a fost trimis automat. Te rugăm să nu răspunzi.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
Salut ${user.firstName!""},
|
||||
|
||||
Mulțumim pentru înregistrare pe DIDI!
|
||||
|
||||
Pentru a activa contul tău, te rugăm să accesezi link-ul de mai jos:
|
||||
|
||||
${link}
|
||||
|
||||
Link-ul expiră în ${linkExpiration}.
|
||||
|
||||
Dacă nu ai creat un cont pe DIDI, poți ignora acest email.
|
||||
|
||||
---
|
||||
DIDI - Misinformation Detection Platform
|
||||
|
|
@ -0,0 +1 @@
|
|||
parent=base
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.registrationLayout displayMessage=true; section>
|
||||
<#if section = "header">
|
||||
<#if messageHeader??>
|
||||
${messageHeader}
|
||||
<#else>
|
||||
${message.summary}
|
||||
</#if>
|
||||
<#elseif section = "form">
|
||||
<div id="kc-info-message">
|
||||
<p class="instruction">${message.summary}<#if requiredActions??><#list requiredActions>: <b><#items as reqActionItem>${kcSanitize(msg("requiredAction.${reqActionItem}"))?no_esc}<#sep>, </#items></b></#list><#else></#if></p>
|
||||
<#if skipLink??>
|
||||
<#else>
|
||||
<#-- Mobile app deep link redirect -->
|
||||
<#if client?? && client.clientId?? && client.clientId == "didi-mobile-app">
|
||||
<#assign mobileRedirectUrl = "didi://email-verified">
|
||||
<p style="text-align: center; margin-top: 20px;">
|
||||
<a href="${mobileRedirectUrl}" style="display: inline-block; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 8px; font-size: 16px; font-weight: 600;">
|
||||
Deschide aplicația DIDI
|
||||
</a>
|
||||
</p>
|
||||
<p style="color: #666; font-size: 14px; text-align: center; margin-top: 15px;">
|
||||
Vei fi redirecționat automat către aplicație...
|
||||
</p>
|
||||
<script>
|
||||
// Try to redirect to mobile app after 1.5 seconds
|
||||
setTimeout(function() {
|
||||
window.location.href = "${mobileRedirectUrl}";
|
||||
}, 1500);
|
||||
|
||||
// Fallback: if deep link fails, try alternative scheme
|
||||
setTimeout(function() {
|
||||
window.location.href = "com.didi365.app://email-verified";
|
||||
}, 3000);
|
||||
</script>
|
||||
<#-- Web app redirect -->
|
||||
<#elseif client?? && client.clientId?? && client.clientId == "didi-web-app">
|
||||
<#assign webRedirectUrl = "/email-verified">
|
||||
<p style="text-align: center; margin-top: 20px;">
|
||||
<a href="${webRedirectUrl}" style="display: inline-block; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 8px; font-size: 16px; font-weight: 600;">
|
||||
Continuă către aplicație
|
||||
</a>
|
||||
</p>
|
||||
<p style="color: #666; font-size: 14px; text-align: center; margin-top: 15px;">
|
||||
Vei fi redirecționat automat...
|
||||
</p>
|
||||
<script>
|
||||
setTimeout(function() {
|
||||
window.location.href = "${webRedirectUrl}";
|
||||
}, 2000);
|
||||
</script>
|
||||
<#-- Standard Keycloak redirects -->
|
||||
<#elseif pageRedirectUri?has_content>
|
||||
<p><a href="${pageRedirectUri}">${kcSanitize(msg("backToApplication"))?no_esc}</a></p>
|
||||
<script>
|
||||
setTimeout(function() {
|
||||
window.location.href = "${pageRedirectUri}";
|
||||
}, 2000);
|
||||
</script>
|
||||
<#elseif actionUri?has_content>
|
||||
<p><a href="${actionUri}">${kcSanitize(msg("proceedWithAction"))?no_esc}</a></p>
|
||||
<script>
|
||||
setTimeout(function() {
|
||||
window.location.href = "${actionUri}";
|
||||
}, 2000);
|
||||
</script>
|
||||
<#elseif (client.baseUrl)?has_content>
|
||||
<p><a href="${client.baseUrl}">${kcSanitize(msg("backToApplication"))?no_esc}</a></p>
|
||||
<script>
|
||||
setTimeout(function() {
|
||||
window.location.href = "${client.baseUrl}";
|
||||
}, 2000);
|
||||
</script>
|
||||
<#-- Fallback: detect mobile browser and redirect accordingly -->
|
||||
<#else>
|
||||
<div id="redirect-buttons" style="text-align: center; margin-top: 20px;">
|
||||
<p style="color: #666; font-size: 14px; margin-bottom: 15px;">
|
||||
Alege cum dorești să continui:
|
||||
</p>
|
||||
<p>
|
||||
<a href="didi://email-verified" id="mobile-link" style="display: inline-block; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 8px; font-size: 16px; font-weight: 600; margin: 5px;">
|
||||
📱 Deschide în aplicație
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/email-verified" id="web-link" style="display: inline-block; background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%); color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 8px; font-size: 16px; font-weight: 600; margin: 5px;">
|
||||
🌐 Continuă în browser
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
var ua = navigator.userAgent || navigator.vendor || window.opera;
|
||||
var isMobile = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(ua.toLowerCase());
|
||||
|
||||
if (isMobile) {
|
||||
// On mobile, try deep link first
|
||||
setTimeout(function() {
|
||||
window.location.href = "didi://email-verified";
|
||||
}, 1500);
|
||||
// Fallback to alternative scheme
|
||||
setTimeout(function() {
|
||||
window.location.href = "com.didi365.app://email-verified";
|
||||
}, 3000);
|
||||
} else {
|
||||
// On desktop, redirect to web app
|
||||
setTimeout(function() {
|
||||
window.location.href = "/email-verified";
|
||||
}, 2000);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</#if>
|
||||
</#if>
|
||||
</div>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.registrationLayout displayInfo=true displayMessage=!messagesPerField.existsError('username'); section>
|
||||
<#if section = "header">
|
||||
${msg("emailForgotTitle")}
|
||||
<#elseif section = "form">
|
||||
<form id="kc-reset-password-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||
<div class="back-link">
|
||||
<a href="${url.loginUrl}">${msg("backToLogin")}</a>
|
||||
</div>
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<label for="username" class="${properties.kcLabelClass!}">
|
||||
<#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if>
|
||||
</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<#if auth?has_content && auth.showUsername()>
|
||||
<input type="text" id="username" name="username" class="${properties.kcInputClass!}" autofocus value="${auth.attemptedUsername}" aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"/>
|
||||
<#else>
|
||||
<input type="text" id="username" name="username" class="${properties.kcInputClass!}" autofocus aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"/>
|
||||
</#if>
|
||||
<#if messagesPerField.existsError('username')>
|
||||
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<#elseif section = "info" >
|
||||
<#if realm.duplicateEmailsAllowed>
|
||||
${msg("emailInstructionUsername")}
|
||||
<#else>
|
||||
${msg("emailInstruction")}
|
||||
</#if>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.registrationLayout displayInfo=true displayMessage=false; section>
|
||||
<#if section = "header">
|
||||
${msg("emailVerifyTitle")}
|
||||
<#elseif section = "form">
|
||||
<div class="verify-email-box">
|
||||
<p class="instruction">
|
||||
${msg("emailVerifyInstruction1")}
|
||||
</p>
|
||||
<p class="instruction auto-check">
|
||||
<span id="check-status">${msg("emailVerifyAutoCheck")}</span>
|
||||
<span class="loader"></span>
|
||||
</p>
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(function() { location.reload(); }, 5000);
|
||||
</script>
|
||||
<#elseif section = "info">
|
||||
<p class="instruction">
|
||||
${msg("emailVerifyInstruction3")}
|
||||
<br/><br/>
|
||||
<a href="${url.loginAction}">${msg("doClickHere")}</a> ${msg("emailVerifyInstruction2")}
|
||||
</p>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# DIDI Custom Messages
|
||||
|
||||
# Forgot Password
|
||||
emailForgotTitle=Reset your password
|
||||
emailInstruction=Enter your email address and we will send you instructions to reset your password.
|
||||
|
||||
# Labels - hide them, use placeholders instead
|
||||
usernameOrEmail=Email
|
||||
username=Email
|
||||
email=Email
|
||||
password=Password
|
||||
passwordConfirm=Confirm Password
|
||||
firstName=First Name
|
||||
lastName=Last Name
|
||||
|
||||
# Login
|
||||
loginTitle=Sign in
|
||||
doLogIn=Sign in
|
||||
loginAccountTitle=Sign in to your account
|
||||
|
||||
# Register
|
||||
registerTitle=Create account
|
||||
doRegister=Create account
|
||||
|
||||
# Back link - keep it simple
|
||||
backToLogin=\u2190 Back to sign in
|
||||
|
||||
# Email Verification
|
||||
emailVerifyTitle=Verify your email
|
||||
emailVerifyInstruction1=You need to verify your email address to activate your account.
|
||||
emailVerifyInstruction2=to re-send the email.
|
||||
emailVerifyInstruction3=Haven't received a verification code in your email?
|
||||
emailVerifyAutoCheck=Checking verification status...
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<#macro termsAcceptance>
|
||||
<#if termsAcceptanceRequired??>
|
||||
<div class="form-group">
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
${msg("termsTitle")}
|
||||
<div id="kc-registration-terms-text">
|
||||
${kcSanitize(msg("termsText"))?no_esc}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<input type="checkbox" id="termsAccepted" name="termsAccepted" class="${properties.kcCheckboxInputClass!}"
|
||||
aria-invalid="<#if messagesPerField.existsError('termsAccepted')>true</#if>"
|
||||
/>
|
||||
<label for="termsAccepted" class="${properties.kcLabelClass!}">${msg("acceptTerms")}</label>
|
||||
</div>
|
||||
<#if messagesPerField.existsError('termsAccepted')>
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<span id="input-error-terms-accepted" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('termsAccepted'))?no_esc}
|
||||
</span>
|
||||
</div>
|
||||
</#if>
|
||||
</div>
|
||||
</#if>
|
||||
</#macro>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<#import "register-commons.ftl" as registerCommons>
|
||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('firstName','lastName','email','username','password','password-confirm','termsAccepted'); section>
|
||||
<#if section = "header">
|
||||
${msg("registerTitle")}
|
||||
<#elseif section = "form">
|
||||
<form id="kc-register-form" class="${properties.kcFormClass!}" action="${url.registrationAction}" method="post">
|
||||
<div class="back-link">
|
||||
<a href="${url.loginUrl}">${kcSanitize(msg("backToLogin"))?no_esc}</a>
|
||||
</div>
|
||||
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<input type="text" id="firstName" class="${properties.kcInputClass!}" name="firstName"
|
||||
value="${(register.formData.firstName!'')}"
|
||||
aria-invalid="<#if messagesPerField.existsError('firstName')>true</#if>"
|
||||
/>
|
||||
<#if messagesPerField.existsError('firstName')>
|
||||
<span id="input-error-firstname" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('firstName'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<input type="text" id="lastName" class="${properties.kcInputClass!}" name="lastName"
|
||||
value="${(register.formData.lastName!'')}"
|
||||
aria-invalid="<#if messagesPerField.existsError('lastName')>true</#if>"
|
||||
/>
|
||||
<#if messagesPerField.existsError('lastName')>
|
||||
<span id="input-error-lastname" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('lastName'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<input type="text" id="email" class="${properties.kcInputClass!}" name="email"
|
||||
value="${(register.formData.email!'')}" autocomplete="email"
|
||||
aria-invalid="<#if messagesPerField.existsError('email')>true</#if>"
|
||||
/>
|
||||
<#if messagesPerField.existsError('email')>
|
||||
<span id="input-error-email" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('email'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<#if !realm.registrationEmailAsUsername>
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<input type="text" id="username" class="${properties.kcInputClass!}" name="username"
|
||||
value="${(register.formData.username!'')}" autocomplete="username"
|
||||
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
|
||||
/>
|
||||
<#if messagesPerField.existsError('username')>
|
||||
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
<#if passwordRequired??>
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<input type="password" id="password" class="${properties.kcInputClass!}" name="password"
|
||||
autocomplete="new-password"
|
||||
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
||||
/>
|
||||
<#if messagesPerField.existsError('password')>
|
||||
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<input type="password" id="password-confirm" class="${properties.kcInputClass!}"
|
||||
name="password-confirm"
|
||||
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
||||
/>
|
||||
<#if messagesPerField.existsError('password-confirm')>
|
||||
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('password-confirm'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
<@registerCommons.termsAcceptance/>
|
||||
|
||||
<#if recaptchaRequired??>
|
||||
<div class="form-group">
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<div class="g-recaptcha" data-size="compact" data-sitekey="${recaptchaSiteKey}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
||||
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doRegister")}"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
||||
|
|
@ -0,0 +1,896 @@
|
|||
/* DIDI Login Theme - Dark Purple */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Outfit:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
/* ========== ROOT VARIABLES ========== */
|
||||
:root {
|
||||
/* Background */
|
||||
--bg-primary: #050510;
|
||||
--bg-secondary: #0a0a1a;
|
||||
--surface: rgba(255, 255, 255, 0.03);
|
||||
--surface-hover: rgba(255, 255, 255, 0.06);
|
||||
|
||||
/* Borders */
|
||||
--border: rgba(255, 255, 255, 0.08);
|
||||
--border-hover: rgba(255, 255, 255, 0.15);
|
||||
--border-focus: rgba(139, 92, 246, 0.5);
|
||||
|
||||
/* Text */
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: rgba(255, 255, 255, 0.6);
|
||||
--text-muted: rgba(255, 255, 255, 0.4);
|
||||
|
||||
/* Purple accent */
|
||||
--purple-light: #A855F7;
|
||||
--purple-main: #8B5CF6;
|
||||
--purple-dark: #7C3AED;
|
||||
--purple-deeper: #6D28D9;
|
||||
--gradient-cta: linear-gradient(135deg, #A855F7 0%, #7C3AED 50%, #6D28D9 100%);
|
||||
--glow: linear-gradient(135deg, #8B5CF6, #7C3AED);
|
||||
|
||||
/* Semantic */
|
||||
--warning: #f59e0b;
|
||||
--success: #10b981;
|
||||
--error: #E63946;
|
||||
|
||||
/* Typography */
|
||||
--font-display: 'Outfit', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
--font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
|
||||
/* Spacing */
|
||||
--radius-card: 20px;
|
||||
--radius-normal: 16px;
|
||||
--radius-button: 12px;
|
||||
--radius-tag: 8px;
|
||||
}
|
||||
|
||||
/* ========== GLOBAL RESET ========== */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
font-weight: 400;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ========== LOGIN PAGE LAYOUT ========== */
|
||||
html.login-pf,
|
||||
.login-pf body {
|
||||
background: var(--bg-primary) !important;
|
||||
min-height: 100vh !important;
|
||||
min-height: 100dvh !important; /* Dynamic viewport height for mobile */
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#kc-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#kc-header-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Force perfect centering */
|
||||
.login-pf .container-fluid,
|
||||
.login-pf .container {
|
||||
background: var(--bg-primary) !important;
|
||||
min-height: 100vh !important;
|
||||
min-height: 100dvh !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
padding: 20px !important;
|
||||
margin: 0 !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
/* Override any row/column that might break centering */
|
||||
.login-pf .row,
|
||||
.login-pf .col-sm-12,
|
||||
.login-pf .col-md-12,
|
||||
.login-pf [class*="col-"] {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
/* ========== LOGIN CARD ========== */
|
||||
#kc-form-wrapper,
|
||||
#kc-content,
|
||||
#kc-content-wrapper {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.card-pf {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
align-items: center !important;
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
max-width: 420px;
|
||||
width: 100%;
|
||||
margin: 0 auto !important;
|
||||
padding: 20px;
|
||||
flex-shrink: 0;
|
||||
/* Prevent overflow on small screens */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#kc-form,
|
||||
#kc-register-form,
|
||||
#kc-reset-password-form {
|
||||
background: var(--surface);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-card);
|
||||
padding: 48px 40px;
|
||||
max-width: 420px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* ========== LOGO / HEADER - Inside card ========== */
|
||||
.card-pf::before {
|
||||
content: 'didi';
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-family: var(--font-display);
|
||||
font-size: 48px;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.04em;
|
||||
color: #ffffff;
|
||||
margin-bottom: 4px;
|
||||
order: -2;
|
||||
}
|
||||
|
||||
.card-pf::after {
|
||||
content: 'Misinformation Detection Platform';
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-family: var(--font-body);
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 24px;
|
||||
letter-spacing: 0.5px;
|
||||
order: -1;
|
||||
}
|
||||
|
||||
/* Hide header outside modal */
|
||||
#kc-form-wrapper::before,
|
||||
#kc-form-wrapper::after,
|
||||
.login-pf-header,
|
||||
#kc-header,
|
||||
#kc-header-wrapper {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* ========== FORM TITLES ========== */
|
||||
#kc-page-title,
|
||||
.kc-page-title,
|
||||
h1#kc-page-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
text-align: center;
|
||||
margin: 0 0 24px 0;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* ========== FORM GROUPS ========== */
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* ========== LABELS ========== */
|
||||
label,
|
||||
.control-label,
|
||||
#kc-form-options label {
|
||||
display: block;
|
||||
font-family: var(--font-body);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* ========== INPUT FIELDS ========== */
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="tel"],
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 14px 16px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 15px;
|
||||
color: var(--text-primary);
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-button);
|
||||
outline: none;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
input[type="text"]:hover,
|
||||
input[type="password"]:hover,
|
||||
input[type="email"]:hover,
|
||||
input[type="tel"]:hover,
|
||||
.form-control:hover {
|
||||
border-color: var(--border-hover);
|
||||
background: var(--surface-hover);
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="email"]:focus,
|
||||
input[type="tel"]:focus,
|
||||
.form-control:focus {
|
||||
border-color: var(--border-focus);
|
||||
background: var(--surface);
|
||||
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15);
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
.form-control::placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ========== PRIMARY BUTTON ========== */
|
||||
input[type="submit"],
|
||||
button[type="submit"],
|
||||
.btn-primary,
|
||||
#kc-login,
|
||||
#kc-form-buttons input[type="submit"] {
|
||||
width: 100%;
|
||||
padding: 16px 32px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
background: var(--gradient-cta);
|
||||
border: none;
|
||||
border-radius: var(--radius-button);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
input[type="submit"]:hover,
|
||||
button[type="submit"]:hover,
|
||||
.btn-primary:hover,
|
||||
#kc-login:hover {
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 0 30px rgba(139, 92, 246, 0.4);
|
||||
}
|
||||
|
||||
input[type="submit"]:active,
|
||||
button[type="submit"]:active,
|
||||
.btn-primary:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* Spacing between Submit and Cancel buttons */
|
||||
#kc-form-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* ========== SECONDARY / LINK BUTTONS ========== */
|
||||
.btn-default,
|
||||
a.btn {
|
||||
display: inline-block;
|
||||
padding: 12px 24px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-hover);
|
||||
border-radius: var(--radius-button);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-default:hover,
|
||||
a.btn:hover {
|
||||
color: var(--text-primary);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
background: var(--surface-hover);
|
||||
}
|
||||
|
||||
/* ========== LINKS ========== */
|
||||
a,
|
||||
#kc-registration a,
|
||||
#kc-form-options a,
|
||||
.kc-form-options a {
|
||||
color: var(--purple-light);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--purple-main);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* ========== FORM OPTIONS (Remember me, Forgot password) ========== */
|
||||
/* Both #kc-form-options and the Forgot Password div are children of .login-pf-settings.
|
||||
We make the parent a flex row so they sit side by side. */
|
||||
.login-pf-settings {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
flex-wrap: nowrap !important;
|
||||
align-items: center !important;
|
||||
justify-content: space-between !important;
|
||||
}
|
||||
|
||||
#kc-form-options {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
/* Forgot Password - the anonymous div right after #kc-form-options */
|
||||
#kc-form-options + div {
|
||||
flex: 0 0 auto;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Back link at top of reset password form */
|
||||
.back-link {
|
||||
margin-bottom: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.back-link a {
|
||||
color: var(--purple-light);
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.back-link a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Mobile responsive */
|
||||
@media (max-width: 480px) {
|
||||
.back-link {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
#kc-form-options span,
|
||||
#kc-form-options .checkbox {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* ========== CHECKBOX ========== */
|
||||
input[type="checkbox"] {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
accent-color: var(--purple-main);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Fix Remember-me alignment: cancel inherited block label + Patternfly padding,
|
||||
make label a flex row so the box and the text sit on the same baseline.
|
||||
Patternfly default uses `position: absolute` on the checkbox + `padding-left`
|
||||
on the label — both must be reset for flex layout to work. */
|
||||
#kc-form-options .checkbox {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
#kc-form-options .checkbox label {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
#kc-form-options .checkbox input[type="checkbox"] {
|
||||
position: static !important; /* override Patternfly position: absolute */
|
||||
margin: 0 !important; /* override Patternfly negative margin-left */
|
||||
flex-shrink: 0;
|
||||
float: none !important; /* in case Patternfly floats it */
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* ========== REGISTRATION LINK ========== */
|
||||
#kc-registration {
|
||||
text-align: center;
|
||||
margin-top: 24px;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
#kc-registration span {
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* ========== INFO / MESSAGES ========== */
|
||||
#kc-info,
|
||||
.kc-feedback-text,
|
||||
#kc-info-wrapper {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#kc-info-message,
|
||||
.kc-feedback-text {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ========== ALERTS ========== */
|
||||
.alert {
|
||||
padding: 14px 18px;
|
||||
border-radius: var(--radius-button);
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.alert-error,
|
||||
.alert-danger {
|
||||
background: rgba(230, 57, 70, 0.1);
|
||||
border: 1px solid rgba(230, 57, 70, 0.3);
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
border: 1px solid rgba(245, 158, 11, 0.3);
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background: rgba(139, 92, 246, 0.1);
|
||||
border: 1px solid rgba(139, 92, 246, 0.3);
|
||||
color: var(--purple-light);
|
||||
}
|
||||
|
||||
/* ========== SOCIAL PROVIDERS ========== */
|
||||
#kc-social-providers {
|
||||
margin-top: 24px;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
#kc-social-providers h4,
|
||||
#kc-social-providers .kc-social-title {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
#kc-social-providers ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#kc-social-providers li a,
|
||||
.kc-social-provider-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
padding: 14px 20px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-button);
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
#kc-social-providers li a:hover,
|
||||
.kc-social-provider-link:hover {
|
||||
background: var(--surface-hover);
|
||||
border-color: var(--border-hover);
|
||||
}
|
||||
|
||||
/* ========== TERMS / RECAPTCHA ========== */
|
||||
#kc-terms-text {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
line-height: 1.6;
|
||||
text-align: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
/* ========== PASSWORD VISIBILITY TOGGLE ========== */
|
||||
.kc-form-password-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.kc-form-password-container button {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.kc-form-password-container button:hover {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* ========== RESPONSIVE ========== */
|
||||
@media (max-width: 480px) {
|
||||
#kc-form,
|
||||
#kc-register-form,
|
||||
#kc-reset-password-form {
|
||||
padding: 28px 28px;
|
||||
margin: 0 auto;
|
||||
border-radius: var(--radius-normal);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.card-pf::before {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
input[type="submit"],
|
||||
button[type="submit"],
|
||||
.btn-primary {
|
||||
padding: 14px 24px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== HIDE UNNECESSARY ELEMENTS ========== */
|
||||
#kc-locale-wrapper,
|
||||
.kc-locale-dropdown,
|
||||
.pf-c-alert__icon,
|
||||
#kc-header-wrapper {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Hide PatternFly error icons (red circles) */
|
||||
.pf-c-form-control__icon,
|
||||
.pf-c-form-control__utilities,
|
||||
.pf-c-form__helper-text,
|
||||
input[aria-invalid="true"] + .pf-c-form-control__utilities,
|
||||
.kc-feedback-text::before,
|
||||
span[class*="error"]::before,
|
||||
span[class*="Error"]::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Simple red border for invalid inputs */
|
||||
input[aria-invalid="true"],
|
||||
input.pf-m-error,
|
||||
.pf-c-form-control.pf-m-error {
|
||||
border-color: var(--error) !important;
|
||||
background-image: none !important;
|
||||
background: var(--surface) !important;
|
||||
}
|
||||
|
||||
/* ========== FOOTER ========== */
|
||||
#kc-content::after {
|
||||
content: '© 2025 didi';
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
/* ========== BACK TO LOGIN LINK ========== */
|
||||
#kc-info {
|
||||
order: 100;
|
||||
margin-top: 24px !important;
|
||||
}
|
||||
|
||||
#kc-info-wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ========== PLACEHOLDERS ========== */
|
||||
input#username::placeholder,
|
||||
input#password::placeholder,
|
||||
input#email::placeholder,
|
||||
input#firstName::placeholder,
|
||||
input#lastName::placeholder,
|
||||
input#password-new::placeholder,
|
||||
input#password-confirm::placeholder {
|
||||
color: var(--text-muted);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Add placeholder text via CSS for inputs that don't have it */
|
||||
input#username:placeholder-shown::placeholder { content: 'Enter your email'; }
|
||||
input#password:placeholder-shown::placeholder { content: 'Enter your password'; }
|
||||
|
||||
/* ========== FIX FORM LAYOUT ========== */
|
||||
#kc-form-login,
|
||||
#kc-register-form,
|
||||
#kc-reset-password-form {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
/* Form groups (inputs) - first */
|
||||
.form-group {
|
||||
order: 1 !important;
|
||||
}
|
||||
|
||||
/* Remember me / Forgot password settings row - between inputs and button */
|
||||
.login-pf-settings {
|
||||
order: 2 !important;
|
||||
}
|
||||
|
||||
/* Submit button - last */
|
||||
#kc-form-buttons {
|
||||
order: 3 !important;
|
||||
}
|
||||
|
||||
|
||||
/* ========== KC-INFO SECTION ========== */
|
||||
#kc-info {
|
||||
text-align: center !important;
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
|
||||
#kc-info-wrapper {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
#kc-info a {
|
||||
color: var(--purple-light) !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
/* ========== EMAIL VERIFICATION PAGE ========== */
|
||||
#kc-content,
|
||||
#kc-content-wrapper {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
/* Purple box for main instruction */
|
||||
.verify-email-box {
|
||||
background: rgba(139, 92, 246, 0.1) !important;
|
||||
border: 1px solid rgba(139, 92, 246, 0.3) !important;
|
||||
border-radius: 12px !important;
|
||||
padding: 28px !important;
|
||||
margin: 0 0 24px 0 !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.verify-email-box .instruction {
|
||||
text-align: center !important;
|
||||
margin: 0 !important;
|
||||
color: var(--text-primary) !important;
|
||||
}
|
||||
|
||||
.verify-email-box .instruction.auto-check {
|
||||
margin-top: 20px !important;
|
||||
font-size: 13px !important;
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
.instruction {
|
||||
text-align: center !important;
|
||||
font-size: 15px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
/* Haven't received section - also in purple box */
|
||||
#kc-info .instruction {
|
||||
margin: 0 !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
/* Loader animation for auto-check */
|
||||
.loader {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 2px solid rgba(139, 92, 246, 0.3);
|
||||
border-top-color: var(--purple-main);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-left: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* ========== FALLBACK CENTERING (for older browsers) ========== */
|
||||
@supports not (min-height: 100dvh) {
|
||||
.login-pf .container-fluid,
|
||||
.login-pf .container {
|
||||
min-height: 100vh !important;
|
||||
min-height: calc(var(--vh, 1vh) * 100) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== MOBILE SPECIFIC FIXES ========== */
|
||||
@media screen and (max-width: 768px) {
|
||||
.login-pf .container-fluid,
|
||||
.login-pf .container {
|
||||
padding: 12px !important;
|
||||
min-height: 100vh !important;
|
||||
min-height: 100dvh !important;
|
||||
}
|
||||
|
||||
.card-pf {
|
||||
max-width: 100%;
|
||||
padding: 8px;
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.card-pf::before {
|
||||
font-size: 40px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.card-pf::after {
|
||||
font-size: 13px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Prevent horizontal overflow */
|
||||
#kc-form,
|
||||
#kc-register-form {
|
||||
max-width: calc(100vw - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== iOS SAFARI SPECIFIC ========== */
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
.login-pf .container-fluid,
|
||||
.login-pf .container {
|
||||
min-height: -webkit-fill-available !important;
|
||||
}
|
||||
|
||||
/* Fix iOS input zoom */
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
.form-control {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== PatternFly v4 input-group fix (Keycloak 26) ========== */
|
||||
/* Password input wrapped in .pf-c-input-group for eye-icon toggle.
|
||||
PF4 specificity overrides our base input rules — force theme. */
|
||||
.pf-c-input-group {
|
||||
background: transparent !important;
|
||||
border-radius: var(--radius-button) !important;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border) !important;
|
||||
}
|
||||
.pf-c-input-group:hover {
|
||||
border-color: var(--border-hover) !important;
|
||||
}
|
||||
.pf-c-input-group:focus-within {
|
||||
border-color: var(--border-focus) !important;
|
||||
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15) !important;
|
||||
}
|
||||
.pf-c-input-group .pf-c-form-control,
|
||||
.pf-c-input-group input[type="password"],
|
||||
.pf-c-input-group input[type="text"],
|
||||
.pf-c-input-group input[type="email"] {
|
||||
background: var(--surface) !important;
|
||||
color: var(--text-primary) !important;
|
||||
border: none !important;
|
||||
border-radius: 0 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.pf-c-input-group .pf-c-button.pf-m-control {
|
||||
background: var(--surface) !important;
|
||||
color: var(--text-primary) !important;
|
||||
border: none !important;
|
||||
padding: 0 16px !important;
|
||||
}
|
||||
.pf-c-input-group .pf-c-button.pf-m-control:hover {
|
||||
background: var(--surface-hover) !important;
|
||||
color: var(--accent) !important;
|
||||
}
|
||||
.pf-c-input-group .pf-c-button.pf-m-control .fa {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Eye-icon button: kill all PF4 borders/shadows */
|
||||
.pf-c-input-group .pf-c-button,
|
||||
.pf-c-input-group .pf-c-button.pf-m-control {
|
||||
border: 0 !important;
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.04) !important;
|
||||
outline: 0 !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.pf-c-input-group .pf-c-button:focus,
|
||||
.pf-c-input-group .pf-c-button:hover {
|
||||
outline: 0 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Force ALL borders off on eye button (override previous border-left) */
|
||||
.pf-c-input-group > .pf-c-button,
|
||||
.pf-c-input-group > .pf-c-button.pf-m-control {
|
||||
border: 0 !important;
|
||||
border-left: 0 !important;
|
||||
border-right: 0 !important;
|
||||
border-top: 0 !important;
|
||||
border-bottom: 0 !important;
|
||||
outline: 0 !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0 !important;
|
||||
background: var(--surface) !important;
|
||||
--pf-c-button--BorderColor: transparent !important;
|
||||
--pf-c-button--BorderWidth: 0 !important;
|
||||
--pf-c-button--m-control--BorderBottomColor: transparent !important;
|
||||
}
|
||||
|
||||
/* THE actual chenar comes from ::after pseudo-element (PF4 pattern) */
|
||||
.pf-c-input-group .pf-c-button::after,
|
||||
.pf-c-input-group .pf-c-button.pf-m-control::after,
|
||||
.pf-c-input-group .pf-c-button:after,
|
||||
.pf-c-input-group .pf-c-button.pf-m-control:after {
|
||||
display: none !important;
|
||||
border: 0 !important;
|
||||
content: none !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// Add placeholders to Keycloak forms
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Common fields
|
||||
var username = document.getElementById('username');
|
||||
if (username) username.placeholder = 'Enter your email';
|
||||
|
||||
var email = document.getElementById('email');
|
||||
if (email) email.placeholder = 'Enter your email';
|
||||
|
||||
// Detect register page by presence of password-confirm
|
||||
var passwordConfirm = document.getElementById('password-confirm');
|
||||
|
||||
var password = document.getElementById('password');
|
||||
if (password) {
|
||||
password.placeholder = passwordConfirm ? 'Create a password' : 'Enter your password';
|
||||
}
|
||||
|
||||
if (passwordConfirm) {
|
||||
passwordConfirm.placeholder = 'Confirm password';
|
||||
}
|
||||
|
||||
// Register-only fields
|
||||
var firstName = document.getElementById('firstName');
|
||||
if (firstName) firstName.placeholder = 'First name';
|
||||
|
||||
var lastName = document.getElementById('lastName');
|
||||
if (lastName) lastName.placeholder = 'Last name';
|
||||
});
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Inherit from keycloak to get templates
|
||||
parent=keycloak
|
||||
|
||||
# Override styles
|
||||
styles=css/login.css
|
||||
|
||||
# Scripts
|
||||
scripts=js/placeholders.js
|
||||
|
||||
# Messages
|
||||
displayName=DIDI - Misinformation Detection Platform
|
||||
displayNameHtml=<strong>DIDI</strong> - Detect. Investigate. Decide. Inform.
|
||||
Loading…
Add table
Add a link
Reference in a new issue