109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { useI18n } from '../components/I18nProvider';
|
|
|
|
type User = { id: string; email: string; role: string; status: string; emailVerifiedAt: string | null; approvedAt: string | null; name: string | null };
|
|
|
|
export default function ProfilePage() {
|
|
const { t } = useI18n();
|
|
const [user, setUser] = useState<User | null>(null);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [name, setName] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [saving, setSaving] = useState(false);
|
|
const [message, setMessage] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
fetch('/api/auth/me', { cache: 'no-store' })
|
|
.then((res) => res.json())
|
|
.then((data) => {
|
|
if (data.user) {
|
|
setUser(data.user);
|
|
setName(data.user.name ?? '');
|
|
} else setError(t('notLoggedIn'));
|
|
})
|
|
.catch(() => setError(t('notLoggedIn')));
|
|
}, [t]);
|
|
|
|
async function onSave(e: React.FormEvent) {
|
|
e.preventDefault();
|
|
setSaving(true);
|
|
setError(null);
|
|
setMessage(null);
|
|
try {
|
|
const res = await fetch('/api/me', {
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name, password: password || undefined }),
|
|
});
|
|
const data = await res.json();
|
|
if (!res.ok) {
|
|
setError(data.error || 'Update failed');
|
|
} else {
|
|
setUser(data.user);
|
|
setPassword('');
|
|
setMessage(t('profileUpdated'));
|
|
}
|
|
} catch (err) {
|
|
setError('Update failed');
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<main className="panel" style={{ maxWidth: 640, margin: '40px auto' }}>
|
|
<h1>{t('myProfileTitle')}</h1>
|
|
{message ? <p style={{ color: 'green' }}>{message}</p> : null}
|
|
{user ? (
|
|
<>
|
|
<ul>
|
|
<li>
|
|
<strong>{t('profileEmail')}:</strong> {user.email}
|
|
</li>
|
|
<li>
|
|
<strong>{t('profileName')}:</strong> {user.name ?? '—'}
|
|
</li>
|
|
<li>
|
|
<strong>{t('profileRole')}:</strong> {user.role}
|
|
</li>
|
|
<li>
|
|
<strong>{t('profileStatus')}:</strong> {user.status}
|
|
</li>
|
|
<li>
|
|
<strong>{t('profileEmailVerified')}:</strong> {user.emailVerifiedAt ? t('yes') : t('no')}
|
|
</li>
|
|
<li>
|
|
<strong>{t('profileApproved')}:</strong> {user.approvedAt ? t('yes') : t('no')}
|
|
</li>
|
|
</ul>
|
|
<div style={{ marginTop: 16, display: 'flex', gap: 10, flexWrap: 'wrap' }}>
|
|
<a className="button secondary" href="/listings/mine">
|
|
{t('navMyListings')}
|
|
</a>
|
|
<a className="button secondary" href="/listings/new">
|
|
{t('navNewListing')}
|
|
</a>
|
|
</div>
|
|
<form onSubmit={onSave} style={{ marginTop: 20, display: 'grid', gap: 10, maxWidth: 420 }}>
|
|
<label>
|
|
{t('profileName')}
|
|
<input value={name} onChange={(e) => setName(e.target.value)} />
|
|
</label>
|
|
<label>
|
|
{t('passwordLabel')} ({t('passwordHint')})
|
|
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} minLength={8} />
|
|
</label>
|
|
<p style={{ fontSize: 12, color: '#666' }}>{t('emailLocked')}</p>
|
|
<button className="button" type="submit" disabled={saving}>
|
|
{saving ? t('saving') : t('save')}
|
|
</button>
|
|
</form>
|
|
</>
|
|
) : (
|
|
<p style={{ color: 'red' }}>{error ?? t('notLoggedIn')}</p>
|
|
)}
|
|
</main>
|
|
);
|
|
}
|