import React, { useState, useEffect } from 'react'; import { Bell, Calendar, Wrench, AlertTriangle, FileText, CheckSquare } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator, DropdownMenuLabel, } from '@/components/ui/dropdown-menu'; import { useEquipmentData } from '@/hooks/use-equipment-data'; import { useRemarksData } from '@/hooks/use-remarks-data'; import { useMaintenanceData } from '@/hooks/use-maintenance-data'; import { useQuery } from '@tanstack/react-query'; import { Link } from 'wouter'; interface Notification { id: string; type: 'maintenance' | 'remark' | 'task' | 'warning' | 'info'; title: string; description: string; link?: string; equipmentId?: string; priority: 'high' | 'medium' | 'low'; createdAt: Date; } export function NotificationsDropdown() { const { equipment } = useEquipmentData(); const { remarks } = useRemarksData(); const { maintenanceRecords, refreshData } = useMaintenanceData(); const [refreshKey, setRefreshKey] = useState(0); // Загрузка задач const { data: tasks = [] } = useQuery({ queryKey: ['/api/tasks'] }); // Слушаем события изменения данных ТО и замечаний useEffect(() => { const handleDataChange = () => { refreshData(); setRefreshKey(prev => prev + 1); }; window.addEventListener('maintenanceDataChanged', handleDataChange); window.addEventListener('remarksUpdated', handleDataChange); window.addEventListener('remarkStatusChanged', handleDataChange); return () => { window.removeEventListener('maintenanceDataChanged', handleDataChange); window.removeEventListener('remarksUpdated', handleDataChange); window.removeEventListener('remarkStatusChanged', handleDataChange); }; }, [refreshData]); const generateNotifications = (): Notification[] => { const notifications: Notification[] = []; const today = new Date(); const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000); // Уведомления о запланированных ТО из записей обслуживания maintenanceRecords.forEach(record => { if (record.status !== 'scheduled') return; const scheduledDate = new Date(record.scheduledDate); const daysUntil = Math.ceil((scheduledDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)); // Проверяем, что оборудование существует в системе const equipmentItem = equipment.find(eq => eq.id === record.equipmentId); if (!equipmentItem) return; // Пропускаем записи для несуществующего оборудования const equipmentName = equipmentItem.name; if (scheduledDate <= nextWeek && scheduledDate >= today) { notifications.push({ id: `maintenance-${record.id}`, type: 'maintenance', title: 'Требуется ТО', description: `${equipmentName} - ${record.maintenanceType} через ${daysUntil} дн.`, link: '/maintenance', equipmentId: record.equipmentId, priority: daysUntil <= 3 ? 'high' : 'medium', createdAt: today }); } else if (scheduledDate < today) { notifications.push({ id: `maintenance-overdue-${record.id}`, type: 'warning', title: 'Просрочено ТО', description: `${equipmentName} - ${record.maintenanceType} просрочено на ${Math.abs(daysUntil)} дн.`, link: '/maintenance', equipmentId: record.equipmentId, priority: 'high', createdAt: today }); } }); // Уведомления о замечаниях (только открытые и в работе) remarks.forEach(remark => { if (remark.status === 'open' || remark.status === 'in_progress') { const priority = remark.priority === 'critical' ? 'high' : remark.priority === 'high' ? 'medium' : 'low'; notifications.push({ id: `remark-${remark.id}`, type: 'remark', title: remark.status === 'in_progress' ? 'Замечание в работе' : 'Открытое замечание', description: `${remark.equipmentName} - ${remark.description.substring(0, 50)}...`, link: '/tasks', equipmentId: remark.equipmentId, priority, createdAt: new Date(remark.createdAt) }); } }); // Уведомления о задачах (только ожидающие и в работе) tasks.forEach((task: any) => { if (task.status === 'pending' || task.status === 'in_progress') { const priority = task.priority === 'critical' ? 'high' : task.priority === 'high' ? 'medium' : 'low'; const dueDate = task.dueDate ? new Date(task.dueDate) : null; const isOverdue = dueDate && dueDate < today; const daysDue = dueDate ? Math.ceil((dueDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)) : null; notifications.push({ id: `task-${task.id}`, type: 'task', title: task.status === 'in_progress' ? 'Задача в работе' : isOverdue ? 'Просроченная задача' : 'Новая задача', description: daysDue !== null ? (isOverdue ? `${task.title} - просрочена на ${Math.abs(daysDue)} дн.` : daysDue <= 3 ? `${task.title} - до ${daysDue} дн.` : task.title) : task.title, link: '/tasks', equipmentId: task.equipmentId, priority: isOverdue ? 'high' : priority, createdAt: new Date(task.createdAt) }); } }); // Оборудование требующее внимания (статус maintenance) equipment.forEach(item => { if (item.status === 'maintenance') { notifications.push({ id: `equipment-maintenance-${item.id}`, type: 'warning', title: 'Оборудование на ТО', description: `${item.name} - находится на техобслуживании`, link: '/equipment', equipmentId: item.id, priority: 'medium', createdAt: today }); } else if (item.status === 'inactive') { notifications.push({ id: `equipment-inactive-${item.id}`, type: 'warning', title: 'Оборудование не активно', description: `${item.name} - требует проверки`, link: '/equipment', equipmentId: item.id, priority: 'high', createdAt: today }); } }); // Сортировка по приоритету и дате return notifications.sort((a, b) => { const priorityOrder = { high: 3, medium: 2, low: 1 }; if (priorityOrder[a.priority] !== priorityOrder[b.priority]) { return priorityOrder[b.priority] - priorityOrder[a.priority]; } return b.createdAt.getTime() - a.createdAt.getTime(); }); }; const notifications = generateNotifications(); const highPriorityCount = notifications.filter(n => n.priority === 'high').length; const getNotificationIcon = (type: string) => { switch (type) { case 'maintenance': return ; case 'remark': return ; case 'task': return ; case 'warning': return ; default: return ; } }; const getPriorityColor = (priority: string) => { switch (priority) { case 'high': return 'text-red-600 dark:text-red-400'; case 'medium': return 'text-yellow-600 dark:text-yellow-400'; default: return 'text-gray-600 dark:text-gray-400'; } }; return ( Уведомления {notifications.length > 0 && ( {notifications.length} )} {notifications.length === 0 ? (

Нет уведомлений

) : ( notifications.map((notification) => (
{getNotificationIcon(notification.type)}

{notification.title}

{notification.priority === 'high' ? 'Срочно' : notification.priority === 'medium' ? 'Важно' : 'Обычное'}

{notification.description}

{notification.equipmentId && (

ID: {notification.equipmentId}

)}
)) )} {notifications.length > 0 && ( <> Все уведомления → )}
); }