LMS-BGN/src/layouts/TopBar.tsx

210 lines
8.2 KiB
TypeScript

'use client';
import React, { useState } from 'react';
import {
MagnifyingGlassIcon,
BellIcon,
Bars3Icon,
ChevronDownIcon
} from '@heroicons/react/24/outline';
import { cn } from '@/utils/cn';
import { useAuth } from '@/contexts/AuthContext';
import Link from 'next/link';
interface TopBarProps {
onMenuClick: () => void;
sidebarCollapsed: boolean;
}
export function TopBar({ onMenuClick, sidebarCollapsed }: TopBarProps) {
const { user, logout } = useAuth();
const [searchQuery, setSearchQuery] = useState('');
const [showNotifications, setShowNotifications] = useState(false);
const [showUserMenu, setShowUserMenu] = useState(false);
const getUserInitials = (name: string) => {
return name.split(' ').map(n => n[0]).join('').toUpperCase();
};
const notifications = [
{
id: 1,
title: 'Tugas Baru Tersedia',
message: 'HACCP - Hazard Analysis Critical Control Point',
time: '5 menit yang lalu',
isRead: false
},
{
id: 2,
title: 'Ujian Akan Dimulai',
message: 'SLHS - Sanitasi Lingkungan Hidup Sehat dalam 30 menit',
time: '25 menit yang lalu',
isRead: false
},
{
id: 3,
title: 'Sertifikat Siap Diunduh',
message: 'Sertifikat Halal - Sistem Jaminan Halal telah tersedia',
time: '2 jam yang lalu',
isRead: true
}
];
const unreadCount = notifications.filter(n => !n.isRead).length;
return (
<header className="bg-white border-b border-gray-200 h-16">
<div className="flex items-center justify-between h-full px-4 sm:px-6 lg:px-8">
{/* Left side */}
<div className="flex items-center space-x-4">
{/* Mobile menu button */}
<button
onClick={onMenuClick}
className="lg:hidden rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500"
>
<Bars3Icon className="h-6 w-6" />
</button>
{/* Search bar */}
<div className="relative flex-1 max-w-lg">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<MagnifyingGlassIcon className="h-5 w-5 text-gray-400" />
</div>
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
placeholder="Cari kursus, materi, atau instruktur..."
/>
</div>
</div>
{/* Right side */}
<div className="flex items-center space-x-4">
{/* Notifications */}
<div className="relative">
<button
onClick={() => setShowNotifications(!showNotifications)}
className="relative rounded-full p-2 text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-primary-500"
>
<BellIcon className="h-6 w-6" />
{unreadCount > 0 && (
<span className="absolute -top-1 -right-1 h-5 w-5 rounded-full bg-red-500 flex items-center justify-center">
<span className="text-xs font-medium text-white">{unreadCount}</span>
</span>
)}
</button>
{/* Notifications dropdown */}
{showNotifications && (
<div className="absolute right-0 mt-2 w-80 bg-white rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 z-50">
<div className="p-4">
<h3 className="text-sm font-medium text-gray-900 mb-3">Notifikasi</h3>
<div className="space-y-3 max-h-64 overflow-y-auto">
{notifications.map((notification) => (
<div
key={notification.id}
className={cn(
'p-3 rounded-lg border cursor-pointer hover:bg-gray-50',
notification.isRead ? 'bg-white border-gray-200' : 'bg-blue-50 border-blue-200'
)}
>
<div className="flex justify-between items-start">
<div className="flex-1">
<p className="text-sm font-medium text-gray-900">
{notification.title}
</p>
<p className="text-sm text-gray-600 mt-1">
{notification.message}
</p>
<p className="text-xs text-gray-400 mt-2">
{notification.time}
</p>
</div>
{!notification.isRead && (
<div className="w-2 h-2 bg-blue-500 rounded-full ml-2 mt-1"></div>
)}
</div>
</div>
))}
</div>
<div className="mt-3 pt-3 border-t border-gray-200">
<button className="text-sm text-primary-600 hover:text-primary-700 font-medium">
Lihat Semua Notifikasi
</button>
</div>
</div>
</div>
)}
</div>
{/* User menu */}
<div className="relative">
<button
onClick={() => setShowUserMenu(!showUserMenu)}
className="flex items-center space-x-3 rounded-full p-2 text-sm hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-primary-500"
>
<div className="h-8 w-8 rounded-full bg-gradient-to-r from-primary-500 to-secondary-500 flex items-center justify-center">
<span className="text-sm font-medium text-white">
{user ? getUserInitials(user.name) : 'U'}
</span>
</div>
<div className="hidden md:block text-left">
<p className="text-sm font-medium text-gray-700">{user?.name || 'User'}</p>
<p className="text-xs text-gray-500 capitalize">{user?.role || 'User'}</p>
</div>
<ChevronDownIcon className="h-4 w-4 text-gray-400" />
</button>
{/* User dropdown */}
{showUserMenu && (
<div className="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 z-50">
<div className="py-1">
<Link
href="/profile"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
onClick={() => setShowUserMenu(false)}
>
Profil Saya
</Link>
<Link
href="/settings"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
onClick={() => setShowUserMenu(false)}
>
Pengaturan
</Link>
<Link
href="/help"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
onClick={() => setShowUserMenu(false)}
>
Bantuan
</Link>
<div className="border-t border-gray-100">
<button
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
onClick={async () => {
try {
setShowUserMenu(false);
logout();
} catch (error) {
console.error('Logout button error:', error);
}
}}
>
Keluar
</button>
</div>
</div>
</div>
)}
</div>
</div>
</div>
</header>
);
}
export default TopBar;