build: d805a69a-e437-4a58-a083-30e34be74fcd
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonInput,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonPage,
|
||||
IonText,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
useIonViewWillEnter,
|
||||
} from '@ionic/react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { supabase } from '../supabase';
|
||||
import { setStatusBarStyle, Style } from '../utils/statusBar';
|
||||
|
||||
const Auth: React.FC = () => {
|
||||
const history = useHistory();
|
||||
const [mode, setMode] = useState<'login' | 'signup'>('login');
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [message, setMessage] = useState('');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useIonViewWillEnter(() => {
|
||||
setStatusBarStyle(Style.Light);
|
||||
});
|
||||
|
||||
const showError = (msg: string) => {
|
||||
setErrorMessage(msg);
|
||||
window.setTimeout(() => setErrorMessage(''), 4000);
|
||||
};
|
||||
|
||||
const showMessage = (msg: string) => {
|
||||
setMessage(msg);
|
||||
window.setTimeout(() => setMessage(''), 4000);
|
||||
};
|
||||
|
||||
const normaliseAuthError = (msg: string) => {
|
||||
const lowered = msg.toLowerCase();
|
||||
|
||||
if (lowered.includes('email not confirmed')) {
|
||||
return 'Please confirm your email before signing in. Check your inbox for the verification email.';
|
||||
}
|
||||
|
||||
if (lowered.includes('invalid login credentials')) {
|
||||
return 'Incorrect email or password. Please try again.';
|
||||
}
|
||||
|
||||
if (lowered.includes('user already registered')) {
|
||||
return 'An account with this email already exists. Please sign in instead.';
|
||||
}
|
||||
|
||||
return msg;
|
||||
};
|
||||
|
||||
const handleResendConfirmation = async () => {
|
||||
if (!email.trim()) {
|
||||
showError(
|
||||
'Enter your email address first so we can resend the confirmation email.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
const { error } = await supabase.auth.resend({
|
||||
type: 'signup',
|
||||
email: email.trim(),
|
||||
});
|
||||
setIsSubmitting(false);
|
||||
|
||||
if (error) {
|
||||
showError(normaliseAuthError(error.message));
|
||||
return;
|
||||
}
|
||||
|
||||
showMessage('Confirmation email sent. Please check your inbox.');
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!email.trim() || password.trim().length < 6) {
|
||||
showError('Enter a valid email and password with at least 6 characters.');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
if (mode === 'login') {
|
||||
const { error } = await supabase.auth.signInWithPassword({
|
||||
email: email.trim(),
|
||||
password,
|
||||
});
|
||||
setIsSubmitting(false);
|
||||
if (error) {
|
||||
showError(normaliseAuthError(error.message));
|
||||
return;
|
||||
}
|
||||
history.replace('/home');
|
||||
return;
|
||||
}
|
||||
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
email: email.trim(),
|
||||
password,
|
||||
});
|
||||
setIsSubmitting(false);
|
||||
if (error) {
|
||||
showError(normaliseAuthError(error.message));
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.session) {
|
||||
history.replace('/home');
|
||||
return;
|
||||
}
|
||||
|
||||
showMessage('Account created! Check your email for a verification link.');
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonTitle>
|
||||
{mode === 'login' ? 'Welcome back' : 'Create account'}
|
||||
</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent className="ion-padding auth-page">
|
||||
<div className="auth-card">
|
||||
<h1>
|
||||
{mode === 'login'
|
||||
? 'Sign in to continue'
|
||||
: 'Create your shopping account'}
|
||||
</h1>
|
||||
<p className="muted-text">
|
||||
Save your orders, addresses, and track deliveries securely.
|
||||
</p>
|
||||
|
||||
{errorMessage && (
|
||||
<IonText color="danger">
|
||||
<p>{errorMessage}</p>
|
||||
</IonText>
|
||||
)}
|
||||
{message && (
|
||||
<IonText color="success">
|
||||
<p>{message}</p>
|
||||
</IonText>
|
||||
)}
|
||||
|
||||
<div className="auth-form">
|
||||
<IonItem lines="none" className="auth-input-item">
|
||||
<IonLabel position="stacked">Email</IonLabel>
|
||||
<IonInput
|
||||
value={email}
|
||||
type="email"
|
||||
onIonInput={(e) => setEmail(e.detail.value ?? '')}
|
||||
/>
|
||||
</IonItem>
|
||||
<IonItem lines="none" className="auth-input-item">
|
||||
<IonLabel position="stacked">Password</IonLabel>
|
||||
<IonInput
|
||||
value={password}
|
||||
type="password"
|
||||
onIonInput={(e) => setPassword(e.detail.value ?? '')}
|
||||
/>
|
||||
</IonItem>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={handleSubmit}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{mode === 'login' ? 'Sign in' : 'Create account'}
|
||||
</IonButton>
|
||||
{mode === 'login' && (
|
||||
<IonButton
|
||||
fill="clear"
|
||||
expand="block"
|
||||
onClick={handleResendConfirmation}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Resend confirmation email
|
||||
</IonButton>
|
||||
)}
|
||||
<IonButton
|
||||
fill="clear"
|
||||
expand="block"
|
||||
onClick={() => setMode(mode === 'login' ? 'signup' : 'login')}
|
||||
>
|
||||
{mode === 'login'
|
||||
? 'Need an account? Sign up'
|
||||
: 'Already have an account? Sign in'}
|
||||
</IonButton>
|
||||
</div>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Auth;
|
||||
Reference in New Issue
Block a user