diff --git a/public/assets/icons/close-icon.svg b/public/assets/icons/close-icon.svg new file mode 100644 index 0000000..426dd5d --- /dev/null +++ b/public/assets/icons/close-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/icons/menu-icon.svg b/public/assets/icons/menu-icon.svg new file mode 100644 index 0000000..ac06cf4 --- /dev/null +++ b/public/assets/icons/menu-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/auth.ts b/src/auth.ts index 7dc9964..8494396 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -107,6 +107,9 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ async jwt({ token, user }) { if (user) { token.id = user.id; + token.name = user.name; + token.email = user.email; + token.picture = user.image; token.role = (user as any).role; token.accessToken = (user as any).accessToken; token.refreshToken = (user as any).refreshToken; @@ -117,6 +120,9 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ async session({ session, token }) { if (session.user) { (session.user as any).id = token.id; + session.user.name = token.name as string; + session.user.email = token.email as string; + session.user.image = token.picture as string; (session.user as any).role = token.role; (session.user as any).accessToken = token.accessToken; (session.user as any).refreshToken = token.refreshToken; diff --git a/src/components/home/HeroSection.tsx b/src/components/home/HeroSection.tsx index 830be28..9fec360 100644 --- a/src/components/home/HeroSection.tsx +++ b/src/components/home/HeroSection.tsx @@ -15,6 +15,7 @@ export function HeroSection() { location: '', }); const [openDropdown, setOpenDropdown] = useState(null); + const [validationError, setValidationError] = useState(null); // Fetch agent types on mount useEffect(() => { @@ -30,6 +31,19 @@ export function HeroSection() { }, []); const handleSearch = () => { + // Validate that at least one field has a value + const hasType = searchParams.type.trim() !== ''; + const hasName = searchParams.name.trim() !== ''; + const hasLocation = searchParams.location.trim() !== ''; + + if (!hasType && !hasName && !hasLocation) { + setValidationError('Please enter at least one search criteria (Type, Name/Keyword, or Location)'); + return; + } + + // Clear any previous validation error + setValidationError(null); + const params = new URLSearchParams(); if (searchParams.type) params.set('type', searchParams.type); if (searchParams.name) params.set('name', searchParams.name); @@ -48,6 +62,12 @@ export function HeroSection() { const selectType = (value: string) => { setSearchParams({ ...searchParams, type: value }); setOpenDropdown(null); + setValidationError(null); // Clear error when user selects a type + }; + + const handleInputChange = (field: 'name' | 'location', value: string) => { + setSearchParams({ ...searchParams, [field]: value }); + setValidationError(null); // Clear error when user starts typing }; return ( @@ -116,7 +136,7 @@ export function HeroSection() { type="text" placeholder="Name or Keyword" value={searchParams.name} - onChange={(e) => setSearchParams({ ...searchParams, name: e.target.value })} + onChange={(e) => handleInputChange('name', e.target.value)} className="w-full h-[42px] px-4 rounded-[7px] border border-[#00293d]/10 bg-white font-serif text-sm text-[#00293d] placeholder:text-[#00293d] focus:outline-none" /> @@ -127,7 +147,7 @@ export function HeroSection() { type="text" placeholder="Location" value={searchParams.location} - onChange={(e) => setSearchParams({ ...searchParams, location: e.target.value })} + onChange={(e) => handleInputChange('location', e.target.value)} className="w-full h-[42px] px-4 rounded-[7px] border border-[#00293d]/10 bg-white font-serif text-sm text-[#00293d] placeholder:text-[#00293d] focus:outline-none" /> @@ -146,6 +166,15 @@ export function HeroSection() { + {/* Validation Error Message */} + {validationError && ( +
+

+ {validationError} +

+
+ )} + {/* Bottom Section - pushed to bottom */} diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 4d09ad8..3bb82b2 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -3,79 +3,171 @@ import Link from 'next/link'; import Image from 'next/image'; import { useSession } from 'next-auth/react'; -import { Button } from '../ui/Button'; +import { useState } from 'react'; export function Header() { const { data: session } = useSession(); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + + // Get first name from session + const firstName = session?.user?.name?.split(' ')[0] || 'User'; return ( -
-
+
+
{/* Logo */} - + RE-Quest - {/* Navigation */} - + {session ? ( + /* ============ AUTHENTICATED HEADER ============ */ + <> + {/* Navigation - Desktop */} + - {/* Right Side */} -
- {session ? ( - <> + {/* Right Side - Authenticated */} +
{/* Notification Bell */} - - {/* Profile */} - - - - + + Notifications + {/* Red notification dot */} + - - ) : ( -
- - - - - - -
- )} - {/* Mobile Menu Button */} - -
+ {/* Profile Section */} + + {/* Profile Avatar */} +
+ {session.user?.image ? ( + Profile + ) : ( + Profile + )} +
+ {/* Greeting Text */} +
+ + Hi {firstName} ! + + + Welcome back ! + +
+ + + {/* Mobile Menu Button */} + +
+ + ) : ( + /* ============ NON-AUTHENTICATED HEADER ============ */ + /* Simple: Just logo + Get Started button */ +
+ + Get Started + +
+ )}
+ + {/* Mobile Menu Dropdown - Authenticated Only */} + {session && mobileMenuOpen && ( +
+ +
+ )}
);