Переделана навигация к датчикам, добавлена работа поиска тултипов на модели, добавлен функционал перехода из дашборда и истории тревог к датчику с тревогой на 3д модели

This commit is contained in:
2026-02-03 16:42:15 +03:00
parent eccc564cc7
commit 458222817e
19 changed files with 5111 additions and 61 deletions

View File

@@ -38,10 +38,37 @@ interface DetectorListProps {
onDetectorSelect: (detectorId: number, selected: boolean) => void
initialSearchTerm?: string
}
// Функция для генерации умного диапазона страниц
function getPaginationRange(currentPage: number, totalPages: number): (number | string)[] {
if (totalPages <= 7) {
// Если страниц мало - показываем все
return Array.from({ length: totalPages }, (_, i) => i + 1)
}
// Всегда показываем первые 3 и последние 3
const start = [1, 2, 3]
const end = [totalPages - 2, totalPages - 1, totalPages]
if (currentPage <= 4) {
// Начало: 1 2 3 4 5 ... 11 12 13
return [1, 2, 3, 4, 5, '...', ...end]
}
if (currentPage >= totalPages - 3) {
// Конец: 1 2 3 ... 9 10 11 12 13
return [...start, '...', totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1, totalPages]
}
// Середина: 1 2 3 ... 6 7 8 ... 11 12 13
return [...start, '...', currentPage - 1, currentPage, currentPage + 1, '...', ...end]
}
const DetectorList: React.FC<DetectorListProps> = ({ objectId, selectedDetectors, onDetectorSelect, initialSearchTerm = '' }) => {
const [detectors, setDetectors] = useState<Detector[]>([])
const [searchTerm, setSearchTerm] = useState<string>(initialSearchTerm)
const [currentPage, setCurrentPage] = useState(1)
const itemsPerPage = 20
useEffect(() => {
const loadDetectors = async () => {
@@ -76,6 +103,24 @@ const DetectorList: React.FC<DetectorListProps> = ({ objectId, selectedDetectors
return matchesSearch
})
// Сброс на первую страницу при изменении поиска
useEffect(() => {
setCurrentPage(1)
}, [searchTerm])
// Пагинация
const totalPages = Math.ceil(filteredDetectors.length / itemsPerPage)
const startIndex = (currentPage - 1) * itemsPerPage
const endIndex = startIndex + itemsPerPage
const currentDetectors = filteredDetectors.slice(startIndex, endIndex)
const paginationRange = getPaginationRange(currentPage, totalPages)
const handlePageChange = (page: number) => {
setCurrentPage(page)
// Скролл наверх таблицы
window.scrollTo({ top: 0, behavior: 'smooth' })
}
return (
<div className="space-y-6">
@@ -109,16 +154,16 @@ const DetectorList: React.FC<DetectorListProps> = ({ objectId, selectedDetectors
<th className="text-left text-white font-medium py-3 w-12">
<input
type="checkbox"
checked={selectedDetectors.length === filteredDetectors.length && filteredDetectors.length > 0}
checked={selectedDetectors.length === currentDetectors.length && currentDetectors.length > 0}
onChange={(e) => {
if (e.target.checked) {
filteredDetectors.forEach(detector => {
currentDetectors.forEach(detector => {
if (!selectedDetectors.includes(detector.detector_id)) {
onDetectorSelect(detector.detector_id, true)
}
})
} else {
filteredDetectors.forEach(detector => {
currentDetectors.forEach(detector => {
if (selectedDetectors.includes(detector.detector_id)) {
onDetectorSelect(detector.detector_id, false)
}
@@ -135,7 +180,7 @@ const DetectorList: React.FC<DetectorListProps> = ({ objectId, selectedDetectors
</tr>
</thead>
<tbody>
{filteredDetectors.map((detector) => {
{currentDetectors.map((detector) => {
const isSelected = selectedDetectors.includes(detector.detector_id)
return (
@@ -183,9 +228,76 @@ const DetectorList: React.FC<DetectorListProps> = ({ objectId, selectedDetectors
</tbody>
</table>
</div>
{/* Пагинация */}
{totalPages > 1 && (
<div className="mt-6 flex flex-col items-center gap-4">
{/* Кнопки пагинации */}
<div className="flex items-center gap-2">
{/* Кнопка "Предыдущая" */}
<button
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
currentPage === 1
? 'text-gray-600 cursor-not-allowed'
: 'text-gray-300 hover:text-white hover:bg-[#1E293B]'
}`}
>
Предыдущая
</button>
{/* Номера страниц */}
{paginationRange.map((page, index) => {
if (page === '...') {
return (
<span key={`ellipsis-${index}`} className="px-3 py-2 text-gray-500">
...
</span>
)
}
const pageNumber = page as number
const isActive = pageNumber === currentPage
return (
<button
key={pageNumber}
onClick={() => handlePageChange(pageNumber)}
className={`min-w-[40px] px-3 py-2 rounded-lg text-sm font-medium transition-colors ${
isActive
? 'bg-blue-500 text-white'
: 'text-gray-300 hover:text-white hover:bg-[#1E293B]'
}`}
>
{pageNumber}
</button>
)
})}
{/* Кнопка "Следующая" */}
<button
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
currentPage === totalPages
? 'text-gray-600 cursor-not-allowed'
: 'text-gray-300 hover:text-white hover:bg-[#1E293B]'
}`}
>
Следующая
</button>
</div>
{/* Счётчик */}
<div className="text-sm text-gray-400">
Показано {startIndex + 1}-{Math.min(endIndex, filteredDetectors.length)} из {filteredDetectors.length} датчиков
</div>
</div>
)}
</div>
{/* Статы детекторров*/}
{/* Статы детекторов */}
<div className="mt-6 grid grid-cols-4 gap-4">
<div className="bg-[#161824] p-4 rounded-lg">
<div className="text-2xl font-bold text-white">{filteredDetectors.length}</div>