sahel-api / website /src /components /VerifyEmailPage.jsx
Mohamed-20's picture
Deploy backend to HF Spaces
1337ed3
Raw
History Blame Contribute Delete
4.62 kB
import { useEffect, useState } from 'react';
import { API_URL } from '../config';
import { saveSession } from '../lib/session';
import { ROUTES } from '../lib/routes';
import MarketingHeader from './layout/MarketingHeader';
import { useLanguage } from '../i18n';
export default function VerifyEmailPage() {
const { t } = useLanguage();
const [status, setStatus] = useState('verifying');
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const token = params.get('token');
if (!token) {
setStatus('no-token');
return;
}
fetch(`${API_URL}/owners/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
})
.then(async (res) => {
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.detail || 'Verification failed');
}
const data = await res.json();
const session = { owner: data.owner, token: data.token, expires_at: data.expires_at };
saveSession(session);
setStatus('success');
})
.catch((err) => {
console.error('Verify email error:', err);
setStatus('error');
});
}, []);
return (
<div className="bg-surface-container-low min-h-screen flex flex-col">
<MarketingHeader />
<div className="flex-1 flex flex-col items-center justify-center p-sm">
<div className="w-full max-w-[400px] bg-surface-container-lowest border border-hairline-border rounded-xl p-lg flex flex-col items-center shadow-sm text-center animate-fade-in">
{status === 'verifying' && (
<>
<div className="w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mb-lg">
<span className="material-symbols-outlined text-primary text-[36px] animate-spin">progress_activity</span>
</div>
<h1 className="font-headline-sm text-headline-sm text-on-surface mb-sm">{t('verifyEmail.verifying')}</h1>
</>
)}
{status === 'success' && (
<>
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mb-lg">
<span className="material-symbols-outlined text-green-600 text-[36px]" style={{ fontVariationSettings: "'FILL' 1" }}>check_circle</span>
</div>
<h1 className="font-headline-sm text-headline-sm text-on-surface mb-sm">{t('verifyEmail.success')}</h1>
<p className="font-body-md text-body-md text-on-surface-variant mb-lg">{t('verifyEmail.successDesc')}</p>
<a href={ROUTES.onboarding} className="bg-primary text-on-primary px-lg py-sm rounded-lg font-label-md text-label-md hover:opacity-90 transition-all no-underline">
{t('verifyEmail.continueOnboarding')}
</a>
</>
)}
{status === 'error' && (
<>
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mb-lg">
<span className="material-symbols-outlined text-red-600 text-[36px]" style={{ fontVariationSettings: "'FILL' 1" }}>error</span>
</div>
<h1 className="font-headline-sm text-headline-sm text-on-surface mb-sm">{t('verifyEmail.failed')}</h1>
<p className="font-body-md text-body-md text-on-surface-variant mb-lg">{t('verifyEmail.failedDesc')}</p>
<a href={ROUTES.login} className="bg-primary text-on-primary px-lg py-sm rounded-lg font-label-md text-label-md hover:opacity-90 transition-all no-underline">
{t('verifyEmail.goToLogin')}
</a>
</>
)}
{status === 'no-token' && (
<>
<div className="w-16 h-16 bg-orange-100 rounded-full flex items-center justify-center mb-lg">
<span className="material-symbols-outlined text-orange-600 text-[36px]">link_off</span>
</div>
<h1 className="font-headline-sm text-headline-sm text-on-surface mb-sm">{t('verifyEmail.missingToken')}</h1>
<p className="font-body-md text-body-md text-on-surface-variant mb-lg">{t('verifyEmail.missingTokenDesc')}</p>
<a href={ROUTES.login} className="bg-primary text-on-primary px-lg py-sm rounded-lg font-label-md text-label-md hover:opacity-90 transition-all no-underline">
{t('verifyEmail.goToLogin')}
</a>
</>
)}
</div>
</div>
</div>
);
}