Skip to content

Commit 39b3ce4

Browse files
committed
cle
1 parent e294cde commit 39b3ce4

1 file changed

Lines changed: 189 additions & 0 deletions

File tree

‎frontend/pages/auth.html‎

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,75 @@
395395
color: #2c2c2c;
396396
font-weight: 600;
397397
}
398+
399+
/* Password Strength Styles */
400+
.password-strength-container {
401+
margin-top: 1rem;
402+
padding: 0 0.2rem;
403+
transition: all 0.3s ease;
404+
}
405+
406+
.password-strength-bar {
407+
height: 6px;
408+
background: rgba(255, 255, 255, 0.1);
409+
border-radius: 4px;
410+
overflow: hidden;
411+
margin-bottom: 0.75rem;
412+
box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);
413+
}
414+
415+
.password-strength-fill {
416+
height: 100%;
417+
width: 0%;
418+
transition: width 0.3s ease, background-color 0.3s ease;
419+
}
420+
421+
.password-criteria {
422+
list-style: none;
423+
padding: 0;
424+
margin: 0;
425+
display: grid;
426+
gap: 0.5rem;
427+
}
428+
429+
.password-criteria li {
430+
font-size: 0.85rem;
431+
color: var(--text-muted);
432+
display: flex;
433+
align-items: center;
434+
gap: 0.5rem;
435+
transition: color 0.2s ease;
436+
}
437+
438+
.password-criteria li i {
439+
font-size: 0.95rem;
440+
width: 16px;
441+
text-align: center;
442+
}
443+
444+
.criterion-met {
445+
color: #4ade80 !important;
446+
}
447+
.criterion-met i {
448+
color: #4ade80;
449+
}
450+
.criterion-unmet {
451+
color: #f87171 !important;
452+
}
453+
.criterion-unmet i {
454+
color: #f87171;
455+
}
456+
457+
.strength-weak { background-color: #f87171; box-shadow: 0 0 8px rgba(248,113,113,0.4); }
458+
.strength-fair { background-color: #fbbf24; box-shadow: 0 0 8px rgba(251,191,36,0.4); }
459+
.strength-good { background-color: #a3e635; box-shadow: 0 0 8px rgba(163,230,53,0.4); }
460+
.strength-strong { background-color: #4ade80; box-shadow: 0 0 8px rgba(74,222,128,0.4); }
461+
462+
@keyframes inputShake {
463+
0%, 100% { transform: translateX(0); }
464+
20%, 60% { transform: translateX(-5px); border-color: #f87171; }
465+
40%, 80% { transform: translateX(5px); border-color: #f87171; }
466+
}
398467
</style>
399468
<link rel="icon" href="../assets/images/biblioDrift_favicon.png" type="image/png">
400469
<!-- PWA -->
@@ -487,6 +556,18 @@ <h2 id="authTitle">Welcome Back</h2>
487556
value="demo123" required autocomplete="current-password">
488557
<i class="fa-solid fa-eye password-toggle-icon" id="togglePassword" title="Show Password"
489558
aria-label="Show password" role="button" tabindex="0"></i>
559+
560+
<!-- Client-side Password Strength Validation UI -->
561+
<div id="passwordStrengthContainer" style="display: none;" class="password-strength-container">
562+
<div class="password-strength-bar">
563+
<div class="password-strength-fill" id="passwordStrengthFill"></div>
564+
</div>
565+
<ul class="password-criteria" id="passwordCriteria">
566+
<li id="criterion-length" class="criterion-unmet"><i class="fa-solid fa-circle-xmark"></i> At least 8 characters</li>
567+
<li id="criterion-number" class="criterion-unmet"><i class="fa-solid fa-circle-xmark"></i> At least 1 number</li>
568+
<li id="criterion-special" class="criterion-unmet"><i class="fa-solid fa-circle-xmark"></i> At least 1 special character</li>
569+
</ul>
570+
</div>
490571
</div>
491572

492573
<!--
@@ -704,6 +785,13 @@ <h3 style="color: var(--text-main) !important;">Ambient Sanctuary</h3>
704785
googleAuthBtn.querySelector('span').textContent = 'Sign up with Google';
705786
toggleText.textContent = 'Already have an account? Sign in.';
706787
passwordInput.autocomplete = 'new-password';
788+
passwordInput.value = ''; // Reset password field on switch to register
789+
if (document.getElementById('passwordStrengthContainer')) {
790+
document.getElementById('passwordStrengthContainer').style.display = 'block';
791+
if (typeof updatePasswordStrengthMeter === 'function') {
792+
updatePasswordStrengthMeter();
793+
}
794+
}
707795
} else {
708796
authForm.dataset.mode = 'login';
709797
authTitle.textContent = 'Welcome Back';
@@ -712,6 +800,9 @@ <h3 style="color: var(--text-main) !important;">Ambient Sanctuary</h3>
712800
googleAuthBtn.querySelector('span').textContent = 'Sign in with Google';
713801
toggleText.textContent = 'No account? Create one.';
714802
passwordInput.autocomplete = 'current-password';
803+
if (document.getElementById('passwordStrengthContainer')) {
804+
document.getElementById('passwordStrengthContainer').style.display = 'none';
805+
}
715806
}
716807
showAuthPanel('signin');
717808
});
@@ -746,6 +837,104 @@ <h3 style="color: var(--text-main) !important;">Ambient Sanctuary</h3>
746837
});
747838
}
748839

840+
// ==============================================================================
841+
// PASSWORD STRENGTH VALIDATION LOGIC
842+
// ==============================================================================
843+
const passwordStrengthContainer = document.getElementById('passwordStrengthContainer');
844+
const passwordStrengthFill = document.getElementById('passwordStrengthFill');
845+
const criteriaNodes = {
846+
length: document.getElementById('criterion-length'),
847+
number: document.getElementById('criterion-number'),
848+
special: document.getElementById('criterion-special')
849+
};
850+
851+
function validatePasswordCriteria(password) {
852+
const hasLength = password.length >= 8;
853+
const hasNumber = /\d/.test(password);
854+
const hasSpecial = /[!@#$%^&*(),.?":{}|<>\-_\+=\/\[\]\\';`~]/.test(password);
855+
856+
return {
857+
length: hasLength,
858+
number: hasNumber,
859+
special: hasSpecial,
860+
score: (hasLength ? 1 : 0) + (hasNumber ? 1 : 0) + (hasSpecial ? 1 : 0)
861+
};
862+
}
863+
864+
function updateCriterionNode(node, isMet) {
865+
if (!node) return;
866+
const icon = node.querySelector('i');
867+
if (isMet) {
868+
node.classList.add('criterion-met');
869+
node.classList.remove('criterion-unmet');
870+
icon.className = 'fa-solid fa-circle-check';
871+
} else {
872+
node.classList.remove('criterion-met');
873+
node.classList.add('criterion-unmet');
874+
icon.className = 'fa-solid fa-circle-xmark';
875+
}
876+
}
877+
878+
function updatePasswordStrengthMeter() {
879+
if (!passwordInput || authForm.dataset.mode !== 'register') return;
880+
881+
const password = passwordInput.value;
882+
const validation = validatePasswordCriteria(password);
883+
884+
updateCriterionNode(criteriaNodes.length, validation.length);
885+
updateCriterionNode(criteriaNodes.number, validation.number);
886+
updateCriterionNode(criteriaNodes.special, validation.special);
887+
888+
let fillWidth = '0%';
889+
let fillClass = 'strength-weak';
890+
891+
if (password.length > 0) {
892+
if (validation.score === 1) {
893+
fillWidth = '33%';
894+
fillClass = 'strength-weak';
895+
} else if (validation.score === 2) {
896+
fillWidth = '66%';
897+
fillClass = 'strength-fair';
898+
} else if (validation.score === 3) {
899+
fillWidth = '100%';
900+
fillClass = password.length >= 12 ? 'strength-strong' : 'strength-good';
901+
}
902+
}
903+
904+
if (passwordStrengthFill) {
905+
passwordStrengthFill.style.width = fillWidth;
906+
passwordStrengthFill.className = `password-strength-fill ${fillClass}`;
907+
}
908+
}
909+
910+
if (passwordInput) {
911+
passwordInput.addEventListener('input', updatePasswordStrengthMeter);
912+
}
913+
914+
// Intercept form submission to enforce validation on register
915+
if (authForm) {
916+
const originalOnSubmit = authForm.onsubmit;
917+
authForm.onsubmit = function(event) {
918+
if (authForm.dataset.mode === 'register') {
919+
const validation = validatePasswordCriteria(passwordInput.value);
920+
if (validation.score < 3) {
921+
event.preventDefault();
922+
923+
passwordInput.style.animation = 'inputShake 0.4s ease-in-out 0s 1';
924+
setTimeout(() => {
925+
passwordInput.style.animation = 'none';
926+
}, 400);
927+
928+
passwordInput.focus();
929+
return false; // Stop submission
930+
}
931+
}
932+
if (originalOnSubmit) {
933+
return originalOnSubmit.call(this, event);
934+
}
935+
};
936+
}
937+
749938
const resetToken = new URLSearchParams(window.location.search).get('token');
750939
if (resetToken) {
751940
showAuthPanel('reset');

0 commit comments

Comments
 (0)