'use client' import Link from 'next/link' import { Bell, Search, Settings } from 'lucide-react' import { usePathname, useRouter } from 'next/navigation' import { useState, useEffect } from 'react' import { io } from 'socket.io-client' import { EMPLOYEE_PROFILE_KEY, apiFetch } from '@/lib/api' import { useDashboardI18n } from '@/components/I18nProvider' import { toDashboardAppPath } from '@/lib/dashboardPaths' function computeInitials(name: string): string { const parts = name.trim().split(/\s+/).filter(Boolean) if (parts.length === 0) return 'U' if (parts.length === 1) return parts[0].slice(0, 1).toUpperCase() return `${parts[0].slice(0, 1)}${parts[1].slice(0, 1)}`.toUpperCase() } function resolveSocketOrigin(): string | null { if (typeof window === 'undefined') return null const configuredApiUrl = process.env.NEXT_PUBLIC_API_URL if (configuredApiUrl) { try { return new URL(configuredApiUrl, window.location.origin).origin } catch {} } if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { return 'http://localhost:4000' } return window.location.origin } export default function TopBar() { const { dict } = useDashboardI18n() const pathname = usePathname() const appPath = toDashboardAppPath(pathname) const router = useRouter() const [unreadCount, setUnreadCount] = useState(0) const [showNotifs, setShowNotifs] = useState(false) const [userInitials, setUserInitials] = useState('W') const [notifications, setNotifications] = useState>([]) const [loadingNotifs, setLoadingNotifs] = useState(false) const [socketEnabled, setSocketEnabled] = useState(false) const [mounted, setMounted] = useState(false) useEffect(() => { setMounted(true) }, []) const title = (() => { if (!mounted) return dict.titles['/'] if (dict.titles[appPath]) return dict.titles[appPath] if (appPath.startsWith('/contracts/')) return dict.titles['/contracts'] ?? dict.titles['/'] if (appPath.startsWith('/reservations/')) return dict.titles['/reservations'] ?? dict.titles['/'] return dict.titles['/'] })() async function refreshUnreadCount() { try { const data = await apiFetch<{ unread: number }>('/notifications/unread-count') setUnreadCount(data.unread) setSocketEnabled(true) } catch { setUnreadCount(0) setSocketEnabled(false) } } useEffect(() => { refreshUnreadCount() }, [pathname]) useEffect(() => { function onNotificationsUpdated() { refreshUnreadCount() } window.addEventListener('notifications:updated', onNotificationsUpdated) return () => window.removeEventListener('notifications:updated', onNotificationsUpdated) }, []) useEffect(() => { if (!socketEnabled) return const socketOrigin = resolveSocketOrigin() if (!socketOrigin) return const socket = io(socketOrigin, { autoConnect: false, withCredentials: true, transports: ['polling', 'websocket'], }) const handleNotification = () => { setUnreadCount((prev) => prev + 1) window.dispatchEvent(new Event('notifications:updated')) } socket.on('notification', handleNotification) const connectTimer = window.setTimeout(() => { socket.connect() }, 0) return () => { window.clearTimeout(connectTimer) socket.off('notification', handleNotification) socket.disconnect() } }, [socketEnabled]) useEffect(() => { if (!showNotifs) return let cancelled = false setLoadingNotifs(true) apiFetch>('/notifications/company') .then((data) => { if (cancelled) return setNotifications(data.slice(0, 8)) }) .catch(() => { if (cancelled) return setNotifications([]) }) .finally(() => { if (cancelled) return setLoadingNotifs(false) }) return () => { cancelled = true } }, [showNotifs]) useEffect(() => { setShowNotifs(false) }, [pathname]) useEffect(() => { const cached = window.localStorage.getItem(EMPLOYEE_PROFILE_KEY) if (cached) { try { const profile = JSON.parse(cached) as { email?: string; firstName?: string; lastName?: string } const fullName = `${profile.firstName ?? ''} ${profile.lastName ?? ''}`.trim() const email = profile.email ?? '' setUserInitials(computeInitials(fullName || email || dict.workspaceUser)) } catch {} } let cancelled = false apiFetch<{ employee: { email: string firstName: string lastName: string } }>('/auth/employee/me') .then(({ employee }) => { if (cancelled) return window.localStorage.setItem(EMPLOYEE_PROFILE_KEY, JSON.stringify(employee)) const fullName = `${employee.firstName ?? ''} ${employee.lastName ?? ''}`.trim() const email = employee.email ?? '' setUserInitials(computeInitials(fullName || email || dict.workspaceUser)) }) .catch(() => { if (cancelled) return setUserInitials(computeInitials(dict.workspaceUser)) }) return () => { cancelled = true } }, [dict.workspaceUser]) async function openNotificationsInbox(notificationId?: string) { if (notificationId) { try { await apiFetch(`/notifications/company/${notificationId}/read`, { method: 'POST' }) setUnreadCount((current) => Math.max(current - 1, 0)) window.dispatchEvent(new Event('notifications:updated')) } catch {} } setShowNotifs(false) router.push('/notifications') } return (

RentalDriveGo

{title}

System operational
{showNotifs ? (

{dict.notifications}

{loadingNotifs ? (

Loading…

) : notifications.length === 0 ? (

{unreadCount === 0 ? dict.noNewNotifications : dict.unreadNotifications(unreadCount)}

) : (
{notifications.map((notification) => ( ))}
)}
) : null}
{userInitials}
) }