testing singup
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 20s
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 20s
This commit is contained in:
@@ -4,16 +4,16 @@ import { LoadingSpinner } from './LoadingSpinner';
|
||||
import { XMarkIcon } from './icons/XMarkIcon';
|
||||
import { GoogleIcon } from './icons/GoogleIcon';
|
||||
import { PasswordStrength } from './PasswordStrength';
|
||||
import { GithubIcon } from './icons/GithubIcon';
|
||||
|
||||
interface AuthModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSwitchToSignUp: () => void;
|
||||
}
|
||||
|
||||
type AuthView = 'signIn' | 'signUp' | 'resetPassword' | 'magicLink';
|
||||
type AuthView = 'signIn' | 'resetPassword' | 'magicLink';
|
||||
|
||||
export const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onClose }) => {
|
||||
export const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onClose, onSwitchToSignUp }) => {
|
||||
const [view, setView] = useState<AuthView>('signIn');
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
@@ -40,21 +40,10 @@ export const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onClose }) => {
|
||||
setMessage(null);
|
||||
|
||||
try {
|
||||
if (view === 'signUp') {
|
||||
// Add the emailRedirectTo option to ensure the confirmation link
|
||||
// points back to the current environment (localhost or production).
|
||||
const { error } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: { emailRedirectTo: window.location.href }
|
||||
});
|
||||
if (error) throw error;
|
||||
setMessage('Check your email for the confirmation link!');
|
||||
} else {
|
||||
const { error } = await supabase.auth.signInWithPassword({ email, password });
|
||||
if (error) throw error;
|
||||
onClose();
|
||||
}
|
||||
// This modal now only handles sign-in.
|
||||
const { error } = await supabase.auth.signInWithPassword({ email, password });
|
||||
if (error) throw error;
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'An unexpected error occurred.');
|
||||
} finally {
|
||||
@@ -137,23 +126,6 @@ export const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onClose }) => {
|
||||
</button>
|
||||
|
||||
<div className="p-8">
|
||||
{view !== 'resetPassword' && view !== 'magicLink' && (
|
||||
<div className="flex border-b border-gray-200 dark:border-gray-700 mb-6">
|
||||
<button
|
||||
onClick={() => handleViewChange('signIn')}
|
||||
className={`flex-1 py-3 text-sm font-semibold text-center transition-colors ${view === 'signIn' ? 'text-brand-primary border-b-2 border-brand-primary' : 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200'}`}
|
||||
>
|
||||
Sign In
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleViewChange('signUp')}
|
||||
className={`flex-1 py-3 text-sm font-semibold text-center transition-colors ${view === 'signUp' ? 'text-brand-primary border-b-2 border-brand-primary' : 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200'}`}
|
||||
>
|
||||
Sign Up
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{view === 'magicLink' ? (
|
||||
<>
|
||||
<h2 className="text-2xl font-bold text-center text-gray-800 dark:text-white mb-2">Sign In with Magic Link</h2>
|
||||
@@ -191,11 +163,9 @@ export const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onClose }) => {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h2 className="text-2xl font-bold text-center text-gray-800 dark:text-white mb-2">
|
||||
{view === 'signUp' ? 'Create an Account' : 'Welcome Back'}
|
||||
</h2>
|
||||
<h2 className="text-2xl font-bold text-center text-gray-800 dark:text-white mb-2">Welcome Back</h2>
|
||||
<p className="text-center text-gray-500 dark:text-gray-400 mb-6 text-sm">
|
||||
{view === 'signUp' ? 'to start personalizing your experience.' : 'to access your watched items and lists.'}
|
||||
Sign in to access your watched items and lists.
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
@@ -203,10 +173,6 @@ export const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onClose }) => {
|
||||
<GoogleIcon className="w-5 h-5 mr-3" />
|
||||
Continue with Google
|
||||
</button>
|
||||
<button onClick={() => handleOAuthSignIn('github')} className="w-full flex items-center justify-center px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<GithubIcon className="w-5 h-5 mr-3" />
|
||||
Continue with GitHub
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="my-6 flex items-center">
|
||||
@@ -223,23 +189,24 @@ export const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onClose }) => {
|
||||
<div>
|
||||
<div className="flex justify-between">
|
||||
<label htmlFor="password" className="block text-sm font-medium text-gray-700 dark:text-gray-300">Password</label>
|
||||
{view === 'signIn' && (
|
||||
<button type="button" onClick={() => handleViewChange('resetPassword')} className="text-sm text-brand-primary hover:underline">Forgot password?</button>
|
||||
)}
|
||||
<button type="button" onClick={() => handleViewChange('resetPassword')} className="text-sm text-brand-primary hover:underline">Forgot password?</button>
|
||||
</div>
|
||||
<input id="password" type="password" value={password} onChange={e => setPassword(e.target.value)} required className="mt-1 block w-full px-3 py-2 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-brand-primary focus:border-brand-primary" placeholder="••••••••" />
|
||||
{view === 'signUp' && password.length > 0 && (
|
||||
<PasswordStrength password={password} />
|
||||
)}
|
||||
</div>
|
||||
{error && <p className="text-sm text-red-600 dark:text-red-400 text-center">{error}</p>}
|
||||
{message && <p className="text-sm text-green-600 dark:text-green-400 text-center">{message}</p>}
|
||||
<button type="submit" disabled={loading || (view === 'signUp' && !!message)} className="w-full bg-brand-secondary hover:bg-brand-dark disabled:bg-gray-400 disabled:cursor-not-allowed text-white font-bold py-2.5 px-4 rounded-lg transition-colors duration-300 flex items-center justify-center">
|
||||
{loading ? <div className="w-5 h-5"><LoadingSpinner /></div> : (view === 'signUp' ? 'Create Account' : 'Sign In')}
|
||||
<button type="submit" disabled={loading} className="w-full bg-brand-secondary hover:bg-brand-dark disabled:bg-gray-400 disabled:cursor-not-allowed text-white font-bold py-2.5 px-4 rounded-lg transition-colors duration-300 flex items-center justify-center">
|
||||
{loading ? <div className="w-5 h-5"><LoadingSpinner /></div> : 'Sign In'}
|
||||
</button>
|
||||
<div className="text-center">
|
||||
<button type="button" onClick={() => handleViewChange('magicLink')} className="text-sm text-brand-primary hover:underline">Sign in with Magic Link instead</button>
|
||||
</div>
|
||||
<div className="text-center pt-2">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Don't have an account?{' '}
|
||||
<button type="button" onClick={onSwitchToSignUp} className="font-medium text-brand-primary hover:underline">Sign Up</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { UnitSystemToggle } from './UnitSystemToggle';
|
||||
import { Session } from '@supabase/supabase-js';
|
||||
import { supabase } from '../services/supabaseClient';
|
||||
import { AuthModal } from './AuthModal';
|
||||
import { SignUpModal } from './SignUpModal';
|
||||
import { UserIcon } from './icons/UserIcon';
|
||||
import { CogIcon } from './icons/CogIcon';
|
||||
import { MicrophoneIcon } from './icons/MicrophoneIcon';
|
||||
@@ -21,7 +22,19 @@ interface HeaderProps {
|
||||
}
|
||||
|
||||
export const Header: React.FC<HeaderProps> = ({ isDarkMode, toggleDarkMode, unitSystem, toggleUnitSystem, session, onOpenProfile, onOpenVoiceAssistant, onSignOut }) => {
|
||||
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
|
||||
const [isSignInModalOpen, setIsSignInModalOpen] = useState(false);
|
||||
const [isSignUpModalOpen, setIsSignUpModalOpen] = useState(false);
|
||||
|
||||
const openSignIn = () => {
|
||||
setIsSignUpModalOpen(false);
|
||||
setIsSignInModalOpen(true);
|
||||
};
|
||||
|
||||
const openSignUp = () => {
|
||||
setIsSignInModalOpen(false);
|
||||
setIsSignUpModalOpen(true);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -72,13 +85,13 @@ export const Header: React.FC<HeaderProps> = ({ isDarkMode, toggleDarkMode, unit
|
||||
) : (
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
onClick={() => setIsAuthModalOpen(true)}
|
||||
onClick={openSignIn}
|
||||
className="text-sm font-semibold text-gray-600 hover:text-brand-primary dark:text-gray-300 dark:hover:text-brand-light transition-colors"
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsAuthModalOpen(true)}
|
||||
onClick={openSignUp}
|
||||
className="px-3 py-1.5 text-sm font-semibold text-white bg-brand-primary hover:bg-brand-secondary rounded-md transition-colors"
|
||||
>
|
||||
Sign Up
|
||||
@@ -89,7 +102,16 @@ export const Header: React.FC<HeaderProps> = ({ isDarkMode, toggleDarkMode, unit
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{isAuthModalOpen && supabase && <AuthModal isOpen={isAuthModalOpen} onClose={() => setIsAuthModalOpen(false)} />}
|
||||
{isSignInModalOpen && supabase && (
|
||||
<AuthModal
|
||||
isOpen={isSignInModalOpen}
|
||||
onClose={() => setIsSignInModalOpen(false)}
|
||||
onSwitchToSignUp={openSignUp}
|
||||
/>
|
||||
)}
|
||||
{isSignUpModalOpen && supabase && (
|
||||
<SignUpModal isOpen={isSignUpModalOpen} onClose={() => setIsSignUpModalOpen(false)} onSwitchToSignIn={openSignIn} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
129
components/SignUpModal.tsx
Normal file
129
components/SignUpModal.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
import React, { useState } from 'react';
|
||||
import { supabase } from '../services/supabaseClient';
|
||||
import { LoadingSpinner } from './LoadingSpinner';
|
||||
import { XMarkIcon } from './icons/XMarkIcon';
|
||||
import { GoogleIcon } from './icons/GoogleIcon';
|
||||
import { GithubIcon } from './icons/GithubIcon';
|
||||
import { PasswordStrength } from './PasswordStrength';
|
||||
|
||||
interface SignUpModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSwitchToSignIn: () => void;
|
||||
}
|
||||
|
||||
export const SignUpModal: React.FC<SignUpModalProps> = ({ isOpen, onClose, onSwitchToSignIn }) => {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [message, setMessage] = useState<string | null>(null);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
setMessage(null);
|
||||
|
||||
try {
|
||||
const { error } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: { emailRedirectTo: window.location.href }
|
||||
});
|
||||
if (error) throw error;
|
||||
setMessage('Check your email for the confirmation link!');
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'An unexpected error occurred.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOAuthSignIn = async (provider: 'google' | 'github') => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const { error } = await supabase.auth.signInWithOAuth({
|
||||
provider,
|
||||
options: {
|
||||
redirectTo: window.location.href,
|
||||
}
|
||||
});
|
||||
if (error) {
|
||||
setError(error.message);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 bg-black bg-opacity-60 z-50 flex justify-center items-center p-4"
|
||||
onClick={onClose}
|
||||
aria-modal="true"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
className="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-md relative"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute top-3 right-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors"
|
||||
aria-label="Close sign up modal"
|
||||
>
|
||||
<XMarkIcon className="w-6 h-6" />
|
||||
</button>
|
||||
|
||||
<div className="p-8">
|
||||
<h2 className="text-2xl font-bold text-center text-gray-800 dark:text-white mb-2">
|
||||
Create an Account
|
||||
</h2>
|
||||
<p className="text-center text-gray-500 dark:text-gray-400 mb-6 text-sm">
|
||||
to start personalizing your experience.
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
<button onClick={() => handleOAuthSignIn('google')} className="w-full flex items-center justify-center px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<GoogleIcon className="w-5 h-5 mr-3" />
|
||||
Continue with Google
|
||||
</button>
|
||||
<button onClick={() => handleOAuthSignIn('github')} className="w-full flex items-center justify-center px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<GithubIcon className="w-5 h-5 mr-3" />
|
||||
Continue with GitHub
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="my-6 flex items-center">
|
||||
<div className="flex-grow border-t border-gray-300 dark:border-gray-600"></div>
|
||||
<span className="flex-shrink mx-4 text-gray-400 text-sm">OR</span>
|
||||
<div className="flex-grow border-t border-gray-300 dark:border-gray-600"></div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="email-signup" className="block text-sm font-medium text-gray-700 dark:text-gray-300">Email address</label>
|
||||
<input id="email-signup" type="email" value={email} onChange={e => setEmail(e.target.value)} required className="mt-1 block w-full px-3 py-2 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-brand-primary focus:border-brand-primary" placeholder="you@example.com" />
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="password-signup" className="block text-sm font-medium text-gray-700 dark:text-gray-300">Password</label>
|
||||
<input id="password-signup" type="password" value={password} onChange={e => setPassword(e.target.value)} required className="mt-1 block w-full px-3 py-2 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-brand-primary focus:border-brand-primary" placeholder="••••••••" />
|
||||
{password.length > 0 && <PasswordStrength password={password} />}
|
||||
</div>
|
||||
{error && <p className="text-sm text-red-600 dark:text-red-400 text-center">{error}</p>}
|
||||
{message && <p className="text-sm text-green-600 dark:text-green-400 text-center">{message}</p>}
|
||||
<button type="submit" disabled={loading || !!message} className="w-full bg-brand-secondary hover:bg-brand-dark disabled:bg-gray-400 disabled:cursor-not-allowed text-white font-bold py-2.5 px-4 rounded-lg transition-colors duration-300 flex items-center justify-center">
|
||||
{loading ? <div className="w-5 h-5"><LoadingSpinner /></div> : 'Create Account'}
|
||||
</button>
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Already have an account? <button type="button" onClick={onSwitchToSignIn} className="font-medium text-brand-primary hover:underline">Sign In</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user