const actionConfig = { login: { loadingText: "Logging in...", genericError: "Login failed. Please check your email or password." }, register: { loadingText: "Creating account...", genericError: "Registration failed. Please try again." }, reset: { loadingText: "Sending verification code...", genericError: "Could not finish password reset. Please try again." } }; const PUBLIC_TEST_ENDPOINT = "https://jsonplaceholder.typicode.com/posts"; const USERS_KEY = "coastal_users"; const SESSION_KEY = "coastal_session"; const RESET_KEY = "coastal_reset_requests"; async function realRequest(action, payload) { const response = await fetch(PUBLIC_TEST_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json; charset=UTF-8" }, body: JSON.stringify({ action, payload, requestedAt: new Date().toISOString() }) }); if (!response.ok) { throw new Error(`Request failed with status ${response.status}`); } return response.json(); } function getUsers() { return JSON.parse(localStorage.getItem(USERS_KEY) || "[]"); } function saveUsers(users) { localStorage.setItem(USERS_KEY, JSON.stringify(users)); } function getResetRequests() { return JSON.parse(localStorage.getItem(RESET_KEY) || "{}"); } function saveResetRequests(data) { localStorage.setItem(RESET_KEY, JSON.stringify(data)); } function findUserByEmail(email) { return getUsers().find((user) => user.email === email); } function setFeedback(el, type, text) { el.className = "feedback"; if (type) { el.classList.add(type); } el.textContent = text; } function setLoading(button, isLoading, loadingText) { if (isLoading) { button.disabled = true; button.classList.add("is-loading"); button.dataset.originalText = button.textContent; button.innerHTML = `${loadingText}`; } else { button.disabled = false; button.classList.remove("is-loading"); button.textContent = button.dataset.originalText || button.textContent; } } function getFormPayload(form) { const formData = new FormData(form); return Object.fromEntries(formData.entries()); } function normalizeEmail(value) { return String(value || "").trim().toLowerCase(); } function createVerificationCode() { return Math.floor(100000 + Math.random() * 900000).toString(); } function getLoadingText(action, form, fallbackText) { if (action === "reset") { const stage = form.dataset.stage || "request"; return stage === "confirm" ? "Resetting password..." : "Sending verification code..."; } return fallbackText; } async function handleRegister(form, feedback) { const payload = getFormPayload(form); const name = String(payload.name || "").trim(); const email = normalizeEmail(payload.email); const password = String(payload.password || ""); const confirmPassword = String(payload.confirmPassword || ""); if (!name || !email || !password) { throw new Error("Please complete all required fields."); } if (password.length < 6) { throw new Error("Password must be at least 6 characters."); } if (password !== confirmPassword) { throw new Error("Passwords do not match."); } if (findUserByEmail(email)) { throw new Error("This email is already registered."); } await realRequest("register", { name, email }); const users = getUsers(); users.push({ name, email, password }); saveUsers(users); setFeedback(feedback, "success", "Account created. Redirecting to login..."); setTimeout(() => { window.location.href = "./index.html"; }, 900); } async function handleLogin(form, feedback) { const payload = getFormPayload(form); const email = normalizeEmail(payload.email); const password = String(payload.password || ""); if (!email || !password) { throw new Error("Please enter email and password."); } await realRequest("login", { email }); const user = findUserByEmail(email); if (!user || user.password !== password) { throw new Error("Invalid email or password."); } localStorage.setItem( SESSION_KEY, JSON.stringify({ email: user.email, name: user.name, loginAt: new Date().toISOString() }) ); setFeedback(feedback, "success", `Welcome back, ${user.name}. Login successful.`); } async function handleReset(form, feedback, button) { const payload = getFormPayload(form); const email = normalizeEmail(payload.email); const code = String(payload.code || "").trim(); const newPassword = String(payload.newPassword || ""); const confirmNewPassword = String(payload.confirmNewPassword || ""); const resetStep = form.querySelector("#reset-step"); const stage = form.dataset.stage || "request"; if (!email) { throw new Error("Please enter your email."); } if (!findUserByEmail(email)) { throw new Error("This email is not registered yet."); } if (stage === "request") { await realRequest("reset-request", { email }); const resetRequests = getResetRequests(); const codeValue = createVerificationCode(); resetRequests[email] = { code: codeValue, expiresAt: Date.now() + 10 * 60 * 1000 }; saveResetRequests(resetRequests); form.dataset.stage = "confirm"; if (resetStep) { resetStep.classList.remove("hidden"); } button.textContent = "Reset Password"; button.dataset.originalText = "Reset Password"; setFeedback( feedback, "success", `Verification code sent. Demo code: ${codeValue} (valid 10 min).` ); return; } if (!code || !newPassword || !confirmNewPassword) { throw new Error("Please complete code and new password fields."); } if (newPassword.length < 6) { throw new Error("New password must be at least 6 characters."); } if (newPassword !== confirmNewPassword) { throw new Error("New passwords do not match."); } const resetRequests = getResetRequests(); const request = resetRequests[email]; if (!request) { throw new Error("Please request a verification code first."); } if (Date.now() > request.expiresAt) { delete resetRequests[email]; saveResetRequests(resetRequests); throw new Error("Verification code expired. Please request a new one."); } if (request.code !== code) { throw new Error("Invalid verification code."); } await realRequest("reset-confirm", { email }); const users = getUsers(); const nextUsers = users.map((user) => user.email === email ? { ...user, password: newPassword } : user ); saveUsers(nextUsers); delete resetRequests[email]; saveResetRequests(resetRequests); form.reset(); form.dataset.stage = "request"; if (resetStep) { resetStep.classList.add("hidden"); } button.textContent = "Send Verification Code"; button.dataset.originalText = "Send Verification Code"; setFeedback( feedback, "success", "Password reset successful. Please login with your new password." ); } document.querySelectorAll(".auth-form").forEach((form) => { const action = form.dataset.action; const config = actionConfig[action]; const button = form.querySelector(".btn"); const feedback = form.querySelector(".feedback"); if (!config || !button || !feedback) { return; } if (action === "reset") { form.dataset.stage = "request"; } form.addEventListener("submit", async (event) => { event.preventDefault(); const loadingText = getLoadingText(action, form, config.loadingText); setFeedback(feedback, "info", "Connecting to server..."); setLoading(button, true, loadingText); try { if (action === "register") { await handleRegister(form, feedback); } else if (action === "login") { await handleLogin(form, feedback); } else if (action === "reset") { await handleReset(form, feedback, button); } } catch (error) { setFeedback(feedback, "error", error.message || config.genericError); } finally { setLoading(button, false, loadingText); } }); });