Files
Kanban-Front/client/src/KBBoardsList.js
Vladiysss 7b9f9cf893 Init
2026-02-01 15:34:27 +03:00

233 lines
8.5 KiB
JavaScript

import React, { useState, useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import Header from './Header';
function ListItem({ item }) {
const navigate = useNavigate();
if (!item) return null;
const openBoard = () => { navigate('/kanban-board/' + item.id); };
return (
<li>
<button onClick={openBoard}>
<div className="sort-row">
<h3>{item.title}</h3>
<p><strong>Владелец:</strong> {item.owner_display_name}</p>
</div>
<div className="sort-row">
<p><strong>Описание:</strong> {item.description}</p>
<p><strong>Обновлено:</strong> {new Date(item.updated_at).toLocaleString()}</p>
</div>
</button>
</li>
);
};
const KBBoardsList = () => {
const [error, setError] = useState('');
const [sort_method, setSortMethod] = useState('title');
const [reverse, setReverse] = useState(false);
const [search_text, setSearchText] = useState('');
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [items, setItems] = useState([]);
const [showCreateModal, setShowCreateModal] = useState(false);
const [loading, setLoading] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
const [filteredItems, setFilteredItems] = useState([]);
const [page, setPage] = useState(1);
const [list, setList] = useState(20);
const debounceRef = useRef(null);
useEffect(() => {
const loadBoardList = async () => {
try {
var newList = { sort_method, reverse, search_text, page, list };
const response = await axios.post('/api/boards/list', newList);
if (Array.isArray(response.data)) {
setItems(response.data);
} else {
// Если данных нет или они не массив — ставим пустой массив
setItems([]);
if (response.data?.detail === 'Доски отсутствуют.') {
console.log('Доски отсутствуют');
}
}
} catch (err) {
if (err.response.data.message === 'Token Error' || err.response.data.message === 'Invalid Token') {
setError('Вы не авторизованы');
setTimeout(() => {
window.location.href = '/login';
}, 1500);
} else {
setError('Ошибка загрузки досок');
console.log(err);
setItems([]); // Гарантируем, что items — массив
}
}
};
loadBoardList()
}, []);
useEffect(() => {
clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => {
setFilteredItems(
items.filter(item =>
item.title.toLowerCase().includes(searchQuery.toLowerCase())
)
);
}, 300); // 300 мс задержка
}, [searchQuery, items]);
const sortList = async (method = sort_method, isReverse = reverse) => {
try {
const newList = { sort_method: method, reverse: isReverse, search_text };
const response = await axios.post('/api/boards/list', newList);
if (Array.isArray(response.data)) {
setItems(response.data);
} else {
setItems([]);
if (response.data?.detail === 'Доски отсутствуют.') {
console.log('Доски отсутствуют');
}
}
} catch (err) {
if (err.response?.data?.message === 'Token Error' || err.response?.data?.message === 'Invalid Token') {
setError('Вы не авторизованы');
setTimeout(() => {
window.location.href = '/login';
}, 1500);
} else {
setError('Ошибка загрузки досок');
console.log(err);
setItems([]);
}
}
};
const setFilterTitle = async () => {
const newMethod = 'title';
const newReverse = sort_method === 'title' ? !reverse : false;
setSortMethod(newMethod);
setReverse(newReverse);
await sortList(newMethod, newReverse);
};
const setFilterOwner = async () => {
const newMethod = 'owner';
const newReverse = sort_method === 'owner' ? !reverse : false;
setSortMethod(newMethod);
setReverse(newReverse);
await sortList(newMethod, newReverse);
};
const setFilterUptime = async () => {
const newMethod = 'update_time';
const newReverse = sort_method === 'update_time' ? !reverse : false;
setSortMethod(newMethod);
setReverse(newReverse);
await sortList(newMethod, newReverse);
};
const createBoard = async () => {
try {
const newBoard = { title, description };
await axios.post('/api/boards/create', newBoard);
setShowCreateModal(false)
} catch (err) {
setError('Ошибка');
}
};
const openCreateModal = () => { setShowCreateModal(true) };
const closeCreateModal = () => { setShowCreateModal(false); setDescription(''); setTitle('') };
return (
<>
<Header />
<div className="profile-page">
{
error && <div className="error">{error}</div>
}
<div className="kan-ban-list-sort">
<h3>Сортировка по:</h3>
<div className="nav-sort">
<button onClick={setFilterTitle}>
Названию
</button>
<button onClick={setFilterOwner}>
Создателю
</button>
<button onClick={setFilterUptime}>
Дате обновления
</button>
</div>
<input
type="text"
placeholder="Поиск по названию..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<div className="kan-ban-list">
<div className="inf">
<h3>Доступные канбан доски:</h3>
<button onClick={openCreateModal}>Создать канбан-доску</button>
</div>
{filteredItems.length > 0 ? (
<ul>
{filteredItems.map((item) => (
<ListItem key={item.id} item={item} />
))}
</ul>
) : (
<p>Нет данных</p>
)}
</div>
</div>
{showCreateModal && (
<div className="confirm-modal">
<div className="modal-content">
<p><strong>Придумайте название и описание канбан доски</strong></p>
<form onSubmit={createBoard}>
<div>
<label>Название:</label>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
required
/>
</div>
<div>
<label>Описание:</label>
<input
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</div>
<div className="modal-buttons">
<button type="submit" disabled={loading}>{loading ? 'Создание...' : 'Создать'}</button>
<button onClick={closeCreateModal}>Отменить</button>
</div>
</form>
</div>
</div>
)}
</>
);
}
export default KBBoardsList;