const debounce = (fn, wait = 350) => { let timeoutId return (...args) => { clearTimeout(timeoutId) timeoutId = setTimeout(() => fn(...args), wait) } } const setStatus = (target, message, tone = 'neutral') => { if (!target) return target.textContent = message || '' target.classList.remove('text-green-600', 'text-red-600', 'text-gray-500') if (tone === 'success') target.classList.add('text-green-600') else if (tone === 'error') target.classList.add('text-red-600') else target.classList.add('text-gray-500') } const initUsernameAvailability = () => { const fields = document.querySelectorAll('[data-username-field="true"]') fields.forEach((field) => { const url = field.getAttribute('data-availability-url') || '/api/username/availability' const statusId = field.getAttribute('data-availability-target') const statusEl = statusId ? document.getElementById(statusId) : null const check = debounce(async () => { const raw = String(field.value || '') const username = raw.trim().toLowerCase() if (!username) { setStatus(statusEl, '') return } setStatus(statusEl, 'Checking availability...') try { const response = await window.axios.get(url, { params: { username } }) const data = response?.data || {} if (data.available) { setStatus(statusEl, `Available: ${data.normalized || username}`, 'success') } else { setStatus(statusEl, `Taken: ${data.normalized || username}`, 'error') } } catch (error) { if (error?.response?.status === 422) { const message = error?.response?.data?.errors?.username?.[0] || 'Invalid username.' setStatus(statusEl, message, 'error') return } setStatus(statusEl, 'Could not check availability right now.', 'error') } }) field.addEventListener('input', check) }) } document.addEventListener('DOMContentLoaded', initUsernameAvailability)