import { useState, useCallback } from 'react' import { useNavigate } from 'react-router-dom' import './AuthPage.css' import Toast from '../components/Toast' import { useToast } from '../hooks/useToast' import { apiCall } from '../utils/api' // ── PASSWORD STRENGTH ───────────────────────────────────────────────────── function PasswordStrength({ value }) { if (!value) return null let score = 0 if (value.length >= 8) score++ if (/[A-Z]/.test(value)) score++ if (/[0-9]/.test(value)) score++ if (/[^A-Za-z0-9]/.test(value)) score++ const colors = ['#e06060', '#e0a060', '#d4b800', '#1c8a5f'] const labels = ['Sangat Lemah', 'Lemah', 'Sedang', 'Kuat'] const color = score > 0 ? colors[score - 1] : 'var(--txt-3)' return (
{[0,1,2,3].map(i => ( ))}
{score > 0 &&
{labels[score - 1]}
}
) } // ── EYE BUTTON ──────────────────────────────────────────────────────────── function EyeButton({ show, onToggle }) { return ( ) } function BackArrow() { return ( ) } // ── LANDING ─────────────────────────────────────────────────────────────── function LandingScreen({ active, onEnter }) { return (
Sistem Informasi Aktif
SIPKEM
Sistem Peta Kemiskinan

Platform pemetaan terpadu untuk rumah ibadah dan{' '} rumah miskin di wilayah Pontianak — mendukung pendataan, monitoring, dan pembinaan sosial berbasis lokasi.

{[['🗺️','Peta Interaktif'],['🕌','Rumah Ibadah'],['🏠','Rumah Miskin'],['📊','Statistik']].map(([icon, label]) => (
{icon} {label}
))}
) } // ── ROLE SELECTION ──────────────────────────────────────────────────────── function RoleScreen({ active, selectedRole, onSelect, onBack, onNext }) { return (
Langkah 1 dari 2
Pilih Peran Anda

Akses sistem disesuaikan berdasarkan peran yang Anda pilih.

{[ { key: 'pengguna', label: 'Pengguna', desc: 'Melihat peta & data yang tersedia', icon: ( ), }, { key: 'pengurus', label: 'Pengurus', desc: 'Kelola & edit seluruh data sistem', icon: ( ), }, ].map(({ key, label, desc, icon }) => (
onSelect(key)} >
{icon}
{label}
{desc}
))}
) } // ── LOGIN ───────────────────────────────────────────────────────────────── function LoginScreen({ active, role, onBack, onSuccess, showToast, onGoRegister }) { const [email, setEmail] = useState('') const [pass, setPass] = useState('') const [showPass, setShowPass] = useState(false) const [loading, setLoading] = useState(false) const [errors, setErrors] = useState({}) const isValidEmail = v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) function validate() { const errs = {} if (!isValidEmail(email)) errs.email = 'Format email tidak valid.' if (pass.length < 8) errs.pass = 'Kata sandi minimal 8 karakter.' return errs } async function handleLogin() { const errs = validate() if (Object.keys(errs).length) { setErrors(errs); return } setErrors({}) setLoading(true) const res = await apiCall('login', { email, password: pass, role }) setLoading(false) if (!res.ok) { // Map error to the right field const msg = res.error || 'Login gagal.' if (msg.toLowerCase().includes('email') || msg.toLowerCase().includes('kata sandi') || msg.toLowerCase().includes('salah')) { setErrors({ api: msg }) } else if (msg.toLowerCase().includes('pengurus') || msg.toLowerCase().includes('pengguna') || msg.toLowerCase().includes('akun')) { setErrors({ role: msg }) } else { setErrors({ api: msg }) } return } onSuccess(res.user.role, res.user) } // Pengurus: no register link shown const isPengurus = role === 'pengurus' return (
{isPengurus ? ( <> Pengurus ) : ( <> Pengguna )}
Masuk Akun
Selamat Datang

{isPengurus ? 'Masukkan kredensial akun Pengurus Anda.' : 'Masukkan email dan kata sandi Anda untuk melanjutkan.'}

{/* Role mismatch error banner */} {errors.role && (
{errors.role}
)}
{ setEmail(e.target.value); setErrors(p => ({ ...p, email: '', api: '' })) }} onKeyDown={e => e.key === 'Enter' && handleLogin()} autoComplete="email" /> {errors.email &&
{errors.email}
}
{ setPass(e.target.value); setErrors(p => ({ ...p, pass: '', api: '' })) }} onKeyDown={e => e.key === 'Enter' && handleLogin()} autoComplete="current-password" /> setShowPass(s => !s)} />
{errors.pass &&
{errors.pass}
} {errors.api &&
{errors.api}
}
{/* Pengurus: no register link */} {!isPengurus && (
Belum punya akun?
)} {isPengurus && (
Akun Pengurus hanya dapat dibuat oleh administrator sistem.
)}
) } // ── REGISTER (Pengguna only) ────────────────────────────────────────────── function RegisterScreen({ active, onBack, showToast }) { const [name, setName] = useState('') const [email, setEmail] = useState('') const [pass, setPass] = useState('') const [pass2, setPass2] = useState('') const [showP, setShowP] = useState(false) const [showP2, setShowP2] = useState(false) const [loading, setLoading] = useState(false) const [errors, setErrors] = useState({}) const isValidEmail = v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) function validate() { const errs = {} if (!name.trim()) errs.name = 'Nama tidak boleh kosong.' if (!isValidEmail(email)) errs.email = 'Format email tidak valid.' if (pass.length < 8) errs.pass = 'Kata sandi minimal 8 karakter.' if (pass !== pass2 || !pass2) errs.pass2 = 'Kata sandi tidak cocok.' return errs } async function handleRegister() { const errs = validate() if (Object.keys(errs).length) { setErrors(errs); return } setErrors({}) setLoading(true) const res = await apiCall('register', { nama: name.trim(), email, password: pass }) setLoading(false) if (!res.ok) { const msg = res.error || 'Pendaftaran gagal.' if (msg.toLowerCase().includes('email')) { setErrors({ email: msg }) } else { setErrors({ api: msg }) } return } showToast('✓ Akun berhasil dibuat — silakan masuk') // Reset form setName(''); setEmail(''); setPass(''); setPass2('') setTimeout(() => onBack(), 1200) } return (
Daftar Akun
Buat Akun Baru

Isi data di bawah untuk membuat akun Pengguna SIPKEM.

{errors.api && (
{errors.api}
)}
{ setName(e.target.value); setErrors(p => ({ ...p, name: '' })) }} autoComplete="name" /> {errors.name &&
{errors.name}
}
{ setEmail(e.target.value); setErrors(p => ({ ...p, email: '' })) }} autoComplete="email" /> {errors.email &&
{errors.email}
}
{ setPass(e.target.value); setErrors(p => ({ ...p, pass: '' })) }} autoComplete="new-password" /> setShowP(s => !s)} />
{errors.pass &&
{errors.pass}
}
{ setPass2(e.target.value); setErrors(p => ({ ...p, pass2: '' })) }} onKeyDown={e => e.key === 'Enter' && handleRegister()} autoComplete="new-password" /> setShowP2(s => !s)} />
{errors.pass2 &&
{errors.pass2}
}
Sudah punya akun?
) } // ── MAIN ────────────────────────────────────────────────────────────────── export default function AuthPage() { const navigate = useNavigate() const [screen, setScreen] = useState('landing') const [role, setRole] = useState(null) const { message, visible, showToast } = useToast() const goTo = useCallback((s) => setScreen(s), []) return (
{screen !== 'landing' &&
} goTo('role')} /> goTo('landing')} onNext={() => { if (role) goTo('login') }} /> goTo('role')} onSuccess={(r, user) => navigate('/map', { state: { role: r, user } })} showToast={showToast} onGoRegister={() => goTo('register')} /> {/* Register only accessible if role is pengguna (or role not yet set) */} {role !== 'pengurus' && ( goTo('login')} showToast={showToast} /> )}
) }