Echo / src /ui /AuthUI.js
mlmihjaz's picture
C:/Program Files/Git/admin route shows admin-only login (no register, no guest)
c036803
Raw
History Blame Contribute Delete
5.2 kB
/**
* AuthUI — wires the login/register form.
*/
import { Auth } from '../auth/Auth.js';
export class AuthUI {
constructor({ onEnter, adminOnly = false } = {}) {
this.onEnter = onEnter;
this.adminOnly = adminOnly;
this.mode = 'login';
this._bind();
if (this.adminOnly) this._applyAdminMode();
this.setMode('login');
}
/** Hide the Register tab, Guest button, and tighten the wording for /admin. */
_applyAdminMode() {
const tabReg = this.$('tabRegister');
if (tabReg) tabReg.style.display = 'none';
const tabLogin = this.$('tabLogin');
if (tabLogin) tabLogin.textContent = 'Admin sign-in';
const guestRow = document.querySelector('.auth-card .guest');
if (guestRow) guestRow.style.display = 'none';
// Tighten the auth card heading.
const h2 = document.querySelector('.auth-card h2');
if (h2) h2.textContent = 'ECHO · Admin';
const sub = this.$('authSub');
if (sub) sub.textContent = 'Restricted area. Admin credentials required.';
}
$(id) { return document.getElementById(id); }
_bind() {
this.$('tabLogin').addEventListener('click', () => this.setMode('login'));
this.$('tabRegister').addEventListener('click', () => this.setMode('register'));
// Form submit (Enter key + button[type=submit])
this.$('authForm').addEventListener('submit', e => this._submit(e));
// Belt-and-braces: also handle direct button click in case the form's submit event is swallowed
this.$('authBtn').addEventListener('click', e => { e.preventDefault(); this._submit(e); });
this.$('forgotBtn').addEventListener('click', () => this._forgot());
this.$('guestBtn').addEventListener('click', () => { Auth.logout().catch(()=>{});this.hide(); this.onEnter?.(true); });
this.$('signOutBtn').addEventListener('click', () => { Auth.logout().catch(()=>{});location.reload(); });
console.log('[Auth] UI bound successfully');
}
setMode(m) {
// In adminOnly mode, ignore register and force login.
if (this.adminOnly) m = 'login';
this.mode = m;
this.$('tabLogin').classList.toggle('active', m === 'login');
this.$('tabRegister').classList.toggle('active', m === 'register');
this.$('fNameRow').style.display = m === 'register' ? 'block' : 'none';
this.$('fPwd2Row').style.display = m === 'register' ? 'block' : 'none';
this.$('authBtn').textContent = m === 'login' ? 'Login' : 'Create account';
if (!this.adminOnly) {
this.$('authSub').textContent = m === 'login'
? 'Welcome back — sign in to resume.'
: 'Create an account — track stars, climb the leaderboard.';
}
this.$('forgotBtn').style.display = (m === 'login' && !this.adminOnly) ? 'inline' : 'none';
this.$('fErr').textContent = '';
}
async _submit(e) {
if (e?.preventDefault) e.preventDefault();
const email = this.$('fEmail').value;
const pwd = this.$('fPwd').value;
const pwd2 = this.$('fPwd2').value;
const name = this.$('fName').value;
console.log(`[Auth] ${this.mode} submit: email="${email}" pwd.len=${pwd.length}`);
if (!email) {
this.$('fErr').style.color = 'var(--coral)';
this.$('fErr').textContent = 'Email is required.';
return;
}
if (!pwd) {
this.$('fErr').style.color = 'var(--coral)';
this.$('fErr').textContent = 'Password is required.';
return;
}
try {
let user;
if (this.mode === 'register') {
if (this.adminOnly) throw new Error('Registration is closed on /admin');
if (pwd !== pwd2) throw new Error('Passwords do not match');
user = await Auth.register(email, pwd, name);
console.log('[Auth] registered:', user.email);
} else {
user = await Auth.login(email, pwd);
console.log('[Auth] logged in:', user.email);
}
// /admin route: reject non-admin logins immediately.
if (this.adminOnly && !user.isAdmin) {
await Auth.logout().catch(() => {});
throw new Error('Admin access only.');
}
this.$('fErr').style.color = 'var(--spring)';
this.$('fErr').textContent = `✓ Welcome, ${user.name}!`;
setTimeout(() => {
this.hide();
this.onEnter?.(false);
}, 350);
} catch (err) {
console.warn('[Auth] error:', err.message);
this.$('fErr').style.color = 'var(--coral)';
this.$('fErr').textContent = err.message;
}
}
_forgot() {
const email = this.$('fEmail').value.trim();
if (!email) { this.$('fErr').textContent = 'Type your email above, then click Forgot password.'; return; }
const np = prompt(`Enter a new password (6+ chars) for ${email}:`);
if (!np) return;
try {
Auth.resetPwd(email, np);
this.$('fErr').style.color = 'var(--spring)';
this.$('fErr').textContent = 'Password reset — login with the new one.';
} catch (err) {
this.$('fErr').style.color = 'var(--coral)';
this.$('fErr').textContent = err.message;
}
}
show() { this.$('authScreen').classList.remove('hidden'); this.$('appWrap').style.display = 'none'; }
hide() { this.$('authScreen').classList.add('hidden'); this.$('appWrap').style.display = ''; }
}