HR-ATS-Portal/frontend/src/app/Topbar.jsx

151 lines
5.9 KiB
JavaScript

import { Link } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import Dropdown, { DropdownGroup } from '../ui/Dropdown'
import GlobalSearch from './GlobalSearch'
import { Avatar, Icon } from '../ui/primitives'
import { seedQuery, useSeedMutation } from '../data/seedQueries'
import { useToast } from '../ui/Toast'
import { useTheme } from '../theme/ThemeProvider'
import { useAuth } from '../auth/AuthContext'
function initialsFromName(name) {
const parts = String(name || '').trim().split(/\s+/).filter(Boolean)
if (!parts.length) return '?'
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase()
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase()
}
export default function Topbar({ onOpenNav, searchRef }) {
const { theme, toggleTheme } = useTheme()
const { user, signOut } = useAuth()
const { toast } = useToast()
const { data: notifications = [] } = useQuery(seedQuery('notifications'))
const { data: messages = [] } = useQuery(seedQuery('messages'))
const updateNotifications = useSeedMutation('notifications')
// App.hydrateProfile's DOM sweep is gone — the session is read directly.
const name = user?.name || 'Guest'
const email = user?.email || ''
const role = user?.role_name || user?.role || 'Member'
function markAllRead() {
updateNotifications((ns) => ns.map((n) => ({ ...n, unread: false })))
toast('All notifications marked as read', 'success')
}
return (
<header className="topbar">
<button className="icon-btn menu-toggle" onClick={onOpenNav} aria-label="Toggle menu">
<Icon name="menu" />
</button>
<GlobalSearch inputRef={searchRef} />
<div className="topbar-actions">
<button
className="icon-btn"
onClick={toggleTheme}
aria-pressed={theme === 'dark'}
title={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
aria-label={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
>
<span className="icon-sun"><Icon name="sun" /></span>
<span className="icon-moon"><Icon name="moon" /></span>
</button>
<DropdownGroup>
<Dropdown
panelClassName="dropdown-menu-wide"
trigger={({ toggle }) => (
<button className="icon-btn" onClick={toggle} title="Messages" aria-label="Messages">
<Icon name="message" />
<span className="dot dot-blue" />
</button>
)}
>
<div className="dropdown-head">Messages</div>
<div className="dd-scroll">
{messages.map((m) => (
<div key={m.id ?? m.name} className={`notif-row${m.unread ? ' unread' : ''}`}>
<Avatar name={m.name} initials={m.initials} color={m.color} />
<div className="notif-body">
<div className="notif-title">{m.name}</div>
<div className="notif-text">{m.text}</div>
<div className="notif-time">{m.time} ago</div>
</div>
</div>
))}
</div>
<div className="dropdown-foot">
<Link to="/notifications">Open inbox</Link>
</div>
</Dropdown>
<Dropdown
panelClassName="dropdown-menu-wide"
trigger={({ toggle }) => (
<button className="icon-btn" onClick={toggle} title="Notifications" aria-label="Notifications">
<Icon name="bell" />
<span className="dot dot-red" />
</button>
)}
>
<div className="dropdown-head">
Notifications
<button className="link-btn" onClick={markAllRead}>Mark all read</button>
</div>
<div className="dd-scroll">
{notifications.slice(0, 6).map((n) => (
<div key={n.id ?? n.title} className={`notif-row${n.unread ? ' unread' : ''}`}>
<span className={`notif-icn ${n.color}`}><Icon name={n.icon} /></span>
<div className="notif-body">
<div className="notif-title">{n.title}</div>
<div className="notif-text">{n.text}</div>
<div className="notif-time">{n.time}</div>
</div>
</div>
))}
</div>
<div className="dropdown-foot">
<Link to="/notifications">View all</Link>
</div>
</Dropdown>
<div className="topbar-divider" />
<Dropdown
trigger={({ toggle }) => (
<button className="profile-btn" onClick={toggle}>
<span className="avatar avatar-grad">{initialsFromName(name)}</span>
<span className="profile-meta">
<span className="profile-name">{name}</span>
<span className="profile-role">{role}</span>
</span>
<Icon name="chevron-down" className="chev" />
</button>
)}
>
<div className="dropdown-profile">
<span className="avatar avatar-grad avatar-lg">{initialsFromName(name)}</span>
<div>
<div className="dp-name">{name}</div>
<div className="dp-email">{email}</div>
</div>
</div>
<div className="dropdown-divider" />
<Link className="dropdown-link" to="/settings"><Icon name="user" />My Profile</Link>
<Link className="dropdown-link" to="/settings"><Icon name="settings" />Settings</Link>
<Link className="dropdown-link" to="/help"><Icon name="help" />Help Center</Link>
<div className="dropdown-divider" />
<button className="dropdown-link danger" onClick={signOut}>
<Icon name="logout" />Sign out
</button>
</Dropdown>
</DropdownGroup>
</div>
</header>
)
}