chore: update UI for signup and login

- add back button for better UX
This commit is contained in:
Hafid Nur
2025-11-25 18:57:03 +07:00
parent f40e0e35b9
commit 7642fef3e6
5 changed files with 174 additions and 91 deletions
+38 -26
View File
@@ -13,62 +13,72 @@ const CallbackPage: FC = (): ReactElement => {
useEffect(() => { useEffect(() => {
const handleCallback = async () => { const handleCallback = async () => {
if (hasRunRef.current) { if (hasRunRef.current) {
console.log('[Callback] Already processed, skipping...'); // console.log('[Callback] Already processed, skipping...');
return; return;
} }
hasRunRef.current = true; hasRunRef.current = true;
try { try {
console.log('[Callback] Processing OAuth callback...'); // console.log('[Callback] Processing OAuth callback...');
console.log('[Callback] Current URL:', globalThis.location.href); // console.log('[Callback] Current URL:', globalThis.location.href);
// Supabase client is configured with detectSessionInUrl: true // Supabase client is configured with detectSessionInUrl: true
// This means Supabase automatically detects and processes OAuth tokens from the URL hash // This means Supabase automatically detects and processes OAuth tokens from the URL hash
// We just need to wait a moment for it to complete, then check for the session // We just need to wait a moment for it to complete, then check for the session
console.log('[Callback] Waiting for Supabase to process OAuth callback...'); // console.log('[Callback] Waiting for Supabase to process OAuth callback...');
await new Promise(resolve => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000));
// Get the session that Supabase automatically created from the URL hash // Get the session that Supabase automatically created from the URL hash
const { data: { session: sessionData }, error: sessionError } = await supabase.auth.getSession(); const {
data: { session: sessionData },
error: sessionError,
} = await supabase.auth.getSession();
if (sessionError) { if (sessionError) {
console.error('[Callback] Session error:', sessionError); // console.error('[Callback] Session error:', sessionError);
throw new Error(sessionError.message || 'Failed to get session'); throw new Error(sessionError.message || 'Failed to get session');
} }
if (!sessionData || !sessionData.user) { if (!sessionData || !sessionData.user) {
throw new Error('No session found after OAuth callback. Please try logging in again.'); throw new Error(
'No session found after OAuth callback. Please try logging in again.'
);
} }
console.log('[Callback] Supabase session established:', { // console.log('[Callback] Supabase session established:', {
userId: sessionData.user.id, // userId: sessionData.user.id,
email: sessionData.user.email, // email: sessionData.user.email,
}); // });
// Create/update user in the users table (for foreign key constraints) // Create/update user in the users table (for foreign key constraints)
console.log('[Callback] Creating/updating user record...'); // console.log('[Callback] Creating/updating user record...');
const { data: userData, error: upsertError } = await supabase const { data: userData, error: upsertError } = await supabase
.from('users') .from('users')
.upsert({ .upsert(
{
id: sessionData.user.id, id: sessionData.user.id,
email: sessionData.user.email || '', email: sessionData.user.email || '',
fullname: sessionData.user.user_metadata?.full_name || fullname:
sessionData.user.user_metadata?.full_name ||
sessionData.user.user_metadata?.name || sessionData.user.user_metadata?.name ||
sessionData.user.email?.split('@')[0] || '', sessionData.user.email?.split('@')[0] ||
'',
avatar: sessionData.user.user_metadata?.avatar_url || '', avatar: sessionData.user.user_metadata?.avatar_url || '',
is_active: true, is_active: true,
updated_at: new Date().toISOString(), updated_at: new Date().toISOString(),
}, { },
{
onConflict: 'id', onConflict: 'id',
}) }
)
.select() .select()
.single(); .single();
if (upsertError) { if (upsertError) {
console.warn('[Callback] Failed to create user record:', upsertError); // console.warn('[Callback] Failed to create user record:', upsertError);
// Don't throw - continue with login even if user record creation fails // Don't throw - continue with login even if user record creation fails
} else { } else {
console.log('[Callback] User record created/updated successfully'); // console.log('[Callback] User record created/updated successfully');
} }
// Store user-friendly data in Zustand for UI purposes // Store user-friendly data in Zustand for UI purposes
@@ -77,9 +87,11 @@ const CallbackPage: FC = (): ReactElement => {
const userRecord = userData || { const userRecord = userData || {
id: sessionData.user.id, id: sessionData.user.id,
email: sessionData.user.email || '', email: sessionData.user.email || '',
fullname: sessionData.user.user_metadata?.full_name || fullname:
sessionData.user.user_metadata?.full_name ||
sessionData.user.user_metadata?.name || sessionData.user.user_metadata?.name ||
sessionData.user.email?.split('@')[0] || '', sessionData.user.email?.split('@')[0] ||
'',
avatar: sessionData.user.user_metadata?.avatar_url || '', avatar: sessionData.user.user_metadata?.avatar_url || '',
phone_number: '', phone_number: '',
birthdate: '', birthdate: '',
@@ -114,21 +126,21 @@ const CallbackPage: FC = (): ReactElement => {
}, },
}); });
console.log('[Callback] Session stored successfully'); // console.log('[Callback] Session stored successfully');
toast.success('Login successful!'); toast.success('Login successful!');
setIsProcessing(false); setIsProcessing(false);
// Check if user has completed onboarding (has location) // Check if user has completed onboarding (has location)
// Use globalThis.location.replace for hard redirect to prevent history issues // Use globalThis.location.replace for hard redirect to prevent history issues
if (userRecord.location) { if (userRecord.location) {
console.log('[Callback] User has completed onboarding, redirecting to dashboard...'); // console.log('[Callback] User has completed onboarding, redirecting to dashboard...');
globalThis.location.replace('/dashboard'); globalThis.location.replace('/dashboard');
} else { } else {
console.log('[Callback] User needs onboarding, redirecting...'); // console.log('[Callback] User needs onboarding, redirecting...');
globalThis.location.replace('/onboarding/user'); globalThis.location.replace('/onboarding/user');
} }
} catch (err) { } catch (err) {
console.error('[Callback] Error:', err); // console.error('[Callback] Error:', err);
setError((err as Error).message); setError((err as Error).message);
setIsProcessing(false); setIsProcessing(false);
toast.error('An error occurred during login'); toast.error('An error occurred during login');
@@ -1,12 +1,14 @@
import { useState } from 'react'; import { useState } from 'react';
import { supabase } from '@imphnen-frontend-service/service'; import { supabase } from '@imphnen-frontend-service/service';
import { Link } from 'react-router'; import { Link, useNavigate } from 'react-router';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Icon } from '@iconify/react';
export default function ForgotPasswordPage() { export default function ForgotPasswordPage() {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [emailSent, setEmailSent] = useState(false); const [emailSent, setEmailSent] = useState(false);
const navigate = useNavigate();
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
@@ -30,7 +32,7 @@ export default function ForgotPasswordPage() {
setEmailSent(true); setEmailSent(true);
toast.success('Password reset email sent! Check your inbox.'); toast.success('Password reset email sent! Check your inbox.');
} catch (err) { } catch (err) {
console.error('Failed to send reset email:', err); // console.error('Failed to send reset email:', err);
toast.error((err as Error).message || 'Failed to send reset email'); toast.error((err as Error).message || 'Failed to send reset email');
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -55,7 +57,8 @@ export default function ForgotPasswordPage() {
<div className="space-y-4"> <div className="space-y-4">
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600">
Click the link in the email to reset your password. The link will expire in 1 hour. Click the link in the email to reset your password. The link will
expire in 1 hour.
</p> </p>
<Link to="/auth/login"> <Link to="/auth/login">
@@ -90,7 +93,10 @@ export default function ForgotPasswordPage() {
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div> <div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="email"
className="block text-sm font-medium text-gray-700 mb-1"
>
Email Address Email Address
</label> </label>
<input <input
@@ -114,9 +120,13 @@ export default function ForgotPasswordPage() {
</button> </button>
</form> </form>
<div className="mt-6 text-center"> <div className="mt-8">
<Link to="/auth/login" className="text-primary-600 hover:text-primary-700 font-semibold"> <Link
Back to Login to="/auth/login"
className="text-primary-500 hover:text-primary-600 flex items-center"
>
<Icon icon="ic:baseline-chevron-left" width="24" height="24" />
<span> Back to Login</span>
</Link> </Link>
</div> </div>
</div> </div>
+44 -17
View File
@@ -1,11 +1,17 @@
import { useState } from 'react'; import { useState } from 'react';
import { useGitHubAuth, useEmailAuth, supabase, useAuthStore } from '@imphnen-frontend-service/service'; import {
useGitHubAuth,
useEmailAuth,
supabase,
useAuthStore,
} from '@imphnen-frontend-service/service';
import { GithubOutlined } from '@ant-design/icons'; import { GithubOutlined } from '@ant-design/icons';
import { useNavigate, Link } from 'react-router'; import { useNavigate, Link } from 'react-router';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Icon } from '@iconify/react';
export default function LoginPage() { export default function LoginPage() {
console.log('[LoginPage] Rendering...'); // console.log('[LoginPage] Rendering...');
const navigate = useNavigate(); const navigate = useNavigate();
const { setSession } = useAuthStore(); const { setSession } = useAuthStore();
@@ -28,10 +34,10 @@ export default function LoginPage() {
try { try {
setIsEmailLoading(true); setIsEmailLoading(true);
console.log('[Login] Attempting email login...'); // console.log('[Login] Attempting email login...');
const result = await signInWithEmail(email, password); const result = await signInWithEmail(email, password);
console.log('[Login] Email login successful:', result); // console.log('[Login] Email login successful:', result);
// Get user data from database // Get user data from database
const { data: userData } = await supabase const { data: userData } = await supabase
@@ -49,7 +55,8 @@ export default function LoginPage() {
user: { user: {
id: result.user.id, id: result.user.id,
email: result.user.email || '', email: result.user.email || '',
fullname: userData?.fullname || result.user.user_metadata?.full_name || '', fullname:
userData?.fullname || result.user.user_metadata?.full_name || '',
phone_number: userData?.phone_number || '', phone_number: userData?.phone_number || '',
avatar: userData?.avatar || '', avatar: userData?.avatar || '',
birthdate: userData?.birthdate || '', birthdate: userData?.birthdate || '',
@@ -86,22 +93,22 @@ export default function LoginPage() {
const handleGithubLogin = async () => { const handleGithubLogin = async () => {
try { try {
setIsGithubLoading(true); setIsGithubLoading(true);
console.log('[Login] Initiating GitHub OAuth...'); // console.log('[Login] Initiating GitHub OAuth...');
const result = await signInWithGitHub(); const result = await signInWithGitHub();
console.log('[Login] OAuth result:', result); // console.log('[Login] OAuth result:', result);
// Check if we got a redirect URL // Check if we got a redirect URL
if (result?.url) { if (result?.url) {
console.log('[Login] Redirecting to GitHub OAuth:', result.url); // console.log('[Login] Redirecting to GitHub OAuth:', result.url);
// Manually redirect immediately // Manually redirect immediately
globalThis.location.href = result.url; globalThis.location.href = result.url;
} else { } else {
console.error('[Login] No OAuth URL returned'); // console.error('[Login] No OAuth URL returned');
setIsGithubLoading(false); setIsGithubLoading(false);
} }
} catch (error) { } catch (error) {
console.error('[Login] GitHub login failed:', error); // console.error('[Login] GitHub login failed');
setIsGithubLoading(false); setIsGithubLoading(false);
} }
}; };
@@ -109,11 +116,19 @@ export default function LoginPage() {
return ( return (
<div className="flex justify-center items-center min-h-screen bg-gray-50 p-4"> <div className="flex justify-center items-center min-h-screen bg-gray-50 p-4">
<div className="bg-white w-full max-w-md p-8 rounded-2xl shadow-lg border border-gray-200"> <div className="bg-white w-full max-w-md p-8 rounded-2xl shadow-lg border border-gray-200">
<button
onClick={() => navigate('/')}
className="cursor-pointer text-primary-500 hover:text-primary-600 text-base font-sans flex items-center mb-6"
>
<Icon icon="ic:baseline-chevron-left" width="24" height="24" />
Back to Homepage
</button>
<div className="text-center mb-8"> <div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-2"> <h2 className="text-3xl font-bold text-gray-900 mb-2">
Welcome Back Welcome Back
</h2> </h2>
<p className="text-gray-600"> <p className="text-gray-600 font-sans">
Sign in to join or create your hackathon team Sign in to join or create your hackathon team
</p> </p>
</div> </div>
@@ -126,7 +141,10 @@ export default function LoginPage() {
<form onSubmit={handleEmailLogin} className="space-y-4"> <form onSubmit={handleEmailLogin} className="space-y-4">
<div> <div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="email"
className="block text-sm font-medium text-gray-700 mb-1"
>
Email Email
</label> </label>
<input <input
@@ -143,10 +161,16 @@ export default function LoginPage() {
<div> <div>
<div className="flex items-center justify-between mb-1"> <div className="flex items-center justify-between mb-1">
<label htmlFor="password" className="block text-sm font-medium text-gray-700"> <label
htmlFor="password"
className="block text-sm font-medium text-gray-700"
>
Password Password
</label> </label>
<Link to="/auth/forgot-password" className="text-sm text-primary-600 hover:text-primary-700"> <Link
to="/auth/forgot-password"
className="text-sm text-primary-600 hover:text-primary-700"
>
Forgot password? Forgot password?
</Link> </Link>
</div> </div>
@@ -165,7 +189,7 @@ export default function LoginPage() {
<button <button
type="submit" type="submit"
disabled={isEmailLoading} disabled={isEmailLoading}
className="w-full py-3 bg-primary-600 text-white rounded-lg font-semibold hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors" className="w-full py-3 bg-primary-600 text-white rounded-lg font-semibold hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors cursor-pointer"
> >
{isEmailLoading ? 'Signing in...' : 'Sign in with Email'} {isEmailLoading ? 'Signing in...' : 'Sign in with Email'}
</button> </button>
@@ -181,7 +205,7 @@ export default function LoginPage() {
onClick={handleGithubLogin} onClick={handleGithubLogin}
disabled={isGithubLoading} disabled={isGithubLoading}
type="button" type="button"
className="w-full py-3 flex items-center justify-center gap-2 bg-gray-100 border border-gray-300 rounded-lg font-semibold hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:bg-gray-100 disabled:cursor-not-allowed transition-colors" className="w-full py-3 flex items-center justify-center gap-2 bg-gray-100 border border-gray-300 rounded-lg font-semibold hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:bg-gray-100 disabled:cursor-not-allowed transition-colors cursor-pointer"
> >
<GithubOutlined className="text-xl" /> <GithubOutlined className="text-xl" />
<span> <span>
@@ -192,7 +216,10 @@ export default function LoginPage() {
<div className="mt-6 text-center"> <div className="mt-6 text-center">
<p className="text-gray-600 text-sm"> <p className="text-gray-600 text-sm">
Don't have an account?{' '} Don't have an account?{' '}
<a href="/auth/signup" className="text-primary-600 hover:text-primary-700 font-semibold"> <a
href="/auth/signup"
className="text-primary-600 hover:text-primary-700 font-semibold"
>
Sign up Sign up
</a> </a>
</p> </p>
@@ -39,7 +39,7 @@ export default function ResetPasswordPage() {
setIsLoading(true); setIsLoading(true);
const { error } = await supabase.auth.updateUser({ const { error } = await supabase.auth.updateUser({
password: password password: password,
}); });
if (error) { if (error) {
@@ -52,7 +52,7 @@ export default function ResetPasswordPage() {
await supabase.auth.signOut(); await supabase.auth.signOut();
navigate('/auth/login'); navigate('/auth/login');
} catch (err) { } catch (err) {
console.error('Failed to reset password:', err); // console.error('Failed to reset password:', err);
toast.error((err as Error).message || 'Failed to reset password'); toast.error((err as Error).message || 'Failed to reset password');
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -77,14 +77,15 @@ export default function ResetPasswordPage() {
<h2 className="text-3xl font-bold text-gray-900 mb-2"> <h2 className="text-3xl font-bold text-gray-900 mb-2">
Set New Password Set New Password
</h2> </h2>
<p className="text-gray-600"> <p className="text-gray-600">Enter your new password below</p>
Enter your new password below
</p>
</div> </div>
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div> <div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="password"
className="block text-sm font-medium text-gray-700 mb-1"
>
New Password New Password
</label> </label>
<input <input
@@ -101,7 +102,10 @@ export default function ResetPasswordPage() {
</div> </div>
<div> <div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="confirmPassword"
className="block text-sm font-medium text-gray-700 mb-1"
>
Confirm New Password Confirm New Password
</label> </label>
<input <input
+56 -26
View File
@@ -1,8 +1,14 @@
import { useState } from 'react'; import { useState } from 'react';
import { useGitHubAuth, useEmailAuth, supabase, useAuthStore } from '@imphnen-frontend-service/service'; import {
useGitHubAuth,
useEmailAuth,
supabase,
useAuthStore,
} from '@imphnen-frontend-service/service';
import { GithubOutlined } from '@ant-design/icons'; import { GithubOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Icon } from '@iconify/react';
export default function SignupPage() { export default function SignupPage() {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -38,30 +44,31 @@ export default function SignupPage() {
try { try {
setIsEmailLoading(true); setIsEmailLoading(true);
console.log('[Signup] Attempting email signup...'); // console.log('[Signup] Attempting email signup...');
const result = await signUpWithEmail(email, password, fullname); const result = await signUpWithEmail(email, password, fullname);
console.log('[Signup] Email signup successful:', result); // console.log('[Signup] Email signup successful:', result);
if (!result.user) { if (!result.user) {
throw new Error('Signup failed - no user returned'); throw new Error('Signup failed - no user returned');
} }
// Create user record in database // Create user record in database
const { error: upsertError } = await supabase const { error: upsertError } = await supabase.from('users').upsert(
.from('users') {
.upsert({
id: result.user.id, id: result.user.id,
email: result.user.email || '', email: result.user.email || '',
fullname: fullname, fullname: fullname,
is_active: true, is_active: true,
updated_at: new Date().toISOString(), updated_at: new Date().toISOString(),
}, { },
{
onConflict: 'id', onConflict: 'id',
}); }
);
if (upsertError) { if (upsertError) {
console.warn('[Signup] Failed to create user record:', upsertError); console.warn('[Signup] Failed to create user record');
} }
// If session is available (email confirmation disabled), store it // If session is available (email confirmation disabled), store it
@@ -94,13 +101,15 @@ export default function SignupPage() {
navigate('/onboarding/user'); navigate('/onboarding/user');
} else { } else {
// Email confirmation is enabled // Email confirmation is enabled
toast.success('Account created! Please check your email to verify your account.'); toast.success(
'Account created! Please check your email to verify your account.'
);
setTimeout(() => { setTimeout(() => {
navigate('/auth/login'); navigate('/auth/login');
}, 2000); }, 2000);
} }
} catch (err) { } catch (err) {
console.error('[Signup] Email signup failed:', err); // console.error('[Signup] Email signup failed:', err);
setError((err as Error).message || 'Signup failed'); setError((err as Error).message || 'Signup failed');
setIsEmailLoading(false); setIsEmailLoading(false);
} }
@@ -109,20 +118,20 @@ export default function SignupPage() {
const handleGithubLogin = async () => { const handleGithubLogin = async () => {
try { try {
setIsGithubLoading(true); setIsGithubLoading(true);
console.log('[Signup] Initiating GitHub OAuth...'); // console.log('[Signup] Initiating GitHub OAuth...');
const result = await signInWithGitHub(); const result = await signInWithGitHub();
console.log('[Signup] OAuth result:', result); // console.log('[Signup] OAuth result:', result);
if (result?.url) { if (result?.url) {
console.log('[Signup] Redirecting to GitHub OAuth:', result.url); // console.log('[Signup] Redirecting to GitHub OAuth:', result.url);
globalThis.location.href = result.url; globalThis.location.href = result.url;
} else { } else {
console.error('[Signup] No OAuth URL returned'); // console.error('[Signup] No OAuth URL returned');
setIsGithubLoading(false); setIsGithubLoading(false);
} }
} catch (error) { } catch (error) {
console.error('[Signup] GitHub login failed:', error); // console.error('[Signup] GitHub login failed:', error);
setIsGithubLoading(false); setIsGithubLoading(false);
} }
}; };
@@ -130,13 +139,19 @@ export default function SignupPage() {
return ( return (
<div className="flex justify-center items-center min-h-screen bg-gray-50 p-4"> <div className="flex justify-center items-center min-h-screen bg-gray-50 p-4">
<div className="bg-white w-full max-w-md p-8 rounded-2xl shadow-lg border border-gray-200"> <div className="bg-white w-full max-w-md p-8 rounded-2xl shadow-lg border border-gray-200">
<button
onClick={() => navigate('/')}
className="cursor-pointer text-primary-500 hover:text-primary-600 text-base font-sans flex items-center mb-6"
>
<Icon icon="ic:baseline-chevron-left" width="24" height="24" />
Back to Homepage
</button>
<div className="text-center mb-8"> <div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-2"> <h2 className="text-3xl font-bold text-gray-900 mb-2">
Create Account Create Account
</h2> </h2>
<p className="text-gray-600"> <p className="text-gray-600">Join the hackathon community</p>
Join the hackathon community
</p>
</div> </div>
{error && ( {error && (
@@ -147,7 +162,10 @@ export default function SignupPage() {
<form onSubmit={handleEmailSignup} className="space-y-4"> <form onSubmit={handleEmailSignup} className="space-y-4">
<div> <div>
<label htmlFor="fullname" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="fullname"
className="block text-sm font-medium text-gray-700 mb-1"
>
Full Name Full Name
</label> </label>
<input <input
@@ -163,7 +181,10 @@ export default function SignupPage() {
</div> </div>
<div> <div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="email"
className="block text-sm font-medium text-gray-700 mb-1"
>
Email Email
</label> </label>
<input <input
@@ -179,7 +200,10 @@ export default function SignupPage() {
</div> </div>
<div> <div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="password"
className="block text-sm font-medium text-gray-700 mb-1"
>
Password Password
</label> </label>
<input <input
@@ -195,7 +219,10 @@ export default function SignupPage() {
</div> </div>
<div> <div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="confirmPassword"
className="block text-sm font-medium text-gray-700 mb-1"
>
Confirm Password Confirm Password
</label> </label>
<input <input
@@ -213,7 +240,7 @@ export default function SignupPage() {
<button <button
type="submit" type="submit"
disabled={isEmailLoading} disabled={isEmailLoading}
className="w-full py-3 bg-primary-600 text-white rounded-lg font-semibold hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors" className="w-full py-3 bg-primary-600 text-white rounded-lg font-semibold hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors cursor-pointer"
> >
{isEmailLoading ? 'Creating account...' : 'Create Account'} {isEmailLoading ? 'Creating account...' : 'Create Account'}
</button> </button>
@@ -229,7 +256,7 @@ export default function SignupPage() {
onClick={handleGithubLogin} onClick={handleGithubLogin}
disabled={isGithubLoading} disabled={isGithubLoading}
type="button" type="button"
className="w-full py-3 flex items-center justify-center gap-2 bg-gray-100 border border-gray-300 rounded-lg font-semibold hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:bg-gray-100 disabled:cursor-not-allowed transition-colors" className="w-full py-3 flex items-center justify-center gap-2 bg-gray-100 border border-gray-300 rounded-lg font-semibold hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:bg-gray-100 disabled:cursor-not-allowed transition-colors cursor-pointer"
> >
<GithubOutlined className="text-xl" /> <GithubOutlined className="text-xl" />
<span> <span>
@@ -240,7 +267,10 @@ export default function SignupPage() {
<div className="mt-6 text-center"> <div className="mt-6 text-center">
<p className="text-gray-600 text-sm"> <p className="text-gray-600 text-sm">
Already have an account?{' '} Already have an account?{' '}
<a href="/auth/login" className="text-primary-600 hover:text-primary-700 font-semibold"> <a
href="/auth/login"
className="text-primary-600 hover:text-primary-700 font-semibold"
>
Sign in Sign in
</a> </a>
</p> </p>