From 7da7c79d20c15091ca2290396dbac14e7f350ccd Mon Sep 17 00:00:00 2001 From: William Date: Wed, 11 Feb 2026 16:46:01 +0000 Subject: [PATCH] Switch auth from phone to email and fix home page for authenticated users - Change signIn/signUp to use email instead of phone (Supabase email auth) - Add Email/Phone tab switcher on login and register pages - Phone tab shows "coming soon" placeholder - Home page now shows role cards when logged in, sign-in buttons when not - Add sign out button to authenticated home page header Co-Authored-By: Claude Opus 4.6 --- src/app/login/page.tsx | 143 ++++++++++++++++--------- src/app/page.tsx | 61 ++++++++++- src/app/register/page.tsx | 217 +++++++++++++++++++++++--------------- src/hooks/use-auth.tsx | 26 ++--- 4 files changed, 296 insertions(+), 151 deletions(-) diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index e1825ef..5ecf42b 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -3,18 +3,21 @@ import { Suspense, useState } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import Link from 'next/link'; -import { Phone, Lock, Loader2 } from 'lucide-react'; +import { Mail, Phone, Lock, Loader2 } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { useAuth } from '@/hooks/use-auth'; import { DEMO_MODE } from '@/lib/config/demo'; +type AuthMethod = 'email' | 'phone'; + function LoginForm() { const router = useRouter(); const searchParams = useSearchParams(); const { signIn } = useAuth(); - const [phone, setPhone] = useState(''); + const [method, setMethod] = useState('email'); + const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); @@ -27,12 +30,12 @@ function LoginForm() { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - if (!phone.trim() || !password) return; + if (!email.trim() || !password) return; setLoading(true); setError(null); - const { error: signInError } = await signIn(phone.trim(), password); + const { error: signInError } = await signIn(email.trim(), password); if (signInError) { setError(signInError); @@ -53,58 +56,98 @@ function LoginForm() {

-
-
- - setPhone(e.target.value)} - className="h-12" - required - /> -
+ {/* Method Tabs */} +
+ + +
-
- - setPassword(e.target.value)} - className="h-12" - required - /> + {method === 'phone' ? ( +
+ +

+ Phone login coming soon +

+

+ Use email to sign in for now +

+ ) : ( + +
+ + setEmail(e.target.value)} + className="h-12" + required + /> +
- {error && ( -
- {error} +
+ + setPassword(e.target.value)} + className="h-12" + required + />
- )} - - + + + + )}

Don't have an account?{' '} diff --git a/src/app/page.tsx b/src/app/page.tsx index 139e276..bb3b838 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,8 +1,11 @@ +'use client'; + import Link from "next/link"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; -import { ShoppingBag, Store, Truck } from "lucide-react"; +import { ShoppingBag, Store, Truck, LogOut } from "lucide-react"; import { DEMO_MODE } from "@/lib/config/demo"; +import { useAuth } from "@/hooks/use-auth"; const roles = [ { @@ -35,7 +38,63 @@ const roles = [ ]; export default function Home() { + const { user, loading, signOut } = useAuth(); + if (!DEMO_MODE) { + // Authenticated user: show role-based navigation + if (user && !loading) { + return ( +

+
+
+

Project Firefly

+
+ {user.name} + +
+
+
+ +
+
+
+

+ Welcome back, {user.name} +

+

+ Select a role to continue +

+
+ +
+ {roles.map((role) => ( + + + +
+ +
+ {role.name} +
+ + + {role.description} + + +
+ + ))} +
+
+
+
+ ); + } + + // Not authenticated: show sign-in / create account return (
diff --git a/src/app/register/page.tsx b/src/app/register/page.tsx index b7eebf1..ba1c657 100644 --- a/src/app/register/page.tsx +++ b/src/app/register/page.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; -import { Phone, Lock, User, Loader2, ShoppingBag, Store, Truck } from 'lucide-react'; +import { Mail, Phone, Lock, User, Loader2, ShoppingBag, Store, Truck } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -11,6 +11,8 @@ import { useAuth } from '@/hooks/use-auth'; import { DEMO_MODE } from '@/lib/config/demo'; import type { UserRole } from '@/types'; +type AuthMethod = 'email' | 'phone'; + const roleOptions = [ { value: 'customer' as UserRole, @@ -41,8 +43,9 @@ const roleOptions = [ export default function RegisterPage() { const router = useRouter(); const { signUp } = useAuth(); + const [method, setMethod] = useState('email'); const [name, setName] = useState(''); - const [phone, setPhone] = useState(''); + const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [role, setRole] = useState('customer'); const [error, setError] = useState(null); @@ -56,7 +59,7 @@ export default function RegisterPage() { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - if (!name.trim() || !phone.trim() || !password) return; + if (!name.trim() || !email.trim() || !password) return; if (password.length < 6) { setError('Password must be at least 6 characters'); @@ -66,7 +69,7 @@ export default function RegisterPage() { setLoading(true); setError(null); - const { error: signUpError } = await signUp(phone.trim(), password, name.trim(), role); + const { error: signUpError } = await signUp(email.trim(), password, name.trim(), role); if (signUpError) { setError(signUpError); @@ -84,97 +87,137 @@ export default function RegisterPage() { Join the Firefly community -
-
- - setName(e.target.value)} - className="h-12" - required - /> -
- -
- - setPhone(e.target.value)} - className="h-12" - required - /> + {/* Method Tabs */} +
+ + +
+ + {method === 'phone' ? ( +
+ +

+ Phone registration coming soon +

+

+ Use email to create your account for now +

+ ) : ( + +
+ + setName(e.target.value)} + className="h-12" + required + /> +
-
- - setPassword(e.target.value)} - className="h-12" - minLength={6} - required - /> -
+
+ + setEmail(e.target.value)} + className="h-12" + required + /> +
- {/* Role Selection */} -
- -
- {roleOptions.map((opt) => ( - - ))} +
+ + setPassword(e.target.value)} + className="h-12" + minLength={6} + required + />
-

- {roleOptions.find((o) => o.value === role)?.description} -

-
- {error && ( -
- {error} + {/* Role Selection */} +
+ +
+ {roleOptions.map((opt) => ( + + ))} +
+

+ {roleOptions.find((o) => o.value === role)?.description} +

- )} - - + + + + )}

Already have an account?{' '} diff --git a/src/hooks/use-auth.tsx b/src/hooks/use-auth.tsx index 1ecbc69..9a916f8 100644 --- a/src/hooks/use-auth.tsx +++ b/src/hooks/use-auth.tsx @@ -18,16 +18,16 @@ import type { UserRole } from '@/types'; interface AuthContextType { user: { id: string; - phone: string; + email: string; name: string; roles: UserRole[]; default_role?: UserRole; } | null; session: Session | null; loading: boolean; - signIn: (phone: string, password: string) => Promise<{ error: string | null }>; + signIn: (email: string, password: string) => Promise<{ error: string | null }>; signUp: ( - phone: string, + email: string, password: string, name: string, role: UserRole @@ -44,21 +44,21 @@ const AuthContext = createContext(undefined); const DEMO_USERS = { customer: { id: '00000000-0000-0000-0000-000000000001', - phone: '555-0101', + email: 'customer@demo.firefly', name: 'Demo Customer', roles: ['customer'] as UserRole[], default_role: 'customer' as UserRole, }, vendor: { id: '00000000-0000-0000-0000-000000000002', - phone: '555-0102', + email: 'maria@demo.firefly', name: 'Maria Garcia', roles: ['vendor'] as UserRole[], default_role: 'vendor' as UserRole, }, delivery: { id: '00000000-0000-0000-0000-000000000003', - phone: '555-0103', + email: 'alex@demo.firefly', name: 'Alex Johnson', roles: ['delivery'] as UserRole[], default_role: 'delivery' as UserRole, @@ -129,7 +129,7 @@ function ProductionAuthProvider({ children }: { children: ReactNode }) { if (profile) { setUser({ id: profile.id, - phone: profile.phone || authUser.phone || '', + email: profile.email || authUser.email || '', name: profile.name, roles: profile.roles || ['customer'], default_role: profile.default_role, @@ -190,10 +190,10 @@ function ProductionAuthProvider({ children }: { children: ReactNode }) { }, [supabase, fetchUserProfile]); const signIn = useCallback( - async (phone: string, password: string) => { + async (email: string, password: string) => { if (!supabase) return { error: 'Not initialized' }; const { error } = await supabase.auth.signInWithPassword({ - phone, + email, password, }); if (error) return { error: error.message }; @@ -203,10 +203,10 @@ function ProductionAuthProvider({ children }: { children: ReactNode }) { ); const signUp = useCallback( - async (phone: string, password: string, name: string, role: UserRole) => { + async (email: string, password: string, name: string, role: UserRole) => { if (!supabase) return { error: 'Not initialized' }; const { data, error } = await supabase.auth.signUp({ - phone, + email, password, }); if (error) return { error: error.message }; @@ -215,9 +215,9 @@ function ProductionAuthProvider({ children }: { children: ReactNode }) { // Create public.users record const { error: profileError } = await supabase.from('users').insert({ id: data.user.id, - email: '', + email, name, - phone, + phone: '', roles: [role], default_role: role, });