import React, { useState, useEffect } from 'react'; import { BuildingTask, Employee, TaskComment, WorkPhoto } from '../../types'; import { X, User, Calendar, Flag, Tag, Clock, MessageSquare, ImageIcon, Camera } from 'lucide-react'; import { backendApi } from '../../services/apiClient'; import { apiClient } from '../../services/apiClient'; interface TaskModalProps { isOpen: boolean; onClose: () => void; onSave: (task: BuildingTask) => void; task?: BuildingTask | null; buildingId: string; employees?: Employee[]; } export const TaskModal: React.FC = ({ isOpen, onClose, onSave, task, buildingId, employees = [] }) => { const [formData, setFormData] = useState>({ title: '', description: '', deadline: '', status: 'new', priority: 'medium', assignedTo: '', assignedToName: '', createdBy: '', createdByName: '', category: '', tags: [], estimatedHours: undefined, comments: [], requirePhotoReport: true, photoReportId: undefined, }); const [availableEmployees, setAvailableEmployees] = useState([]); const [tagInput, setTagInput] = useState(''); const [commentText, setCommentText] = useState(''); const [photoReport, setPhotoReport] = useState(null); const [photoReportLoading, setPhotoReportLoading] = useState(false); const [showPhotoForm, setShowPhotoForm] = useState(false); const [draftTaskId, setDraftTaskId] = useState(null); useEffect(() => { if (isOpen) { if (task) { setFormData({ title: task.title || '', description: task.description || '', deadline: task.deadline || '', status: task.status || 'new', priority: task.priority || 'medium', assignedTo: task.assignedTo || '', assignedToName: task.assignedToName || '', createdBy: task.createdBy || '', createdByName: task.createdByName || '', category: task.category || '', tags: task.tags || [], estimatedHours: task.estimatedHours, comments: task.comments || [], requirePhotoReport: task.requirePhotoReport !== false, photoReportId: task.photoReportId, }); setDraftTaskId(task.id); setPhotoReport(null); if (task.photoReportId) { setPhotoReportLoading(true); apiClient.get(`/pr/work-photos/${task.photoReportId}`) .then((data: WorkPhoto) => setPhotoReport(data)) .catch(() => setPhotoReport(null)) .finally(() => setPhotoReportLoading(false)); } else { setPhotoReportLoading(false); } } else { const newId = `task-${Date.now()}`; setDraftTaskId(newId); setFormData({ title: '', description: '', deadline: '', status: 'new', priority: 'medium', assignedTo: '', assignedToName: '', createdBy: '', createdByName: '', category: '', tags: [], estimatedHours: undefined, comments: [], requirePhotoReport: true, photoReportId: undefined, }); setPhotoReport(null); } setShowPhotoForm(false); setCommentText(''); fetchEmployees(); } }, [isOpen, task, buildingId]); const fetchEmployees = async () => { try { const fetchedEmployees = await backendApi.getEmployees(); setAvailableEmployees(fetchedEmployees); } catch (error) { console.error('Failed to fetch employees:', error); setAvailableEmployees(employees); } }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!formData.title?.trim()) return; const selectedEmployee = availableEmployees.find(emp => emp.id === formData.assignedTo); const authorEmployee = availableEmployees.find(emp => emp.id === formData.createdBy); const taskId = task?.id || draftTaskId || `task-${Date.now()}`; const taskData: BuildingTask = { id: taskId, title: formData.title.trim(), description: formData.description?.trim(), deadline: formData.deadline || new Date().toISOString(), status: formData.status || 'new', priority: formData.priority || 'medium', assignedTo: formData.assignedTo || undefined, assignedToName: selectedEmployee?.name || formData.assignedToName || undefined, createdBy: formData.createdBy || undefined, createdByName: authorEmployee?.name || formData.createdByName || undefined, category: formData.category || undefined, tags: formData.tags || [], estimatedHours: formData.estimatedHours, createdAt: task?.createdAt || new Date().toISOString(), updatedAt: new Date().toISOString(), buildingId: buildingId, comments: formData.comments || [], requirePhotoReport: formData.requirePhotoReport !== false, photoReportId: formData.photoReportId, }; onSave(taskData); onClose(); }; const handleAddComment = () => { if (!commentText.trim()) return; const author = availableEmployees.find(emp => emp.id === formData.createdBy)?.name || formData.createdByName || 'Не указан'; const newComment: TaskComment = { id: `comment-${Date.now()}`, authorId: formData.createdBy || undefined, authorName: author, text: commentText.trim(), createdAt: new Date().toISOString(), }; setFormData({ ...formData, comments: [...(formData.comments || []), newComment], }); setCommentText(''); }; const handlePhotoReportCreated = (workPhoto: WorkPhoto) => { const updatedFormData = { ...formData, photoReportId: workPhoto.id }; setFormData(updatedFormData); setPhotoReport(workPhoto); setShowPhotoForm(false); // Сразу сохраняем задачу с привязкой к фото, чтобы photoReportId не терялся const taskId = task?.id || draftTaskId || `task-${Date.now()}`; const authorEmployee = availableEmployees.find(emp => emp.id === formData.createdBy); const selectedEmployee = availableEmployees.find(emp => emp.id === formData.assignedTo); const taskData: BuildingTask = { id: taskId, title: (formData.title || '').trim() || 'Задача', description: formData.description?.trim(), deadline: formData.deadline || new Date().toISOString(), status: formData.status || 'new', priority: formData.priority || 'medium', assignedTo: formData.assignedTo || undefined, assignedToName: selectedEmployee?.name || formData.assignedToName || undefined, createdBy: formData.createdBy || undefined, createdByName: authorEmployee?.name || formData.createdByName || undefined, category: formData.category || undefined, tags: formData.tags || [], estimatedHours: formData.estimatedHours, createdAt: task?.createdAt || new Date().toISOString(), updatedAt: new Date().toISOString(), buildingId: buildingId, comments: formData.comments || [], requirePhotoReport: formData.requirePhotoReport !== false, photoReportId: workPhoto.id, }; onSave(taskData); }; const handleAddTag = () => { if (tagInput.trim() && !formData.tags?.includes(tagInput.trim())) { setFormData({ ...formData, tags: [...(formData.tags || []), tagInput.trim()] }); setTagInput(''); } }; const handleRemoveTag = (tag: string) => { setFormData({ ...formData, tags: formData.tags?.filter(t => t !== tag) || [] }); }; const priorityColors = { low: 'bg-slate-100 text-slate-600', medium: 'bg-blue-50 text-blue-600', high: 'bg-amber-50 text-amber-600', urgent: 'bg-red-50 text-red-600', }; const statusColors = { new: 'bg-blue-50 text-blue-600', in_progress: 'bg-amber-50 text-amber-600', done: 'bg-emerald-50 text-emerald-600', cancelled: 'bg-slate-100 text-slate-600', }; const uploadsBase = (import.meta.env.VITE_API_BASE_URL || 'http://localhost:4000/api').replace(/\/api\/?$/, '') || 'http://localhost:4000'; if (!isOpen) return null; return (

{task ? 'Редактировать задачу' : 'Новая задача'}

{/* Название */}
setFormData({ ...formData, title: e.target.value })} className="w-full border border-slate-200 rounded-xl px-4 py-2 focus:ring-2 focus:ring-primary-500 outline-none" required />
{/* Описание */}