// Profile / Account page — own info, password change, active sessions function ProfilePage() { const u = window.APP_USER || {}; const [name, setName] = useState(u.name || ''); const [savingName, setSavingName] = useState(false); const [nameMsg, setNameMsg] = useState(null); const [oldPw, setOldPw] = useState(''); const [newPw, setNewPw] = useState(''); const [newPw2, setNewPw2] = useState(''); const [savingPw, setSavingPw] = useState(false); const [pwMsg, setPwMsg] = useState(null); const [sessions, setSessions] = useState([]); const [loadingSessions, setLoadingSessions] = useState(true); useEffect(() => { apiFetch('/api/auth/sessions') .then(r => r.json()) .then(setSessions) .catch(() => {}) .finally(() => setLoadingSessions(false)); }, []); const saveName = async (e) => { e.preventDefault(); if (!name.trim()) return; setSavingName(true); setNameMsg(null); try { const r = await apiFetch('/api/auth/me', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: name.trim() }), }); if (!r.ok) throw new Error((await r.json()).detail?.message || 'שמירה נכשלה'); const data = await r.json(); window.APP_USER = data.user; setNameMsg({ kind: 'ok', text: 'נשמר ✓' }); } catch (err) { setNameMsg({ kind: 'err', text: err.message }); } finally { setSavingName(false); } }; const changePw = async (e) => { e.preventDefault(); if (newPw !== newPw2) { setPwMsg({ kind: 'err', text: 'הסיסמאות החדשות לא תואמות' }); return; } if (newPw.length < 10) { setPwMsg({ kind: 'err', text: 'הסיסמה חייבת לפחות 10 תווים' }); return; } setSavingPw(true); setPwMsg(null); try { const r = await apiFetch('/api/auth/change_password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ old_password: oldPw, new_password: newPw }), }); if (!r.ok) throw new Error((await r.json()).detail?.message || 'שינוי נכשל'); const data = await r.json(); setPwMsg({ kind: 'ok', text: data.other_sessions_revoked > 0 ? `הסיסמה הוחלפה. ${data.other_sessions_revoked} סשנים אחרים נותקו.` : 'הסיסמה הוחלפה ✓', }); setOldPw(''); setNewPw(''); setNewPw2(''); // Reload sessions apiFetch('/api/auth/sessions') .then(r => r.json()).then(setSessions).catch(() => {}); } catch (err) { setPwMsg({ kind: 'err', text: err.message }); } finally { setSavingPw(false); } }; const revokeSession = async (sessionPrefix) => { if (!confirm('לנתק את המכשיר הזה?')) return; try { const id = sessionPrefix.replace('…', ''); await apiFetch(`/api/auth/sessions/${encodeURIComponent(id)}`, { method: 'DELETE', }); setSessions(prev => prev.filter(s => s.id !== sessionPrefix)); } catch (err) { alert('ניתוק נכשל: ' + err.message); } }; const fmtDate = (iso) => new Date(iso).toLocaleString('he-IL', { dateStyle: 'short', timeStyle: 'short', }); const parseDevice = (ua) => { if (!ua) return 'לא ידוע'; if (/iPhone/i.test(ua)) return 'iPhone'; if (/iPad/i.test(ua)) return 'iPad'; if (/Android/i.test(ua)) return 'Android'; if (/Mac/i.test(ua)) return 'Mac'; if (/Windows/i.test(ua)) return 'Windows'; if (/Linux/i.test(ua)) return 'Linux'; return ua.slice(0, 30); }; return (