Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 98 additions & 19 deletions app/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
'use client'
import { useState } from 'react'
import { useState, useRef, useEffect } from 'react'
import Link from 'next/link'
import Image from 'next/image'
import { usePathname } from 'next/navigation'
import { Menu, X } from 'lucide-react'
import { Menu, X, ChevronDown } from 'lucide-react'
import { useSession, signOut } from 'next-auth/react'

const Navbar = () => {
const pathname = usePathname()
const [isOpen, setIsOpen] = useState(false)
const [isProfileOpen, setIsProfileOpen] = useState(false)
const { data: session } = useSession()
const profileRef = useRef<HTMLDivElement>(null)

// Close profile dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (profileRef.current && !profileRef.current.contains(event.target as Node)) {
setIsProfileOpen(false);
}
};

document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);

const linkClasses = (path: string) =>
`px-4 py-2 text-base font-normal transition-colors duration-200 ${
`px-4 py-2 text-base font-medium transition-all duration-300 ${
pathname === path
? 'underline decoration-2 underline-offset-4 text-black'
: 'text-black hover:text-afh-orange'
}`
? 'underline decoration-2 underline-offset-4 text-afh-blue'
: 'text-afh-blue hover:text-afh-orange'
}`;

return (
<nav className="sticky top-0 z-50 bg-white">
<nav className="sticky top-0 z-50 bg-white shadow-sm">
<div className="max-w-[1440px] mx-auto px-6 sm:px-8 lg:px-12 xl:px-16 py-4">
<div className="flex justify-between items-center">
{/* Logo */}
Expand All @@ -40,16 +56,58 @@ const Navbar = () => {
<Link href="/upload" className={linkClasses('/upload')}>
Upload My Work
</Link>
<Link href="/login" className={linkClasses('/login')}>
Login
</Link>
{/* Render Login if not logged in, otherwise render profile picture with dropdown */}
{session ? (
<div className="relative" ref={profileRef}>
<button
onClick={() => setIsProfileOpen(!isProfileOpen)}
className="flex items-center space-x-1 focus:outline-none rounded-full"
aria-label="Profile menu"
>
<Image
src={session?.user?.profile?.profile_image_url || "/imgs/default-profile.png"}
alt="Profile Picture"
width={32}
height={32}
className="h-8 w-8 rounded-full object-cover"
/>
<ChevronDown size={16} className="text-afh-blue/70" />
</button>

{/* Dropdown Menu */}
{isProfileOpen && (
<div className="absolute left-1/2 -translate-x-1/2 mt-2 w-auto min-w-[120px] bg-white rounded-lg shadow-afh-lg py-1 border border-afh-blue/10 z-50">
<Link
href="/user-page"
className="block px-4 py-2 text-sm font-medium text-afh-blue hover:bg-afh-orange/10 hover:text-afh-orange transition-all duration-150 text-center whitespace-nowrap"
onClick={() => setIsProfileOpen(false)}
>
Go to Profile
</Link>
<button
onClick={() => {
setIsProfileOpen(false);
signOut({ callbackUrl: '/login' });
}}
className="block w-full px-4 py-2 text-sm font-medium text-afh-blue hover:bg-afh-orange/10 hover:text-afh-orange transition-all duration-150 text-center whitespace-nowrap"
>
Sign Out
</button>
</div>
)}
</div>
) : (
<Link href="/login" className={linkClasses("/login")}>
Login
</Link>
)}
</div>

{/* Mobile Menu Button */}
<div className="md:hidden">
<button
onClick={() => setIsOpen(!isOpen)}
className="p-2 rounded-md text-black hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-afh-orange"
className="p-2 rounded-md text-afh-blue hover:bg-afh-orange/10 hover:text-afh-orange focus:outline-none transition-all duration-300"
aria-label="Toggle menu"
>
{isOpen ? <X size={24} /> : <Menu size={24} />}
Expand All @@ -60,7 +118,7 @@ const Navbar = () => {

{/* Mobile Dropdown Menu */}
{isOpen && (
<div className="md:hidden border-t border-gray-200 bg-white">
<div className="md:hidden border-t border-afh-blue/10 bg-white shadow-afh">
<div className="px-4 pt-2 pb-3 space-y-1">
<Link
href="/"
Expand All @@ -76,13 +134,34 @@ const Navbar = () => {
>
Upload My Work
</Link>
<Link
href="/login"
className={`block ${linkClasses('/login')}`}
onClick={() => setIsOpen(false)}
>
Login
</Link>
{session ? (
<>
<Link
href="/user-page"
className={`block ${linkClasses("/user-page")}`}
onClick={() => setIsOpen(false)}
>
Go to Profile
</Link>
<button
onClick={() => {
setIsOpen(false);
signOut({ callbackUrl: '/login' });
}}
className="block w-full text-left px-4 py-2 text-base font-medium text-afh-blue hover:text-afh-orange transition-all duration-300"
>
Sign Out
</button>
</>
) : (
<Link
href="/login"
className={`block ${linkClasses("/login")}`}
onClick={() => setIsOpen(false)}
>
Login
</Link>
)}
</div>
</div>
)}
Expand Down
2 changes: 1 addition & 1 deletion app/upload/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ export default function UploadPage() {
// Reset form
setTimeout(() => {
if (session) {
router.push('/user-portal')
router.push('/user-page')
} else {
// For guests, reset form and show success message
setFormData({
Expand Down
2 changes: 1 addition & 1 deletion app/user-page/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const initialProfile = {
year: '2029',
school: 'Tufts University',
instagram: 'Username01',
avatar: '/imgs/user-stock.png',
avatar: '/imgs/default-profile.png',
banner: '/imgs/profile-banner-temp.png',
}
const publicArtwork = [
Expand Down
2 changes: 1 addition & 1 deletion components/login/LoginBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const LoginBody = () => {
if (session?.user?.role === 'ADMIN') {
router.push('/admin')
} else {
router.push('/user-portal')
router.push('/user-page')
}
}
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion components/signup/SignUpBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const SignUpBody = () => {
)
} else {
// Success! Redirect to user portal
router.push('/user-portal')
router.push('/user-page')
}
} catch (error) {
console.error('Signup error:', error)
Expand Down
10 changes: 10 additions & 0 deletions lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import CredentialsProvider from 'next-auth/providers/credentials'
import { prisma } from '@/lib/prisma'
import { Role } from '@prisma/client'
import bcrypt from 'bcryptjs'
import { Role } from '@prisma/client'

export const authOptions: AuthOptions = {
providers: [
Expand Down Expand Up @@ -40,6 +41,13 @@ export const authOptions: AuthOptions = {
email: user.email,
username: user.username,
role: user.role,
profile: user.profile ? {
id: user.profile.id,
user_id: user.profile.user_id,
display_name: user.profile.display_name,
bio: user.profile.bio,
profile_image_url: user.profile.profile_image_url,
} : undefined,
}
},
}),
Expand All @@ -57,6 +65,7 @@ export const authOptions: AuthOptions = {
token.id = user.id
token.role = user.role
token.username = user.username
token.profile = user.profile
}
return token
},
Expand All @@ -66,6 +75,7 @@ export const authOptions: AuthOptions = {
session.user.id = token.id as string
session.user.role = token.role as Role
session.user.username = token.username as string
session.user.profile = token.profile as typeof token.profile
}
return session
},
Expand Down
1 change: 0 additions & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ model Profile {
display_name String? @db.VarChar(255)
bio String?
profile_image_url String?
department String? @db.VarChar(100)
created_at DateTime @default(now())
updated_at DateTime @updatedAt

Expand Down
Binary file added public/imgs/default-profile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/imgs/user-stock.png
Binary file not shown.
21 changes: 21 additions & 0 deletions types/next-auth.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ declare module 'next-auth' {
id: string
role: Role
username: string
profile?: {
id: string
user_id: string
display_name?: string | null
bio?: string | null
profile_image_url?: string | null
}
} & DefaultSession['user']
}

Expand All @@ -19,6 +26,13 @@ declare module 'next-auth' {
email: string
username: string
role: Role
profile?: {
id: string
user_id: string
display_name?: string | null
bio?: string | null
profile_image_url?: string | null
}
}
}

Expand All @@ -27,5 +41,12 @@ declare module 'next-auth/jwt' {
id: string
role: Role
username: string
profile?: {
id: string
user_id: string
display_name?: string | null
bio?: string | null
profile_image_url?: string | null
}
}
}