import { Link, useLocation } from "wouter"; import { useAuthSimple } from "@/hooks/useAuthSimple"; import { Button } from "@/components/ui/button"; import { useStore } from "@/contexts/StoreContext"; import { useIsMobile } from "@/hooks/use-mobile"; import { useScreenSize } from "@/hooks/use-screen-size"; import { useState } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { useToast } from "@/hooks/use-toast"; import { apiRequest } from "@/lib/queryClient"; import { Store, Calendar, BarChart3, Package, Truck, Building, Users, UserCog, LogOut, FileText, Megaphone, Database, ShoppingCart, Clock, ListTodo, ChevronLeft, ChevronRight, X, Settings, Wrench, FileUp, Send, Receipt, TrendingUp, CreditCard } from "lucide-react"; export default function Sidebar() { const { user, isLoading, error } = useAuthSimple(); const [location] = useLocation(); const { sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen } = useStore(); const isMobile = useIsMobile(); const { screenSize, isMobileOrTablet, isTablet } = useScreenSize(); const [showBapModal, setShowBapModal] = useState(false); const [selectedFile, setSelectedFile] = useState(null); const [selectedRecipient, setSelectedRecipient] = useState(''); const [isUploading, setIsUploading] = useState(false); const [showWaitingModal, setShowWaitingModal] = useState(false); const [processingSeconds, setProcessingSeconds] = useState(0); const [processingTimeout, setProcessingTimeout] = useState(null); const { toast } = useToast(); const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file && file.type === 'application/pdf') { setSelectedFile(file); } else { toast({ title: "Erreur", description: "Veuillez sélectionner un fichier PDF", variant: "destructive", }); } }; const startProcessingTimer = () => { setProcessingSeconds(0); const interval = setInterval(() => { setProcessingSeconds(prev => { if (prev >= 60) { clearInterval(interval); return 60; } return prev + 1; }); }, 1000); setProcessingTimeout(interval); }; const handleCloseWaitingModal = () => { setShowWaitingModal(false); setProcessingSeconds(0); if (processingTimeout) { clearTimeout(processingTimeout); setProcessingTimeout(null); } }; const handleSendBap = async () => { if (!selectedFile || !selectedRecipient) { toast({ title: "Erreur", description: "Veuillez sélectionner un fichier PDF et un destinataire", variant: "destructive", }); return; } // Récupérer l'URL webhook configurée let webhookUrl = ''; try { const configResponse = await fetch('/api/webhook-bap-config', { credentials: 'include' }); if (configResponse.ok) { const config = await configResponse.json(); webhookUrl = config?.webhookUrl; } } catch (error) { console.warn('Impossible de récupérer la config webhook, utilisation URL par défaut'); } // Utiliser une URL par défaut si pas de configuration if (!webhookUrl) { webhookUrl = "https://workflow.ffnancy.fr/webhook/a3d03176-b72f-412d-8fb9-f920b9fbab4d"; } // Fermer le modal de sélection et ouvrir le modal d'attente setShowBapModal(false); setShowWaitingModal(true); setIsUploading(true); startProcessingTimer(); try { // Utiliser FormData comme le système de rapprochement qui fonctionne const formData = new FormData(); formData.append('file', selectedFile); formData.append('recipient', selectedRecipient); formData.append('type', 'BAP'); formData.append('fileName', selectedFile.name); // Créer un AbortController pour gérer le timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 secondes // Envoyer directement au webhook (comme le rapprochement) const response = await fetch(webhookUrl, { method: 'POST', body: formData, signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`Erreur ${response.status}: ${response.statusText}`); } handleCloseWaitingModal(); toast({ title: "Succès", description: "Fichier BAP envoyé avec succès", }); // Reset des données setSelectedFile(null); setSelectedRecipient(''); } catch (error: any) { handleCloseWaitingModal(); let errorMessage = "Impossible d'envoyer le fichier BAP"; if (error.name === 'AbortError') { errorMessage = "Le traitement a pris trop de temps (timeout de 60 secondes)"; } else if (error.message) { errorMessage = error.message; } toast({ title: "Erreur", description: errorMessage, variant: "destructive", }); // Réouvrir le modal de sélection en cas d'erreur setShowBapModal(true); } finally { setIsUploading(false); } }; // Debug logging pour production console.log('Sidebar - User:', user); console.log('Sidebar - isLoading:', isLoading); console.log('Sidebar - error:', error); const handleLogout = async () => { try { // Force logout via fetch to ensure session is destroyed await fetch('/api/logout', { method: 'POST', credentials: 'include' }); } catch (error) { console.error('Logout error:', error); } finally { // Force redirect to auth page regardless of API response window.location.href = "/auth"; } }; const toggleSidebar = () => { const newCollapsed = !sidebarCollapsed; console.log('🔧 Sidebar - Toggling sidebar:', { from: sidebarCollapsed, to: newCollapsed }); setSidebarCollapsed(newCollapsed); try { localStorage.setItem('sidebarCollapsed', JSON.stringify(newCollapsed)); console.log('💾 Sidebar state saved to localStorage:', newCollapsed); } catch (error) { console.error('Error saving sidebar state:', error); } }; const getInitials = (firstName?: string, lastName?: string) => { if (!firstName && !lastName) return "U"; return `${firstName?.[0] || ""}${lastName?.[0] || ""}`.toUpperCase(); }; const isActive = (path: string) => { if (path === "/" && location === "/") return true; if (path !== "/" && location.startsWith(path)) return true; return false; }; const menuItems = [ { path: "/dashboard", label: "Tableau de bord", icon: BarChart3, roles: ["admin", "directeur", "manager", "employee"] }, { path: "/calendar", label: "Calendrier", icon: Calendar, roles: ["admin", "directeur", "manager", "employee"] }, { path: "/orders", label: "Commandes", icon: Package, roles: ["admin", "directeur", "manager"] }, { path: "/deliveries", label: "Livraisons", icon: Truck, roles: ["admin", "directeur", "manager"] }, { path: "/bl-reconciliation", label: "Rapprochement", icon: FileText, roles: ["admin", "directeur"] }, { path: "/analytics", label: "Statistiques", icon: BarChart3, roles: ["admin", "directeur", "manager"] }, { path: "/sales-analysis", label: "Analyse Vente", icon: TrendingUp, roles: ["admin", "directeur"] }, { path: "/payment-schedule", label: "Échéance", icon: CreditCard, roles: ["admin", "directeur"] }, { path: "/avoirs", label: "Avoirs", icon: Receipt, roles: ["admin", "directeur", "manager"] }, { path: "/publicities", label: "Publicités", icon: Megaphone, roles: ["admin", "directeur", "manager", "employee"] }, { path: "/customer-orders", label: "Commandes Client", icon: ShoppingCart, roles: ["admin", "directeur", "manager", "employee"] }, { path: "/dlc", label: "Gestion DLC", icon: Clock, roles: ["admin", "directeur", "manager", "employee"] }, { path: "/tasks", label: "Tâches", icon: ListTodo, roles: ["admin", "directeur", "manager", "employee"] }, { path: "/sav", label: "SAV", icon: Wrench, roles: ["admin", "directeur", "manager", "employee"] }, ]; const adminItems = [ { path: "/users", label: "Utilisateurs", icon: UserCog, roles: ["admin"] }, { path: "/suppliers", label: "Fournisseurs", icon: Building, roles: ["admin"] }, { path: "/groups", label: "Magasins", icon: Users, roles: ["admin"] }, { path: "/utilities", label: "Utilitaires", icon: Settings, roles: ["admin"] }, { path: "#bap", label: "BAP", icon: FileUp, roles: ["admin"], isButton: true }, ]; const hasPermission = (roles: string[]) => { const hasRole = user?.role && roles.includes(user.role); // Debug uniquement en développement pour éviter spam console if (import.meta.env.MODE === 'development') { console.log('hasPermission check:', { userRole: user?.role, roles, hasRole }); } return hasRole; }; // Classes responsives adaptées avec support tablette const getSidebarClasses = () => { if (isMobile) { return `fixed inset-y-0 left-0 z-50 w-64 bg-white border-r border-gray-200 shadow-lg transform transition-transform duration-300 ease-in-out ${ mobileMenuOpen ? 'translate-x-0' : '-translate-x-full' }`; } // Mode tablette : semi-collapsed par défaut ou collapsed si forcé if (isTablet) { return `${sidebarCollapsed ? 'sidebar-collapsed' : 'sidebar-tablet'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`; } // Mode desktop standard return `${sidebarCollapsed ? 'sidebar-collapsed' : 'sidebar-expanded'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`; }; const sidebarClasses = getSidebarClasses(); // Debug log pour vérifier l'état de la sidebar console.log('🔧 Sidebar Debug:', { isMobile, sidebarCollapsed, mobileMenuOpen, width: sidebarCollapsed ? 'collapsed (64px)' : 'expanded (256px)', classes: sidebarClasses }); // Si l'utilisateur n'est pas encore chargé, afficher un état de chargement if (isLoading) { return ( ); } // Si l'utilisateur n'est pas authentifié, afficher seulement le logo if (!user) { return ( ); } const handleMenuClick = () => { if (isMobile) { setMobileMenuOpen(false); } }; return ( ); }