import { ReactNode, useState, useRef, useEffect } from "react"; import { Link, useLocation } from "wouter"; import { useAuth } from "../context/auth-context"; import { useListNotifications, useMarkNotificationRead, useMarkAllNotificationsRead, getListNotificationsQueryKey, } from "@workspace/api-client-react"; import { useQueryClient } from "@tanstack/react-query"; import { LayoutDashboard, CheckSquare, Users2, Bell, Users, Building2, ShieldCheck, X, ExternalLink, LogOut, Menu, } from "lucide-react"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { useToast } from "@/hooks/use-toast"; // Role helpers function isAdmin(role: string) { return role === "admin"; } function isHeadOfTeam(role: string) { return role === "head_of_team"; } function isTeamLead(role: string) { return role === "team_lead"; } function isMember(role: string) { return role === "member"; } function canManageUsers(role: string) { return isAdmin(role) || isHeadOfTeam(role) || isTeamLead(role); } function getNavLinks(role: string) { const roles_all = ["admin", "head_of_team", "team_lead", "member"]; const roles_privileged = ["admin", "head_of_team", "team_lead"]; const links = [ { href: "/", label: "لوحة التحكم", icon: LayoutDashboard, roles: roles_all }, { href: "/tasks", label: "المهام", icon: CheckSquare, roles: roles_all }, { href: "/clients", label: "العملاء", icon: Building2, roles: roles_privileged }, { href: "/notifications", label: "الإشعارات", icon: Bell, roles: roles_all }, { href: "/users", label: "الأعضاء", icon: Users, roles: roles_privileged }, { href: "/teams", label: "الفرق", icon: Users2, roles: ["admin"] }, { href: "/admin", label: "الإدارة", icon: ShieldCheck, roles: ["admin"] }, ]; return links.filter(l => l.roles.includes(role)); } function getMobileTabs(role: string) { const roles_all = ["admin", "head_of_team", "team_lead", "member"]; const roles_privileged = ["admin", "head_of_team", "team_lead"]; const all = [ { href: "/", label: "الرئيسية", icon: LayoutDashboard, roles: roles_all }, { href: "/tasks", label: "المهام", icon: CheckSquare, roles: roles_all }, { href: "/clients", label: "العملاء", icon: Building2, roles: roles_privileged }, { href: "/users", label: "الأعضاء", icon: Users, roles: roles_privileged }, { href: "/admin", label: "الإدارة", icon: ShieldCheck, roles: ["admin"] }, ]; return all.filter(l => l.roles.includes(role)).slice(0, 5); } function NotificationsDropdown({ userId }: { userId: number }) { const [open, setOpen] = useState(false); const ref = useRef(null); const queryClient = useQueryClient(); const { toast } = useToast(); const { data: notifications } = useListNotifications( { userId, unreadOnly: false }, { query: { enabled: !!userId, queryKey: getListNotificationsQueryKey({ userId, unreadOnly: false }) } } ); const { data: unreadNotifs } = useListNotifications( { userId, unreadOnly: true }, { query: { enabled: !!userId, queryKey: getListNotificationsQueryKey({ userId, unreadOnly: true }) } } ); const markRead = useMarkNotificationRead(); const markAllRead = useMarkAllNotificationsRead(); const unreadCount = unreadNotifs?.length ?? 0; const recent = notifications?.slice(0, 12) ?? []; useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); useEffect(() => { const interval = setInterval(() => { queryClient.invalidateQueries({ queryKey: getListNotificationsQueryKey({ userId, unreadOnly: true }) }); }, 60_000); return () => clearInterval(interval); }, [queryClient, userId]); const handleMarkRead = (id: number) => { markRead.mutate({ id }, { onSuccess: () => { queryClient.invalidateQueries({ queryKey: getListNotificationsQueryKey({ userId, unreadOnly: true }) }); queryClient.invalidateQueries({ queryKey: getListNotificationsQueryKey({ userId, unreadOnly: false }) }); }, }); }; const handleMarkAllRead = () => { markAllRead.mutate({ data: { userId } }, { onSuccess: () => { queryClient.invalidateQueries({ queryKey: getListNotificationsQueryKey({ userId, unreadOnly: true }) }); queryClient.invalidateQueries({ queryKey: getListNotificationsQueryKey({ userId, unreadOnly: false }) }); toast({ title: "تم تعليم جميع الإشعارات كمقروءة" }); setOpen(false); }, }); }; const notifTypeIcon: Record = { task_assigned: "📋", status_changed: "🔄", new_comment: "💬", comment_added: "💬", deadline_soon: "⏰", }; return (
{open && (
الإشعارات {unreadCount > 0 && ( {unreadCount} جديد )}
{unreadCount > 0 && ( )}
{recent.length === 0 ? (

أنت محدّث!

لا توجد إشعارات جديدة

) : ( recent.map(n => (
{notifTypeIcon[n.type] ?? "🔔"}

{n.message}

{new Date(n.createdAt).toLocaleDateString("ar", { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" })}

{n.taskId && ( setOpen(false)}> )} {!n.isRead && (
)) )}
setOpen(false)}>
)}
); } function RoleBadge({ role }: { role: string }) { const labels: Record = { admin: { label: "مدير النظام", cls: "bg-red-100 text-red-700" }, head_of_team: { label: "رئيس الفرق", cls: "bg-orange-100 text-orange-700" }, team_lead: { label: "قائد فريق", cls: "bg-blue-100 text-blue-700" }, member: { label: "عضو", cls: "bg-slate-100 text-slate-600" }, }; const r = labels[role] ?? { label: role, cls: "bg-slate-100 text-slate-500" }; return ( {r.label} ); } function Header({ onMenuToggle }: { onMenuToggle: () => void }) { const { user, logout } = useAuth(); return (

TeamTasker

Command Center
{user && } {user && (
{user.name.substring(0, 2).toUpperCase()}
{user.name}
{user.team}
)}
); } function Sidebar({ open, onClose }: { open: boolean; onClose: () => void }) { const [location] = useLocation(); const { user, logout } = useAuth(); const navLinks = user ? getNavLinks(user.role) : []; return ( <> {open && (
)} ); } function MobileBottomBar() { const [location] = useLocation(); const { user } = useAuth(); const tabs = user ? getMobileTabs(user.role) : []; return ( ); } export function AppLayout({ children }: { children: ReactNode }) { const [sidebarOpen, setSidebarOpen] = useState(false); return (
setSidebarOpen(o => !o)} />
setSidebarOpen(false)} />
{children}
); } // Export role helpers for use in other components export { isAdmin, isHeadOfTeam, isTeamLead, isMember, canManageUsers };