fix first online resevation

This commit is contained in:
root
2026-05-09 20:01:51 -04:00
parent c4a45c8b21
commit 09b0e3b55f
75 changed files with 6394 additions and 2190 deletions
+85 -11
View File
@@ -3,6 +3,7 @@
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
export type DashboardLanguage = 'en' | 'fr' | 'ar'
export type DashboardTheme = 'light' | 'dark'
type DashboardDictionary = {
nav: Record<string, string>
@@ -11,9 +12,10 @@ type DashboardDictionary = {
noNewNotifications: string
unreadNotifications: (count: number) => string
signOut: string
demoUser: string
clerkDisabled: string
workspaceUser: string
localAuth: string
language: string
theme: string
light: string
dark: string
}
@@ -24,6 +26,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
dashboard: 'Dashboard',
fleet: 'Fleet',
reservations: 'Reservations',
onlineReservations: 'Online Reservations',
customers: 'Customers',
offers: 'Offers',
team: 'Team',
@@ -36,6 +39,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
'/dashboard': 'Dashboard',
'/dashboard/fleet': 'Fleet Management',
'/dashboard/reservations': 'Reservations',
'/dashboard/online-reservations': 'Online Reservations',
'/dashboard/customers': 'Customers',
'/dashboard/offers': 'Offers',
'/dashboard/team': 'Team',
@@ -47,9 +51,10 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
noNewNotifications: 'No new notifications',
unreadNotifications: (count) => `${count} unread notification${count > 1 ? 's' : ''}`,
signOut: 'Sign out',
demoUser: 'Demo User',
clerkDisabled: 'Clerk disabled in local dev',
workspaceUser: 'Workspace user',
localAuth: 'Local authentication',
language: 'Language',
theme: 'Theme',
light: 'Light',
dark: 'Dark',
},
@@ -58,6 +63,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
dashboard: 'Tableau de bord',
fleet: 'Flotte',
reservations: 'Réservations',
onlineReservations: 'Réservations en ligne',
customers: 'Clients',
offers: 'Offres',
team: 'Équipe',
@@ -70,6 +76,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
'/dashboard': 'Tableau de bord',
'/dashboard/fleet': 'Gestion de flotte',
'/dashboard/reservations': 'Réservations',
'/dashboard/online-reservations': 'Réservations en ligne',
'/dashboard/customers': 'Clients',
'/dashboard/offers': 'Offres',
'/dashboard/team': 'Équipe',
@@ -81,9 +88,10 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
noNewNotifications: 'Aucune nouvelle notification',
unreadNotifications: (count) => `${count} notification${count > 1 ? 's' : ''} non lue${count > 1 ? 's' : ''}`,
signOut: 'Déconnexion',
demoUser: 'Utilisateur démo',
clerkDisabled: 'Clerk désactivé en local',
workspaceUser: 'Utilisateur espace',
localAuth: 'Authentification locale',
language: 'Langue',
theme: 'Mode',
light: 'Clair',
dark: 'Sombre',
},
@@ -92,6 +100,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
dashboard: 'لوحة التحكم',
fleet: 'الأسطول',
reservations: 'الحجوزات',
onlineReservations: 'الحجوزات الإلكترونية',
customers: 'العملاء',
offers: 'العروض',
team: 'الفريق',
@@ -104,6 +113,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
'/dashboard': 'لوحة التحكم',
'/dashboard/fleet': 'إدارة الأسطول',
'/dashboard/reservations': 'الحجوزات',
'/dashboard/online-reservations': 'الحجوزات الإلكترونية',
'/dashboard/customers': 'العملاء',
'/dashboard/offers': 'العروض',
'/dashboard/team': 'الفريق',
@@ -115,9 +125,10 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
noNewNotifications: 'لا توجد إشعارات جديدة',
unreadNotifications: (count) => `${count} إشعار غير مقروء`,
signOut: 'تسجيل الخروج',
demoUser: 'مستخدم تجريبي',
clerkDisabled: 'Clerk غير مفعّل محلياً',
workspaceUser: 'مستخدم مساحة العمل',
localAuth: 'مصادقة محلية',
language: 'اللغة',
theme: 'الوضع',
light: 'فاتح',
dark: 'داكن',
},
@@ -126,6 +137,8 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
type I18nContextValue = {
language: DashboardLanguage
setLanguage: (value: DashboardLanguage) => void
theme: DashboardTheme
setTheme: (value: DashboardTheme) => void
dict: DashboardDictionary
}
@@ -133,12 +146,24 @@ const I18nContext = createContext<I18nContextValue | null>(null)
export function DashboardI18nProvider({ children }: { children: React.ReactNode }) {
const [language, setLanguage] = useState<DashboardLanguage>('en')
const [theme, setTheme] = useState<DashboardTheme>('light')
useEffect(() => {
const stored = window.localStorage.getItem('dashboard-language')
const storedTheme = window.localStorage.getItem('dashboard-theme')
if (stored === 'en' || stored === 'fr' || stored === 'ar') {
setLanguage(stored)
}
if (storedTheme === 'light' || storedTheme === 'dark') {
setTheme(storedTheme)
return
}
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
setTheme('dark')
}
}, [])
useEffect(() => {
@@ -147,7 +172,18 @@ export function DashboardI18nProvider({ children }: { children: React.ReactNode
window.localStorage.setItem('dashboard-language', language)
}, [language])
const value = useMemo(() => ({ language, setLanguage, dict: dictionaries[language] }), [language])
useEffect(() => {
document.documentElement.classList.remove('light', 'dark')
document.documentElement.classList.add(theme)
document.documentElement.style.colorScheme = theme
document.body.dataset.theme = theme
window.localStorage.setItem('dashboard-theme', theme)
}, [theme])
const value = useMemo(
() => ({ language, setLanguage, theme, setTheme, dict: dictionaries[language] }),
[language, theme],
)
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>
}
@@ -168,7 +204,7 @@ export function DashboardLanguageSwitcher() {
if (embedded) return null
return (
<div className="flex items-center gap-1 rounded-full border border-slate-200 bg-white px-2 py-1 shadow-sm">
<div className="flex items-center gap-1 rounded-full border border-slate-200 bg-white px-2 py-1 shadow-sm transition-colors dark:border-slate-700 dark:bg-slate-900">
<span className="px-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">
{dict.language}
</span>
@@ -180,7 +216,9 @@ export function DashboardLanguageSwitcher() {
type="button"
onClick={() => setLanguage(value)}
className={`rounded-full px-3 py-1.5 text-xs font-semibold transition ${
active ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-100'
active
? 'bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-950'
: 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800'
}`}
>
{value.toUpperCase()}
@@ -190,3 +228,39 @@ export function DashboardLanguageSwitcher() {
</div>
)
}
export function DashboardThemeSwitcher() {
const { theme, setTheme, dict } = useDashboardI18n()
const [embedded, setEmbedded] = useState(false)
useEffect(() => {
setEmbedded(window.self !== window.top)
}, [])
if (embedded) return null
return (
<div className="flex items-center gap-1 rounded-full border border-slate-200 bg-white px-2 py-1 shadow-sm transition-colors dark:border-slate-700 dark:bg-slate-900">
<span className="px-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500 dark:text-slate-400">
{dict.theme}
</span>
{(['light', 'dark'] as DashboardTheme[]).map((value) => {
const active = value === theme
return (
<button
key={value}
type="button"
onClick={() => setTheme(value)}
className={`rounded-full px-3 py-1.5 text-xs font-semibold transition ${
active
? 'bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-950'
: 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800'
}`}
>
{value === 'light' ? dict.light : dict.dark}
</button>
)
})}
</div>
)
}
@@ -1,7 +1,11 @@
'use client'
import Link from 'next/link'
import { DashboardLanguageSwitcher, useDashboardI18n } from '@/components/I18nProvider'
import {
DashboardLanguageSwitcher,
DashboardThemeSwitcher,
useDashboardI18n,
} from '@/components/I18nProvider'
import { marketplaceUrl } from '@/lib/urls'
export default function PublicShell({ children }: { children: React.ReactNode }) {
@@ -28,31 +32,32 @@ export default function PublicShell({ children }: { children: React.ReactNode })
}[language]
return (
<div className="min-h-screen bg-[radial-gradient(circle_at_top,#dbeafe,transparent_35%),linear-gradient(180deg,#f8fafc,white)]">
<header className="sticky top-0 z-30 border-b border-slate-200 bg-white/85 backdrop-blur-md">
<div className="min-h-screen bg-[radial-gradient(circle_at_top,#dbeafe,transparent_35%),linear-gradient(180deg,#f8fafc,white)] transition-colors dark:bg-[radial-gradient(circle_at_top,rgba(37,99,235,0.18),transparent_35%),linear-gradient(180deg,#020617,#0f172a)] dark:text-slate-100">
<header className="sticky top-0 z-30 border-b border-slate-200 bg-white/85 backdrop-blur-md transition-colors dark:border-slate-800 dark:bg-slate-950/85">
<div className="mx-auto flex max-w-6xl items-center justify-between gap-4 px-4 py-4">
<Link href={marketplaceUrl} className="flex items-center gap-3 text-slate-900">
<Link href={marketplaceUrl} className="flex items-center gap-3 text-slate-900 dark:text-slate-100">
<span className="text-xs font-semibold uppercase tracking-[0.24em] text-blue-600">RentalDriveGo</span>
<span className="hidden text-sm font-semibold text-slate-500 sm:inline">{dict.workspace}</span>
<span className="hidden text-sm font-semibold text-slate-500 dark:text-slate-400 sm:inline">{dict.workspace}</span>
</Link>
<nav className="flex items-center gap-2">
<Link href="/sign-in" className="rounded-full px-4 py-2 text-sm font-medium text-slate-600 transition hover:bg-slate-100 hover:text-slate-900">
<Link href="/sign-in" className="rounded-full px-4 py-2 text-sm font-medium text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-100">
{dict.signIn}
</Link>
<Link href="/sign-up" className="rounded-full bg-slate-900 px-4 py-2 text-sm font-semibold text-white transition hover:bg-slate-700">
<Link href="/sign-up" className="rounded-full bg-slate-900 px-4 py-2 text-sm font-semibold text-white transition hover:bg-slate-700 dark:bg-slate-100 dark:text-slate-950 dark:hover:bg-slate-200">
{dict.createWorkspace}
</Link>
</nav>
</div>
</header>
<div>{children}</div>
<footer className="border-t border-stone-200 bg-white/90 px-4 py-4 backdrop-blur-md transition-colors">
<footer className="border-t border-stone-200 bg-white/90 px-4 py-4 backdrop-blur-md transition-colors dark:border-slate-800 dark:bg-slate-950/90">
<div className="mx-auto flex max-w-6xl flex-col items-center justify-between gap-3 lg:flex-row">
<p className="text-xs font-medium uppercase tracking-[0.16em] text-stone-400">
<p className="text-xs font-medium uppercase tracking-[0.16em] text-stone-400 dark:text-slate-500">
{dict.preferences}
</p>
<div className="flex flex-col items-center gap-3 sm:flex-row">
<DashboardLanguageSwitcher />
<DashboardThemeSwitcher />
</div>
</div>
</footer>
@@ -1,11 +1,12 @@
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { usePathname, useRouter } from 'next/navigation'
import {
LayoutDashboard,
Car,
Calendar,
Globe,
Users,
Tag,
UserPlus,
@@ -15,15 +16,15 @@ import {
Settings,
LogOut,
} from 'lucide-react'
import { useClerk, useUser } from '@clerk/nextjs'
import { clerkFrontendEnabled } from '@/lib/clerk'
import { useDashboardI18n } from '@/components/I18nProvider'
import { marketplaceUrl } from '@/lib/urls'
import { EMPLOYEE_TOKEN_KEY } from '@/lib/api'
const NAV_ITEMS = [
{ href: '/dashboard', key: 'dashboard', icon: LayoutDashboard, exact: true },
{ href: '/dashboard/fleet', key: 'fleet', icon: Car },
{ href: '/dashboard/reservations', key: 'reservations', icon: Calendar },
{ href: '/dashboard/online-reservations', key: 'onlineReservations', icon: Globe },
{ href: '/dashboard/customers', key: 'customers', icon: Users },
{ href: '/dashboard/offers', key: 'offers', icon: Tag },
{ href: '/dashboard/team', key: 'team', icon: UserPlus },
@@ -33,29 +34,32 @@ const NAV_ITEMS = [
{ href: '/dashboard/settings', key: 'settings', icon: Settings },
] as const
function SidebarWithClerk() {
export default function Sidebar() {
const { dict } = useDashboardI18n()
const pathname = usePathname()
const { signOut } = useClerk()
const { user } = useUser()
const router = useRouter()
const isActive = (item: typeof NAV_ITEMS[0]) => {
if (item.exact) return pathname === item.href
return pathname.startsWith(item.href)
}
function signOut() {
localStorage.removeItem(EMPLOYEE_TOKEN_KEY)
document.cookie = 'employee_token=; path=/; max-age=0; samesite=lax'
router.push('/sign-in')
}
return (
<aside className="fixed inset-y-0 left-0 w-64 bg-slate-900 flex flex-col z-40">
{/* Logo */}
<Link href={marketplaceUrl} className="flex items-center gap-3 px-6 py-5 border-b border-slate-800">
<div className="w-8 h-8 bg-blue-500 rounded-lg flex items-center justify-center">
<Car className="w-4 h-4 text-white" />
<aside className="fixed inset-y-0 left-0 z-40 flex w-64 flex-col bg-slate-900">
<Link href={marketplaceUrl} className="flex items-center gap-3 border-b border-slate-800 px-6 py-5">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-blue-500">
<Car className="h-4 w-4 text-white" />
</div>
<span className="font-bold text-white text-sm tracking-wide">RentalDriveGo</span>
<span className="text-sm font-bold tracking-wide text-white">RentalDriveGo</span>
</Link>
{/* Navigation */}
<nav className="flex-1 px-3 py-4 space-y-0.5 overflow-y-auto">
<nav className="flex-1 space-y-0.5 overflow-y-auto px-3 py-4">
{NAV_ITEMS.map((item) => {
const Icon = item.icon
const active = isActive(item)
@@ -64,101 +68,35 @@ function SidebarWithClerk() {
key={item.href}
href={item.href}
className={[
'flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors',
active
? 'bg-blue-600 text-white'
: 'text-slate-400 hover:text-white hover:bg-slate-800',
'flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors',
active ? 'bg-blue-600 text-white' : 'text-slate-400 hover:bg-slate-800 hover:text-white',
].join(' ')}
>
<Icon className="w-4 h-4 flex-shrink-0" />
<Icon className="h-4 w-4 flex-shrink-0" />
{dict.nav[item.key]}
</Link>
)
})}
</nav>
{/* User menu */}
<div className="px-3 py-4 border-t border-slate-800">
<div className="flex items-center gap-3 px-3 py-2 rounded-lg">
<div className="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white text-xs font-semibold flex-shrink-0">
{user?.firstName?.[0]?.toUpperCase() ?? 'U'}
<div className="border-t border-slate-800 px-3 py-4">
<div className="flex items-center gap-3 rounded-lg px-3 py-2">
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-xs font-semibold text-white">
W
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-white truncate">
{user?.firstName} {user?.lastName}
</p>
<p className="text-xs text-slate-400 truncate">
{user?.primaryEmailAddress?.emailAddress}
</p>
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-medium text-white">{dict.workspaceUser}</p>
<p className="truncate text-xs text-slate-400">{dict.localAuth}</p>
</div>
</div>
<button
onClick={() => signOut()}
className="mt-2 w-full flex items-center gap-3 px-3 py-2 text-sm text-slate-400 hover:text-white hover:bg-slate-800 rounded-lg transition-colors"
onClick={signOut}
className="mt-2 flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm text-slate-400 transition-colors hover:bg-slate-800 hover:text-white"
>
<LogOut className="w-4 h-4" />
<LogOut className="h-4 w-4" />
{dict.signOut}
</button>
</div>
</aside>
)
}
function SidebarWithoutClerk() {
const { dict } = useDashboardI18n()
const pathname = usePathname()
const isActive = (item: typeof NAV_ITEMS[0]) => {
if (item.exact) return pathname === item.href
return pathname.startsWith(item.href)
}
return (
<aside className="fixed inset-y-0 left-0 w-64 bg-slate-900 flex flex-col z-40">
<Link href={marketplaceUrl} className="flex items-center gap-3 px-6 py-5 border-b border-slate-800">
<div className="w-8 h-8 bg-blue-500 rounded-lg flex items-center justify-center">
<Car className="w-4 h-4 text-white" />
</div>
<span className="font-bold text-white text-sm tracking-wide">RentalDriveGo</span>
</Link>
<nav className="flex-1 px-3 py-4 space-y-0.5 overflow-y-auto">
{NAV_ITEMS.map((item) => {
const Icon = item.icon
const active = isActive(item)
return (
<Link
key={item.href}
href={item.href}
className={[
'flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors',
active
? 'bg-blue-600 text-white'
: 'text-slate-400 hover:text-white hover:bg-slate-800',
].join(' ')}
>
<Icon className="w-4 h-4 flex-shrink-0" />
{dict.nav[item.key]}
</Link>
)
})}
</nav>
<div className="px-3 py-4 border-t border-slate-800">
<div className="flex items-center gap-3 px-3 py-2 rounded-lg">
<div className="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white text-xs font-semibold flex-shrink-0">
D
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-white truncate">{dict.demoUser}</p>
<p className="text-xs text-slate-400 truncate">{dict.clerkDisabled}</p>
</div>
</div>
</div>
</aside>
)
}
export default function Sidebar() {
return clerkFrontendEnabled ? <SidebarWithClerk /> : <SidebarWithoutClerk />
}
+47 -106
View File
@@ -4,113 +4,54 @@ import { Bell } from 'lucide-react'
import { usePathname } from 'next/navigation'
import { useState, useEffect } from 'react'
import { apiFetch } from '@/lib/api'
import { useUser } from '@clerk/nextjs'
import { clerkFrontendEnabled } from '@/lib/clerk'
import { useDashboardI18n } from '@/components/I18nProvider'
function TopBarWithClerk() {
const { dict } = useDashboardI18n()
const pathname = usePathname()
const { user } = useUser()
const [unreadCount, setUnreadCount] = useState(0)
const [showNotifs, setShowNotifs] = useState(false)
const title = dict.titles[pathname] ?? dict.titles['/dashboard']
useEffect(() => {
apiFetch<{ unread: number }>('/notifications/unread-count')
.then((data) => setUnreadCount(data.unread))
.catch(() => {})
}, [pathname])
return (
<header className="h-16 bg-white border-b border-slate-200 flex items-center justify-between px-6">
<h1 className="text-lg font-semibold text-slate-900">{title}</h1>
<div className="flex items-center gap-3">
{/* Notification bell */}
<div className="relative">
<button
onClick={() => setShowNotifs(!showNotifs)}
className="relative p-2 text-slate-500 hover:text-slate-700 hover:bg-slate-100 rounded-lg transition-colors"
>
<Bell className="w-5 h-5" />
{unreadCount > 0 && (
<span className="absolute top-1 right-1 min-w-[16px] h-4 bg-red-500 text-white text-[10px] font-bold rounded-full flex items-center justify-center px-1">
{unreadCount > 99 ? '99+' : unreadCount}
</span>
)}
</button>
{showNotifs && (
<div className="absolute right-0 top-full mt-2 w-80 bg-white border border-slate-200 rounded-xl shadow-lg z-50 p-4">
<p className="text-sm font-medium text-slate-900 mb-2">{dict.notifications}</p>
<p className="text-sm text-slate-500">
{unreadCount === 0 ? dict.noNewNotifications : dict.unreadNotifications(unreadCount)}
</p>
</div>
)}
</div>
{/* User avatar */}
<div className="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white text-xs font-semibold">
{user?.firstName?.[0]?.toUpperCase() ?? 'U'}
</div>
</div>
</header>
)
}
function TopBarWithoutClerk() {
const { dict } = useDashboardI18n()
const pathname = usePathname()
const [unreadCount, setUnreadCount] = useState(0)
const [showNotifs, setShowNotifs] = useState(false)
const title = dict.titles[pathname] ?? dict.titles['/dashboard']
useEffect(() => {
apiFetch<{ unread: number }>('/notifications/unread-count')
.then((data) => setUnreadCount(data.unread))
.catch(() => {})
}, [pathname])
return (
<header className="h-16 bg-white border-b border-slate-200 flex items-center justify-between px-6">
<h1 className="text-lg font-semibold text-slate-900">{title}</h1>
<div className="flex items-center gap-3">
<div className="relative">
<button
onClick={() => setShowNotifs(!showNotifs)}
className="relative p-2 text-slate-500 hover:text-slate-700 hover:bg-slate-100 rounded-lg transition-colors"
>
<Bell className="w-5 h-5" />
{unreadCount > 0 && (
<span className="absolute top-1 right-1 min-w-[16px] h-4 bg-red-500 text-white text-[10px] font-bold rounded-full flex items-center justify-center px-1">
{unreadCount > 99 ? '99+' : unreadCount}
</span>
)}
</button>
{showNotifs && (
<div className="absolute right-0 top-full mt-2 w-80 bg-white border border-slate-200 rounded-xl shadow-lg z-50 p-4">
<p className="text-sm font-medium text-slate-900 mb-2">{dict.notifications}</p>
<p className="text-sm text-slate-500">
{unreadCount === 0 ? dict.noNewNotifications : dict.unreadNotifications(unreadCount)}
</p>
</div>
)}
</div>
<div className="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white text-xs font-semibold">
D
</div>
</div>
</header>
)
}
export default function TopBar() {
return clerkFrontendEnabled ? <TopBarWithClerk /> : <TopBarWithoutClerk />
const { dict } = useDashboardI18n()
const pathname = usePathname()
const [unreadCount, setUnreadCount] = useState(0)
const [showNotifs, setShowNotifs] = useState(false)
const title = dict.titles[pathname] ?? dict.titles['/dashboard']
useEffect(() => {
apiFetch<{ unread: number }>('/notifications/unread-count')
.then((data) => setUnreadCount(data.unread))
.catch(() => {})
}, [pathname])
return (
<header className="flex h-16 items-center justify-between border-b border-slate-200 bg-white px-6 transition-colors dark:border-slate-800 dark:bg-slate-950">
<h1 className="text-lg font-semibold text-slate-900 dark:text-slate-100">{title}</h1>
<div className="flex items-center gap-3">
<div className="relative">
<button
onClick={() => setShowNotifs(!showNotifs)}
className="relative rounded-lg p-2 text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100"
>
<Bell className="h-5 w-5" />
{unreadCount > 0 ? (
<span className="absolute right-1 top-1 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-red-500 px-1 text-[10px] font-bold text-white">
{unreadCount > 99 ? '99+' : unreadCount}
</span>
) : null}
</button>
{showNotifs ? (
<div className="absolute right-0 top-full z-50 mt-2 w-80 rounded-xl border border-slate-200 bg-white p-4 shadow-lg dark:border-slate-700 dark:bg-slate-900">
<p className="mb-2 text-sm font-medium text-slate-900 dark:text-slate-100">{dict.notifications}</p>
<p className="text-sm text-slate-500 dark:text-slate-400">
{unreadCount === 0 ? dict.noNewNotifications : dict.unreadNotifications(unreadCount)}
</p>
</div>
) : null}
</div>
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-500 text-xs font-semibold text-white">
W
</div>
</div>
</header>
)
}
@@ -143,7 +143,7 @@ export default function EditMemberModal({
</p>
<p className="text-xs text-red-600 dark:text-red-500 mb-3">
{confirm === 'deactivate' && "They'll immediately lose dashboard access. You can reactivate anytime."}
{confirm === 'remove' && "This will delete the employee record and revoke their Clerk access. This cannot be undone."}
{confirm === 'remove' && "This will delete the employee record and revoke their dashboard access. This cannot be undone."}
{confirm === 'reactivate' && "They'll regain dashboard access with their current role."}
</p>
<div className="flex gap-2">