Add profile settings page, mentor management, and S3 email logos
- Add universal /settings/profile page accessible to all roles with avatar upload, bio, phone, password change, and account deletion - Expand updateProfile endpoint to accept bio (metadataJson), phone, and notification preference - Add deleteAccount endpoint with password confirmation - Add Profile Settings link to all nav components (admin, jury, mentor, observer) - Add /admin/mentors list page and /admin/mentors/[id] detail page for mentor management - Add Mentors nav item to admin sidebar - Update email logo URLs to S3 (s3.monaco-opc.com/public/) - Add ocean.png background image to email wrapper Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import type { Route } from 'next'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { signOut } from 'next-auth/react'
|
||||
import { cn } from '@/lib/utils'
|
||||
@@ -29,6 +30,7 @@ import {
|
||||
Handshake,
|
||||
FileText,
|
||||
CircleDot,
|
||||
GraduationCap,
|
||||
History,
|
||||
User,
|
||||
} from 'lucide-react'
|
||||
@@ -66,6 +68,11 @@ const navigation = [
|
||||
href: '/admin/users' as const,
|
||||
icon: Users,
|
||||
},
|
||||
{
|
||||
name: 'Mentors',
|
||||
href: '/admin/mentors' as const,
|
||||
icon: GraduationCap,
|
||||
},
|
||||
{
|
||||
name: 'Reports',
|
||||
href: '/admin/reports' as const,
|
||||
@@ -176,7 +183,7 @@ export function AdminSidebar({ user }: AdminSidebarProps) {
|
||||
return (
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
href={item.href as Route}
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className={cn(
|
||||
'group flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-150',
|
||||
@@ -278,7 +285,7 @@ export function AdminSidebar({ user }: AdminSidebarProps) {
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href="/admin/settings"
|
||||
href={"/settings/profile" as Route}
|
||||
className="flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2"
|
||||
>
|
||||
<User className="h-4 w-4 text-muted-foreground" />
|
||||
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { BookOpen, ClipboardList, Home, LogOut, Menu, User, X } from 'lucide-react'
|
||||
import type { Route } from 'next'
|
||||
import { BookOpen, ClipboardList, Home, LogOut, Menu, Settings, User, X } from 'lucide-react'
|
||||
import { getInitials } from '@/lib/utils'
|
||||
import { Logo } from '@/components/shared/logo'
|
||||
|
||||
@@ -105,6 +106,13 @@ export function JuryNav({ user }: JuryNavProps) {
|
||||
{user.email}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={"/settings/profile" as Route} className="flex cursor-pointer items-center">
|
||||
<Settings className="mr-2 h-4 w-4" />
|
||||
Profile Settings
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => signOut({ callbackUrl: '/login' })}
|
||||
className="text-destructive focus:text-destructive"
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { BookOpen, Home, LogOut, Menu, User, Users, X } from 'lucide-react'
|
||||
import { BookOpen, Home, LogOut, Menu, Settings, User, Users, X } from 'lucide-react'
|
||||
import { getInitials } from '@/lib/utils'
|
||||
import { Logo } from '@/components/shared/logo'
|
||||
|
||||
@@ -106,6 +106,13 @@ export function MentorNav({ user }: MentorNavProps) {
|
||||
{user.email}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={"/settings/profile" as Route} className="flex cursor-pointer items-center">
|
||||
<Settings className="mr-2 h-4 w-4" />
|
||||
Profile Settings
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => signOut({ callbackUrl: '/login' })}
|
||||
className="text-destructive focus:text-destructive"
|
||||
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Home, BarChart3, Menu, X, LogOut, Eye } from 'lucide-react'
|
||||
import type { Route } from 'next'
|
||||
import { Home, BarChart3, Menu, X, LogOut, Eye, Settings } from 'lucide-react'
|
||||
import { getInitials } from '@/lib/utils'
|
||||
import { Logo } from '@/components/shared/logo'
|
||||
|
||||
@@ -92,6 +93,13 @@ export function ObserverNav({ user }: ObserverNavProps) {
|
||||
{user.email}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={"/settings/profile" as Route} className="flex cursor-pointer items-center">
|
||||
<Settings className="mr-2 h-4 w-4" />
|
||||
Profile Settings
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => signOut({ callbackUrl: '/login' })}
|
||||
className="text-destructive focus:text-destructive"
|
||||
|
||||
Reference in New Issue
Block a user