Pasek siły hasła oraz podpowiedzi do stworzenia silnego hasła
This commit is contained in:
@@ -5,6 +5,30 @@
|
|||||||
<title>PassMetric</title>
|
<title>PassMetric</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" th:href="@{/css/password.css}">
|
<link rel="stylesheet" th:href="@{/css/password.css}">
|
||||||
|
<style>
|
||||||
|
/* Pasek postępu siły hasła */
|
||||||
|
.strength-indicator {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: min(92vw, 480px);
|
||||||
|
margin: 10px auto 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.strength-dot {
|
||||||
|
flex: 1;
|
||||||
|
height: 8px;
|
||||||
|
margin: 0 3px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #e5e7eb; /* jasnoszary */
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.strength-dot.active-1 { background-color: #dc2626; } /* bardzo słabe */
|
||||||
|
.strength-dot.active-2 { background-color: #f97316; } /* słabe */
|
||||||
|
.strength-dot.active-3 { background-color: #facc15; } /* średnie */
|
||||||
|
.strength-dot.active-4 { background-color: #22c55e; } /* silne */
|
||||||
|
.strength-dot.active-5 { background-color: #16a34a; } /* bardzo silne */
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Sprawdź siłę hasła</h1>
|
<h1>Sprawdź siłę hasła</h1>
|
||||||
@@ -14,18 +38,80 @@
|
|||||||
<input type="password" id="password" autocomplete="off">
|
<input type="password" id="password" autocomplete="off">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Pasek postępu -->
|
||||||
|
<div class="strength-indicator">
|
||||||
|
<div class="strength-dot" id="dot1"></div>
|
||||||
|
<div class="strength-dot" id="dot2"></div>
|
||||||
|
<div class="strength-dot" id="dot3"></div>
|
||||||
|
<div class="strength-dot" id="dot4"></div>
|
||||||
|
<div class="strength-dot" id="dot5"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p id="liveMessage" aria-live="polite"></p>
|
<p id="liveMessage" aria-live="polite"></p>
|
||||||
|
<p id="suggestion" style="text-align:center; color: var(--muted); font-size: 14px; margin-top: 6px;"></p>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const input = document.getElementById('password');
|
const input = document.getElementById('password');
|
||||||
const out = document.getElementById('liveMessage');
|
const out = document.getElementById('liveMessage');
|
||||||
|
const suggestion = document.getElementById('suggestion');
|
||||||
|
const dots = [
|
||||||
|
document.getElementById('dot1'),
|
||||||
|
document.getElementById('dot2'),
|
||||||
|
document.getElementById('dot3'),
|
||||||
|
document.getElementById('dot4'),
|
||||||
|
document.getElementById('dot5')
|
||||||
|
];
|
||||||
|
|
||||||
let t;
|
let t;
|
||||||
input.addEventListener('input', () => {
|
input.addEventListener('input', () => {
|
||||||
clearTimeout(t);
|
clearTimeout(t);
|
||||||
const val = input.value;
|
const val = input.value;
|
||||||
if (!val) { out.textContent = ''; return; }
|
if (!val) {
|
||||||
|
out.textContent = '';
|
||||||
|
suggestion.textContent = '';
|
||||||
|
dots.forEach(dot => dot.className = 'strength-dot');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ocena hasła (lokalna)
|
||||||
|
let score = 0;
|
||||||
|
const hasLower = /[a-z]/.test(val);
|
||||||
|
const hasUpper = /[A-Z]/.test(val);
|
||||||
|
const hasNumber = /[0-9]/.test(val);
|
||||||
|
const hasSymbol = /[@$!%*?&#]/.test(val);
|
||||||
|
const longEnough = val.length >= 8;
|
||||||
|
|
||||||
|
if (hasLower) score++;
|
||||||
|
if (hasUpper) score++;
|
||||||
|
if (hasNumber) score++;
|
||||||
|
if (hasSymbol) score++;
|
||||||
|
if (longEnough) score++;
|
||||||
|
|
||||||
|
// Aktualizacja kropek
|
||||||
|
dots.forEach((dot, i) => {
|
||||||
|
dot.className = 'strength-dot';
|
||||||
|
if (i < score) dot.classList.add(`active-${score}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stopniowe podpowiedzi
|
||||||
|
let tip = "";
|
||||||
|
if (!hasLower) {
|
||||||
|
tip = "Zacznij od dodania małych liter, np. a–z.";
|
||||||
|
} else if (hasLower && !hasUpper) {
|
||||||
|
tip = "Spróbuj dodać dużą literę, np. A–Z.";
|
||||||
|
} else if (hasLower && hasUpper && !hasNumber) {
|
||||||
|
tip = "Dodaj cyfrę, np. 3 lub 7.";
|
||||||
|
} else if (hasLower && hasUpper && hasNumber && !hasSymbol) {
|
||||||
|
tip = "Dodaj znak specjalny, np. !, @, # lub $.";
|
||||||
|
} else if (hasLower && hasUpper && hasNumber && hasSymbol && !longEnough) {
|
||||||
|
tip = "Hasło jest dobre — wydłuż je do co najmniej 8 znaków, by było silniejsze.";
|
||||||
|
} else {
|
||||||
|
tip = "Świetnie! Twoje hasło jest bardzo silne 💪";
|
||||||
|
}
|
||||||
|
|
||||||
|
suggestion.textContent = tip;
|
||||||
|
|
||||||
|
// Wysłanie do backendu (do oceny)
|
||||||
t = setTimeout(async () => {
|
t = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch('/api/password/strength', {
|
const resp = await fetch('/api/password/strength', {
|
||||||
|
|||||||
Reference in New Issue
Block a user