Files
breadcrumb-the-shire/web/js/components/app-password-hints.js
fs 25370a1a55 add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00

88 lines
3.2 KiB
JavaScript

import { warnOnce } from '../core/app-dom.js';
const rules = {
min: (value, min) => value.length >= min,
upper: (value) => /[A-Z]/.test(value),
lower: (value) => /[a-z]/.test(value),
number: (value) => /\d/.test(value),
symbol: (value) => /[^a-zA-Z0-9]/.test(value),
email: (value, _min, email) => {
if (!email) {return true;}
return !value.toLowerCase().includes(email.toLowerCase());
},
match: (_value, _min, _email, confirm) => {
if (!confirm) {return false;}
return true;
}
};
export function initPasswordHints() {
const containers = document.querySelectorAll('[data-password-hints]');
if (!containers.length) {return;}
containers.forEach((container) => {
const passwordSelector = container.dataset.passwordInput;
const confirmSelector = container.dataset.confirmInput;
const emailSelector = container.dataset.emailInput;
const passwordInput = passwordSelector ? document.querySelector(passwordSelector) : null;
const confirmInput = confirmSelector ? document.querySelector(confirmSelector) : null;
const emailInput = emailSelector ? document.querySelector(emailSelector) : null;
if (passwordSelector && !passwordInput) {
warnOnce('UI_EL_MISSING', `Missing password input: ${passwordSelector}`, { module: 'password-hints' });
}
if (confirmSelector && !confirmInput) {
warnOnce('UI_EL_MISSING', `Missing confirm input: ${confirmSelector}`, { module: 'password-hints' });
}
if (emailSelector && !emailInput) {
warnOnce('UI_EL_MISSING', `Missing email input: ${emailSelector}`, { module: 'password-hints' });
}
const minLength = Number.parseInt(container.dataset.minLength || '12', 10);
const items = container.querySelectorAll('[data-rule]');
const setState = (item, state) => {
item.classList.remove('is-valid', 'is-invalid');
if (state === 'valid') {item.classList.add('is-valid');}
if (state === 'invalid') {item.classList.add('is-invalid');}
};
const evaluate = () => {
const password = passwordInput?.value ?? '';
const confirm = confirmInput?.value ?? '';
const email = emailInput?.value ?? '';
const hasValue = password.length > 0 || confirm.length > 0;
items.forEach((item) => {
const rule = item.dataset.rule;
if (!rule) {return;}
if (!hasValue) {
setState(item, 'neutral');
return;
}
if (rule === 'match') {
if (password === '' || confirm === '') {
setState(item, 'neutral');
return;
}
setState(item, password === confirm ? 'valid' : 'invalid');
return;
}
const check = rules[rule];
if (!check) {return;}
const ok = check(password, minLength, email, confirm);
setState(item, ok ? 'valid' : 'invalid');
});
};
if (passwordInput) {passwordInput.addEventListener('input', evaluate);}
if (confirmInput) {confirmInput.addEventListener('input', evaluate);}
if (emailInput) {emailInput.addEventListener('input', evaluate);}
evaluate();
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initPasswordHints);
} else {
initPasswordHints();
}