feat: добавление работы с пользователями

- добавление участников в канбан доску
- назначение исполнителя на задачу
- снятие исполнителя с задачи
This commit is contained in:
Vladiysss
2026-03-01 21:37:57 +03:00
parent 66385c8b22
commit 859a45aad9
4 changed files with 202 additions and 9 deletions

View File

@@ -31,3 +31,15 @@ export const deleteTaskAPI = async (taskId) => {
data: { id: taskId } data: { id: taskId }
}); });
}; };
export const assignMemberAPI = async (updateData) => {
return axios.put('/api/boards/categories/tasks/assign', updateData);
};
export const unassignMemberAPI = async (updateData) => {
return axios.put('/api/boards/categories/tasks/unassign', updateData);
};
export const addMemberAPI = async (updateData) => {
return axios.post('/api/boards/members/add', updateData);
};

View File

@@ -7,7 +7,10 @@ import {
updateTaskAPI, updateTaskAPI,
updateCategoryAPI, updateCategoryAPI,
deleteCategoryAPI, deleteCategoryAPI,
deleteTaskAPI deleteTaskAPI,
assignMemberAPI,
unassignMemberAPI,
addMemberAPI
} from './BoardAPI'; } from './BoardAPI';
export const useBoardLogic = (id, setError, setInfo, setCategories, setLoading) => { export const useBoardLogic = (id, setError, setInfo, setCategories, setLoading) => {
@@ -88,6 +91,7 @@ export const useBoardLogic = (id, setError, setInfo, setCategories, setLoading)
update_method: 'category', update_method: 'category',
value: Number(taskCategory) value: Number(taskCategory)
}); });
await loadBoardData(); await loadBoardData();
modalEditTask({}, null)(); modalEditTask({}, null)();
} catch (err) { } catch (err) {
@@ -147,6 +151,45 @@ export const useBoardLogic = (id, setError, setInfo, setCategories, setLoading)
} }
}, [loadBoardData, setLoading, setError]); }, [loadBoardData, setLoading, setError]);
const assignMember = useCallback(async (editedTaskId, memberId, act, modalAssignMember) => {
setLoading(true);
try {
if (act) {
await assignMemberAPI({
id: editedTaskId,
member_id: memberId
});
} else if (!act) {
await unassignMemberAPI({
id: editedTaskId,
member_id: memberId
});
}
await loadBoardData();
modalAssignMember();
} catch {
setError('Ошибка');
} finally{
setLoading(false);
}
}, [loadBoardData, setLoading, setError]);
const addMember = useCallback(async (username, boardId, modalAddMember) => {
setLoading(true);
try {
await addMemberAPI({
username: username,
board_id: boardId
});
await loadBoardData();
modalAddMember();
} catch {
setError('Ошибка');
} finally{
setLoading(false);
}
}, [loadBoardData, setLoading, setError]);
return { return {
loadBoardData, loadBoardData,
createTask, createTask,
@@ -154,6 +197,8 @@ export const useBoardLogic = (id, setError, setInfo, setCategories, setLoading)
editTask, editTask,
editCategory, editCategory,
deleteCategory, deleteCategory,
deleteTask deleteTask,
assignMember,
addMember
}; };
}; };

View File

@@ -17,6 +17,9 @@ const KBBoard = () => {
const [edCateg, setEdCateg] = useState(false); const [edCateg, setEdCateg] = useState(false);
const [delTask, setDelTask] = useState(false); const [delTask, setDelTask] = useState(false);
const [delCateg, setDelCateg] = useState(false); const [delCateg, setDelCateg] = useState(false);
const [asgnMember, setAsgnMember] = useState(false);
const [assignAction, setAssignAction] = useState(false);
const [addMemb, setAddMemb] = useState(false);
const [categoryTitle, setCategoryTitle] = useState(''); const [categoryTitle, setCategoryTitle] = useState('');
const [taskTitle, setTaskTitle] = useState(''); const [taskTitle, setTaskTitle] = useState('');
@@ -27,6 +30,10 @@ const KBBoard = () => {
const [editedCateg, setEditedCateg] = useState({}); const [editedCateg, setEditedCateg] = useState({});
const [taskPosition, setTaskPosition] = useState(null); const [taskPosition, setTaskPosition] = useState(null);
const [categoryPosition, setCategoryPosition] = useState(null); const [categoryPosition, setCategoryPosition] = useState(null);
const [assignedMember, setAssignedMember] = useState(0);
const [addedUsername, setAddedUsername] = useState('');
const { const {
loadBoardData, loadBoardData,
@@ -35,7 +42,9 @@ const KBBoard = () => {
editTask, editTask,
editCategory, editCategory,
deleteCategory, deleteCategory,
deleteTask deleteTask,
assignMember,
addMember
} = useBoardLogic(id, setError, setInfo, setCategories, setLoading); } = useBoardLogic(id, setError, setInfo, setCategories, setLoading);
useEffect(() => { useEffect(() => {
@@ -60,6 +69,7 @@ const KBBoard = () => {
setTaskDescription(task.description); setTaskDescription(task.description);
setTaskPosition(task.position); setTaskPosition(task.position);
setTaskCategory(task.category_id); setTaskCategory(task.category_id);
//setAssignedMember(task.assigned_users.id)
} }
const modalEditCateg = (categ) => () => { const modalEditCateg = (categ) => () => {
setEdCateg(!edCateg); setEdCateg(!edCateg);
@@ -73,6 +83,14 @@ const KBBoard = () => {
const modalDelCateg = () => { const modalDelCateg = () => {
setDelCateg(!delCateg); setDelCateg(!delCateg);
} }
const modalAssignMember = (action) => () => {
setAsgnMember(!asgnMember);
setAssignAction(action)
}
const modalAddMember = () => {
setAddMemb(!addMemb);
}
const handleCreateCategory = async (e) => { const handleCreateCategory = async (e) => {
e.preventDefault(); e.preventDefault();
@@ -98,6 +116,16 @@ const KBBoard = () => {
e.preventDefault(); e.preventDefault();
await deleteCategory(editedCateg.id, modalDelCateg, modalEditCateg); await deleteCategory(editedCateg.id, modalDelCateg, modalEditCateg);
}; };
const handleAssignMember = async (e) => {
e.preventDefault();
await assignMember(editedTask.id, assignedMember, assignAction, modalAssignMember);
};
const handleAddMember = async (e) => {
e.preventDefault();
await addMember(addedUsername, id, modalAddMember);
};
return ( return (
<div className="app-container"> <div className="app-container">
@@ -122,7 +150,9 @@ const KBBoard = () => {
</div> </div>
</div> </div>
<div className="set-panel" > <div className="set-panel" >
<p>SetingPanel:В разработке</p> <button onClick={modalAddMember}>
Добавить участника
</button>
</div> </div>
<div className="board-panel" > <div className="board-panel" >
{categories.map((category) => ( {categories.map((category) => (
@@ -143,6 +173,12 @@ const KBBoard = () => {
<button className='task' onClick={modalEditTask(task, category.id)} key={task.id}> <button className='task' onClick={modalEditTask(task, category.id)} key={task.id}>
<div>{task.title}</div> <div>{task.title}</div>
<div>{task.description}</div> <div>{task.description}</div>
<div>Исполнители
{task.assigned_users.map((member) => (
<img key={member.id} className='members-avatar' src={member.avatar_url}></img>
))}
</div>
</button> </button>
)) ))
) : ( ) : (
@@ -173,6 +209,7 @@ const KBBoard = () => {
<div><h3>Новая категория</h3></div> <div><h3>Новая категория</h3></div>
<form onSubmit={handleCreateCategory}> <form onSubmit={handleCreateCategory}>
<div> <div>
<label >Название:</label>
<input <input
type="text" type="text"
value={categoryTitle} value={categoryTitle}
@@ -195,12 +232,14 @@ const KBBoard = () => {
<div><h3>Новая задача</h3></div> <div><h3>Новая задача</h3></div>
<form onSubmit={handleCreateTask}> <form onSubmit={handleCreateTask}>
<div> <div>
<label >Название:</label>
<input <input
type="text" type="text"
value={taskTitle} value={taskTitle}
onChange={(e) => setTaskTitle(e.target.value)} onChange={(e) => setTaskTitle(e.target.value)}
required required
/> />
<label >Описание:</label>
<input <input
type="text" type="text"
value={taskDescription} value={taskDescription}
@@ -222,6 +261,7 @@ const KBBoard = () => {
<div><h3>Изменение категории</h3></div> <div><h3>Изменение категории</h3></div>
<form onSubmit={handleEditCategory}> <form onSubmit={handleEditCategory}>
<div> <div>
<label >Название:</label>
<input <input
type="text" type="text"
value={categoryTitle} value={categoryTitle}
@@ -245,12 +285,14 @@ const KBBoard = () => {
<div><h3>Изменение задачи</h3></div> <div><h3>Изменение задачи</h3></div>
<form onSubmit={handleEditTask}> <form onSubmit={handleEditTask}>
<div> <div>
<label >Название:</label>
<input <input
type="text" type="text"
value={taskTitle} value={taskTitle}
onChange={(e) => setTaskTitle(e.target.value)} onChange={(e) => setTaskTitle(e.target.value)}
required required
/> />
<label >Описание:</label>
<input <input
type="text" type="text"
value={taskDescription} value={taskDescription}
@@ -267,6 +309,11 @@ const KBBoard = () => {
))} ))}
</select> </select>
</div> </div>
<div>
<label >Исполнитель:</label>
<button type="button" onClick={modalAssignMember(true)}>Назначить</button>
<button type="button" onClick={modalAssignMember(false)}>Снять</button>
</div>
</div> </div>
<button type="submit" disabled={loading}> <button type="submit" disabled={loading}>
{loading ? 'Изменение...' : 'Изменить'} {loading ? 'Изменение...' : 'Изменить'}
@@ -307,7 +354,86 @@ const KBBoard = () => {
</div> </div>
</div> </div>
)} )}
</div>
{addMemb && (
<div className="confirm-modal">
<div className="modal-content">
<div><h3>Удаление категории</h3></div>
<form onSubmit={handleAddMember}>
<label >Введите логин человека которого хотитепригласить</label>
<input
type="text"
placeholder="Поиск по названию..."
value={addedUsername}
onChange={(e) => setAddedUsername(e.target.value)}
/>
<button onClick={modalAddMember}>Отменить</button>
<button type="submit" disabled={loading}>
{loading ? 'Добавление...' : 'Добавить'}
</button>
</form>
</div>
</div>
)}
{asgnMember && (
<div className="confirm-modal">
{assignAction ? (
<div className="modal-content">
<div><h3>Назначение пользователя</h3></div>
<form onSubmit={handleAssignMember}>
<div>
<div>
<label >Исполнитель:</label>
<select value={assignedMember} onChange={(e) => setAssignedMember(e.target.value)}>
<option value={0}>
Выберите пользователя
</option>
{info.members.map((member) => (
<option key={member.display_name} value={member.id}>
{member.display_name}
</option>
))}
</select>
</div>
</div>
<button type="submit" disabled={loading}>
{loading ? 'Назначение...' : 'Назначить'}
</button>
</form>
<button onClick={modalAssignMember(null)}>Отменить</button>
</div>
) : (
<div className="modal-content">
<div><h3>Снятие пользователя</h3></div>
<form onSubmit={handleAssignMember}>
<div>
<div>
<label >Исполнитель:</label>
<select value={assignedMember} onChange={(e) => setAssignedMember(e.target.value)}>
<option value={0}>
Выберите пользователя
</option>
{info.members.map((member) => (
<option key={member.display_name} value={member.id}>
{member.display_name}
</option>
))}
</select>
</div>
</div>
<button type="submit" disabled={loading}>
{loading ? 'Снятие...' : 'Снять'}
</button>
</form>
<button onClick={modalAssignMember(null)}>Отменить</button>
</div>
)}
</div>
)}
</div>
</div> </div>
); );
} }

View File

@@ -31,9 +31,17 @@
margin: 20px 0px 0px 0px; margin: 20px 0px 0px 0px;
} }
.modal-content h3 {
text-align: center;
}
.members-avatar {
height: 32px;
width: 32px;
margin-left: 8px;
border-radius: 1000px;
margin-right: -24px;
}
@@ -208,7 +216,9 @@
margin-right: 0px; margin-right: 0px;
} }
label {
text-align: left;
}