XENXOS / assets /JS /login.js
JSWAI's picture
Rename assets/js/login.js to assets/JS/login.js
216eca6 verified
Raw History Blame Contribute Delete
5.68 kB
/* ==========================================================================
XENEX GLASS OS — Login Screen Behavior
assets/JS/login.js
This file handles UI behavior only: password visibility and client-side
form validation. It does NOT authenticate anyone. There is no backend
yet, so submitting the form cannot and does not log a user in — it only
reports that sign-in isn't wired up yet. See the comment above
handleSubmit() for where the real API call goes once it exists.
========================================================================== */
document.addEventListener('DOMContentLoaded', () => {
initPasswordToggle();
initFormValidation();
});
/* ---- Password visibility toggle ---- */
function initPasswordToggle() {
const toggleButton = document.querySelector('[data-password-toggle]');
const passwordInput = document.getElementById('password');
if (!toggleButton || !passwordInput) return;
toggleButton.addEventListener('click', () => {
const isHidden = passwordInput.type === 'password';
passwordInput.type = isHidden ? 'text' : 'password';
toggleButton.setAttribute('aria-pressed', String(isHidden));
toggleButton.setAttribute('aria-label', isHidden ? 'Hide password' : 'Show password');
const toggleText = toggleButton.querySelector('.login-field__toggle-text');
if (toggleText) {
toggleText.textContent = isHidden ? 'Hide' : 'Show';
}
});
}
/* ---- Form validation + submit ---- */
function initFormValidation() {
const form = document.querySelector('.login-form');
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');
if (!form || !emailInput || !passwordInput) return;
const statusRegion = createStatusRegion(form);
form.addEventListener('submit', (event) => {
event.preventDefault();
clearFieldErrors(form);
setStatus(statusRegion, '', null);
const errors = validateFields(emailInput, passwordInput);
if (errors.length > 0) {
errors.forEach(({ input, message }) => showFieldError(input, message));
errors[0].input.focus();
return;
}
handleSubmit({ email: emailInput.value.trim() });
});
// Clear a field's error state as soon as the person edits it.
[emailInput, passwordInput].forEach((input) => {
input.addEventListener('input', () => clearFieldError(input));
});
}
function validateFields(emailInput, passwordInput) {
const errors = [];
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (emailInput.value.trim() === '') {
errors.push({ input: emailInput, message: 'Enter your email address.' });
} else if (!emailPattern.test(emailInput.value.trim())) {
errors.push({ input: emailInput, message: 'Enter a valid email address.' });
}
if (passwordInput.value === '') {
errors.push({ input: passwordInput, message: 'Enter your password.' });
}
return errors;
}
/*
* There is no backend yet. When one exists, this is where the real
* request goes, e.g.:
*
* const response = await fetch('/auth/login', {
* method: 'POST',
* headers: { 'Content-Type': 'application/json' },
* body: JSON.stringify({ email, password }),
* credentials: 'include' // secure HttpOnly session cookie, not a token in JS
* });
*
* Until that endpoint exists, do not simulate a response. Tell the person
* plainly that sign-in isn't connected yet.
*/
function handleSubmit({ email }) {
const form = document.querySelector('.login-form');
const statusRegion = form.querySelector('[data-login-status]');
const submitButton = document.querySelector('[data-login-submit]');
submitButton.disabled = true;
setStatus(
statusRegion,
"Sign-in isn't connected to a server yet. This form will work once the Xenex backend is live.",
'info'
);
console.info('[Xenex] Login form submitted with no backend configured.', { email });
submitButton.disabled = false;
}
/* ---- Field error helpers ---- */
function showFieldError(input, message) {
const field = input.closest('.login-field');
if (!field) return;
field.classList.add('login-field--error');
input.setAttribute('aria-invalid', 'true');
let errorEl = field.querySelector('.login-field__error');
if (!errorEl) {
errorEl = document.createElement('p');
errorEl.className = 'login-field__error';
errorEl.id = `${input.id}-error`;
field.appendChild(errorEl);
}
errorEl.textContent = message;
input.setAttribute('aria-describedby', errorEl.id);
}
function clearFieldError(input) {
const field = input.closest('.login-field');
if (!field) return;
field.classList.remove('login-field--error');
input.removeAttribute('aria-invalid');
input.removeAttribute('aria-describedby');
const errorEl = field.querySelector('.login-field__error');
if (errorEl) errorEl.remove();
}
function clearFieldErrors(form) {
form.querySelectorAll('.login-field__input').forEach(clearFieldError);
}
/* ---- Status region (created once, announced to screen readers) ---- */
function createStatusRegion(form) {
let region = form.querySelector('[data-login-status]');
if (region) return region;
region = document.createElement('p');
region.setAttribute('data-login-status', '');
region.className = 'login-status';
region.setAttribute('role', 'status');
region.setAttribute('aria-live', 'polite');
form.appendChild(region);
return region;
}
function setStatus(region, message, kind) {
if (!region) return;
region.textContent = message;
region.classList.toggle('login-status--info', kind === 'info');
region.classList.toggle('login-status--error', kind === 'error');
}