diff --git a/src/KBBoard/BoardLogic.js b/src/KBBoard/BoardLogic.js index 44655d8..302cccf 100644 --- a/src/KBBoard/BoardLogic.js +++ b/src/KBBoard/BoardLogic.js @@ -119,7 +119,9 @@ export const useBoardLogic = (id, setError, setInfo, setCategories, setLoading, } await updateTaskAPI(editedTaskId, taskTitle, taskDescription, taskPosition, taskDeadline, taskCategory); await loadBoardData(); - modalEditTask({}, null)(); + if(modalEditTask !== null) { + modalEditTask({}, null)(); + } } catch (err) { setError(err.response?.data?.message || 'Ошибка редактирования задачи'); } finally { diff --git a/src/KBBoard/KBBoard.js b/src/KBBoard/KBBoard.js index 2fcbe6a..0812028 100644 --- a/src/KBBoard/KBBoard.js +++ b/src/KBBoard/KBBoard.js @@ -13,6 +13,8 @@ const KBBoard = () => { const [categories, setCategories] = useState([]); const [isOwner, setIsOwner] = useState(null); const [items, setItems] = useState([]); + const [draggedItem, setDraggedItem] = useState(null); + const [draggedType, setDraggedType] = useState(null); const [memList, setMemList] = useState(false); const [crTask, setCrTask] = useState(false); @@ -36,12 +38,11 @@ const KBBoard = () => { const [taskDescription, setTaskDescription] = useState(''); const [taskCategory, setTaskCategory] = useState(null); const [taskCategori, setTaskCategori] = useState(null); - const [taskDeadline, setTaskDeadline] = useState(''); + const [taskDeadline, setTaskDeadline] = useState(null); const [assignedMembers, setAssignedMembers] = useState(null); const [editedTask, setEditedTask] = useState({}); const [editedCateg, setEditedCateg] = useState({}); const [taskPosition, setTaskPosition] = useState(null); - const [categoryPosition, setCategoryPosition] = useState(null); const [assignedMember, setAssignedMember] = useState(0); const [addedUsername, setAddedUsername] = useState(''); const [deletedMember, setDeletedMember] = useState(''); @@ -78,6 +79,24 @@ const KBBoard = () => { ); }; + + const [socket, setSocket] = useState(null); + useEffect(() => { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + const ws = new WebSocket(`${protocol}//https://back.fool-stack.ru/api/boards/ws/`+id); + ws.onopen = () => { + console.log('WebSocket соединение установлено'); + setSocket(ws); + }; + ws.onmessage = (event) => { + const message = JSON.parse(event.data); + loadBoardData(); + }; + ws.onclose = () => console.log('WebSocket соединение закрыто'); + ws.onerror = (error) => console.error('Ошибка WebSocket:', error); + return () => ws.close(); + }, [id, loadBoardData]); + useEffect(() => { if (id) loadBoardData(); }, [id, loadBoardData]); @@ -94,7 +113,7 @@ const KBBoard = () => { setTaskCategori(categori); setTaskTitle(''); setTaskDescription(''); - setTaskDeadline('') + setTaskDeadline(null) } const modalCrCateg = () => { setCrCateg(!crCateg); @@ -119,7 +138,6 @@ const KBBoard = () => { setEdCateg(!edCateg); setEditedCateg(categ); setCategoryTitle(categ.title); - setCategoryPosition(categ.position); } const modalDelTask = () => { setDelTask(!delTask); @@ -194,6 +212,137 @@ const KBBoard = () => { await deleteMember(id, deletedMember, modalDeleteMember); }; + const handleCategoryDragStart = (e, category) => { + e.stopPropagation(); + setDraggedType('c'); + setDraggedItem(category); + e.dataTransfer.setData('text/plain', JSON.stringify({ + id: category.id, + type: 'c', + position: category.position + })); + e.currentTarget.style.opacity = '0.5'; + }; + + const handleTaskDragStart = (e, task, categoryId) => { + e.stopPropagation(); + setDraggedType('t'); + setDraggedItem({ ...task, categoryId }); + e.dataTransfer.setData('text/plain', JSON.stringify({ + id: task.id, + type: 't', + position: task.position, + categoryId: categoryId + })); + e.currentTarget.style.opacity = '0.5'; + e.currentTarget.style.transform = 'scale(0.95)'; + }; + + + + const handleDragEnd = (e) => { + setDraggedType(null); + setDraggedItem(null); + e.currentTarget.style.opacity = '1'; + e.currentTarget.style.transform = 'scale(1)'; + }; + const handleDragOver = (e) => { + e.preventDefault(); + const data = e.dataTransfer.getData('text/plain'); + if (!data) return; + try { + const draggedData = JSON.parse(data); + if (draggedData.type === 't') { + e.currentTarget.style.boxShadow = '0 0 10px rgba(0, 0, 255, 0.3)'; + e.currentTarget.style.border = '2px dashed #007bff'; + } else if (draggedData.type === 'c') { + e.currentTarget.style.backgroundColor = '#f0f0f0'; + } + } catch (err) { + console.error('Ошибка парсинга данных drag:', err); + } + }; + const handleDragLeave = (e) => { + e.currentTarget.style.backgroundColor = ''; + e.currentTarget.style.boxShadow = ''; + e.currentTarget.style.border = ''; + }; + + const handleCategoryReorder = async (e, targetPosition) => { + e.preventDefault(); + e.currentTarget.style.boxShadow = ''; + e.currentTarget.style.border = ''; + e.currentTarget.style.backgroundColor = ''; + e.currentTarget.style.transform = ''; + + const data = e.dataTransfer.getData('text/plain'); + if (!data) return; + + try { + const draggedData = JSON.parse(data); + if (draggedData.type !== 'c') return; + + if (draggedType === 'c' && draggedItem.position !== targetPosition) { + setCategories((prevItems) => { + const draggedIndex = prevItems.findIndex(item => item.position === draggedItem.position); + const targetIndex = prevItems.findIndex(item => item.position === targetPosition); + const newItems = [...prevItems]; + newItems.splice(draggedIndex, 1); + newItems.splice(targetIndex, 0, draggedItem); + const updatedItems = newItems.map((item, index) => ({ + ...item, + position: index + })); + return updatedItems; + }); + await editCategoryPosition(draggedItem.id, targetPosition); + } + } catch (err) { + console.error('Ошибка при переупорядочивании категорий:', err); + setError('Не удалось переупорядочить категории'); + } + }; + + + + const handleTaskReorder = async (e, targetPosition, targetCategoryPos) => { + e.stopPropagation(); + e.preventDefault(); + e.currentTarget.style.boxShadow = ''; + e.currentTarget.style.border = ''; + e.currentTarget.style.backgroundColor = ''; + e.currentTarget.style.transform = ''; + + const data = e.dataTransfer.getData('text/plain'); + if (!data) return; + + if (!draggedItem || draggedType !== 't') return; + try { + const draggedData = JSON.parse(data); + + // Обрабатываем только задачи + if (draggedData.type !== 't' || !draggedItem) return; + + // Находим целевую категорию по позиции + const targetCat = categories.find(cat => cat.position === targetCategoryPos); + if (!targetCat) return; + + // Проверяем, что задача не перемещается в ту же позицию той же категории + if ((draggedItem.position === targetPosition || + (draggedItem.position === targetCat.length-1 && targetPosition === targetCat.length)) && + draggedItem.category_id === targetCat.id) return; + + + await editTask( draggedItem.id, draggedItem.title, + draggedItem.description, targetPosition, + draggedItem.deadline, targetCat.id, null ); + + } catch (err) { + console.error('Ошибка при переупорядочивании задач:', err); + setError('Не удалось переупорядочить задачи'); + } + }; + return (
Позиция: {category.position}
-Задач: {category.tasks.length}
-