Добавлена иконка на боковой тултип для отображения/скрытия подсветки датчиков на модели

This commit is contained in:
2026-02-03 13:13:16 +03:00
parent 81b361b282
commit eccc564cc7
9 changed files with 2135 additions and 9 deletions

View File

@@ -68,6 +68,7 @@ export interface NavigationStore {
showNotifications: boolean
showListOfDetectors: boolean
showSensors: boolean
showSensorHighlights: boolean
selectedDetector: DetectorType | null
showDetectorMenu: boolean
@@ -100,6 +101,8 @@ export interface NavigationStore {
closeListOfDetectors: () => void
openSensors: () => void
closeSensors: () => void
toggleSensorHighlights: () => void
setSensorHighlights: (show: boolean) => void
closeAllMenus: () => void
clearSelections: () => void
@@ -137,6 +140,7 @@ const useNavigationStore = create<NavigationStore>()(
showNotifications: false,
showListOfDetectors: false,
showSensors: false,
showSensorHighlights: true,
selectedDetector: null,
showDetectorMenu: false,
@@ -316,6 +320,9 @@ const useNavigationStore = create<NavigationStore>()(
currentSubmenu: null
}),
toggleSensorHighlights: () => set((state) => ({ showSensorHighlights: !state.showSensorHighlights })),
setSensorHighlights: (show: boolean) => set({ showSensorHighlights: show }),
closeAllMenus: () => {
set({
showMonitoring: false,

View File

@@ -0,0 +1,373 @@
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
import type { Zone } from '@/app/types'
export interface DetectorType {
detector_id: number
name: string
serial_number: string
object: string
status: string
type: string
detector_type: string
location: string
floor: number
checked: boolean
notifications: Array<{
id: number
type: string
message: string
timestamp: string
acknowledged: boolean
priority: string
}>
}
interface NotificationType {
id: number
detector_id: number
detector_name: string
type: string
status: string
message: string
timestamp: string
location: string
object: string
acknowledged: boolean
priority: string
}
interface AlertType {
id: number
detector_id: number
detector_name: string
type: string
status: string
message: string
timestamp: string
location: string
object: string
acknowledged: boolean
priority: string
}
export interface NavigationStore {
currentObject: { id: string | undefined; title: string | undefined }
navigationHistory: string[]
currentSubmenu: string | null
currentModelPath: string | null
// Состояния Зон
currentZones: Zone[]
zonesCache: Record<string, Zone[]>
zonesLoading: boolean
zonesError: string | null
showMonitoring: boolean
showFloorNavigation: boolean
showNotifications: boolean
showListOfDetectors: boolean
showSensors: boolean
selectedDetector: DetectorType | null
showDetectorMenu: boolean
selectedNotification: NotificationType | null
showNotificationDetectorInfo: boolean
selectedAlert: AlertType | null
showAlertMenu: boolean
setCurrentObject: (id: string | undefined, title: string | undefined) => void
clearCurrentObject: () => void
addToHistory: (path: string) => void
goBack: () => string | null
setCurrentModelPath: (path: string) => void
setCurrentSubmenu: (submenu: string | null) => void
clearSubmenu: () => void
// Действия с зонами
loadZones: (objectId: string) => Promise<void>
setZones: (zones: Zone[]) => void
clearZones: () => void
openMonitoring: () => void
closeMonitoring: () => void
openFloorNavigation: () => void
closeFloorNavigation: () => void
openNotifications: () => void
closeNotifications: () => void
openListOfDetectors: () => void
closeListOfDetectors: () => void
openSensors: () => void
closeSensors: () => void
closeAllMenus: () => void
clearSelections: () => void
setSelectedDetector: (detector: DetectorType | null) => void
setShowDetectorMenu: (show: boolean) => void
setSelectedNotification: (notification: NotificationType | null) => void
setShowNotificationDetectorInfo: (show: boolean) => void
setSelectedAlert: (alert: AlertType | null) => void
setShowAlertMenu: (show: boolean) => void
isOnNavigationPage: () => boolean
getCurrentRoute: () => string | null
getActiveSidebarItem: () => number
}
const useNavigationStore = create<NavigationStore>()(
persist(
(set, get) => ({
currentObject: {
id: undefined,
title: undefined,
},
navigationHistory: [],
currentSubmenu: null,
currentModelPath: null,
currentZones: [],
zonesCache: {},
zonesLoading: false,
zonesError: null,
showMonitoring: false,
showFloorNavigation: false,
showNotifications: false,
showListOfDetectors: false,
showSensors: false,
selectedDetector: null,
showDetectorMenu: false,
selectedNotification: null,
showNotificationDetectorInfo: false,
selectedAlert: null,
showAlertMenu: false,
setCurrentObject: (id: string | undefined, title: string | undefined) =>
set({ currentObject: { id, title } }),
clearCurrentObject: () =>
set({ currentObject: { id: undefined, title: undefined } }),
setCurrentModelPath: (path: string) => set({ currentModelPath: path }),
addToHistory: (path: string) => {
const { navigationHistory } = get()
const newHistory = [...navigationHistory, path]
if (newHistory.length > 10) {
newHistory.shift()
}
set({ navigationHistory: newHistory })
},
goBack: () => {
const { navigationHistory } = get()
if (navigationHistory.length > 1) {
const newHistory = [...navigationHistory]
newHistory.pop()
const previousPage = newHistory.pop()
set({ navigationHistory: newHistory })
return previousPage || null
}
return null
},
setCurrentSubmenu: (submenu: string | null) =>
set({ currentSubmenu: submenu }),
clearSubmenu: () =>
set({ currentSubmenu: null }),
loadZones: async (objectId: string) => {
const cache = get().zonesCache
const cached = cache[objectId]
const hasCached = Array.isArray(cached) && cached.length > 0
if (hasCached) {
// Показываем кэшированные зоны сразу, но обновляем в фоне
set({ currentZones: cached, zonesLoading: true, zonesError: null })
} else {
set({ zonesLoading: true, zonesError: null })
}
try {
const res = await fetch(`/api/get-zones?objectId=${encodeURIComponent(objectId)}`, { cache: 'no-store' })
const text = await res.text()
let payload: string | Record<string, unknown>
try { payload = JSON.parse(text) } catch { payload = text }
if (!res.ok) throw new Error(typeof payload === 'string' ? payload : (payload?.error as string || 'Не удалось получить зоны'))
const zones: Zone[] = typeof payload === 'string' ? [] :
Array.isArray(payload?.data) ? payload.data as Zone[] :
(payload?.data && typeof payload.data === 'object' && 'zones' in payload.data ? (payload.data as { zones?: Zone[] }).zones :
payload?.zones ? payload.zones as Zone[] : []) || []
const normalized = zones.map((z) => ({
...z,
image_path: z.image_path ?? null,
}))
set((state) => ({
currentZones: normalized,
zonesCache: { ...state.zonesCache, [objectId]: normalized },
zonesLoading: false,
zonesError: null,
}))
} catch (e: unknown) {
set({ zonesLoading: false, zonesError: (e as Error)?.message || 'Ошибка при загрузке зон' })
}
},
setZones: (zones: Zone[]) => set({ currentZones: zones }),
clearZones: () => set({ currentZones: [] }),
openMonitoring: () => {
set({
showMonitoring: true,
showFloorNavigation: false,
showNotifications: false,
showListOfDetectors: false,
currentSubmenu: 'monitoring',
showDetectorMenu: false,
selectedDetector: null,
showNotificationDetectorInfo: false,
selectedNotification: null,
zonesError: null // Очищаем ошибку зон при открытии мониторинга
})
const objId = get().currentObject.id
if (objId) {
// Вызываем загрузку зон сразу, но обновляем в фоне
get().loadZones(objId)
}
},
closeMonitoring: () => set({
showMonitoring: false,
currentSubmenu: null
}),
openFloorNavigation: () => set({
showFloorNavigation: true,
showMonitoring: false,
showNotifications: false,
showListOfDetectors: false,
currentSubmenu: 'floors',
showNotificationDetectorInfo: false,
selectedNotification: null
}),
closeFloorNavigation: () => set({
showFloorNavigation: false,
showDetectorMenu: false,
selectedDetector: null,
currentSubmenu: null
}),
openNotifications: () => set({
showNotifications: true,
showMonitoring: false,
showFloorNavigation: false,
showListOfDetectors: false,
currentSubmenu: 'notifications',
showDetectorMenu: false,
selectedDetector: null
}),
closeNotifications: () => set({
showNotifications: false,
showNotificationDetectorInfo: false,
selectedNotification: null,
currentSubmenu: null
}),
openListOfDetectors: () => set({
showListOfDetectors: true,
showMonitoring: false,
showFloorNavigation: false,
showNotifications: false,
currentSubmenu: 'detectors',
showDetectorMenu: false,
selectedDetector: null,
showNotificationDetectorInfo: false,
selectedNotification: null
}),
closeListOfDetectors: () => set({
showListOfDetectors: false,
showDetectorMenu: false,
selectedDetector: null,
currentSubmenu: null
}),
openSensors: () => set({
showSensors: true,
showMonitoring: false,
showFloorNavigation: false,
showNotifications: false,
showListOfDetectors: false,
currentSubmenu: 'sensors',
showDetectorMenu: false,
selectedDetector: null,
showNotificationDetectorInfo: false,
selectedNotification: null
}),
closeSensors: () => set({
showSensors: false,
showDetectorMenu: false,
selectedDetector: null,
currentSubmenu: null
}),
closeAllMenus: () => {
set({
showMonitoring: false,
showFloorNavigation: false,
showNotifications: false,
showListOfDetectors: false,
showSensors: false,
currentSubmenu: null,
});
get().clearSelections();
},
clearSelections: () => set({
selectedDetector: null,
showDetectorMenu: false,
selectedAlert: null,
showAlertMenu: false,
}),
setSelectedDetector: (detector: DetectorType | null) => set({ selectedDetector: detector }),
setShowDetectorMenu: (show: boolean) => set({ showDetectorMenu: show }),
setSelectedNotification: (notification: NotificationType | null) => set({ selectedNotification: notification }),
setShowNotificationDetectorInfo: (show: boolean) => set({ showNotificationDetectorInfo: show }),
setSelectedAlert: (alert: AlertType | null) => set({ selectedAlert: alert }),
setShowAlertMenu: (show: boolean) => set({ showAlertMenu: show }),
isOnNavigationPage: () => {
const { navigationHistory } = get()
const currentRoute = navigationHistory[navigationHistory.length - 1]
return currentRoute === '/navigation'
},
getCurrentRoute: () => {
const { navigationHistory } = get()
return navigationHistory[navigationHistory.length - 1] || null
},
getActiveSidebarItem: () => {
const { showMonitoring, showFloorNavigation, showNotifications, showListOfDetectors, showSensors } = get()
if (showMonitoring) return 3 // Зоны Мониторинга
if (showFloorNavigation) return 4 // Навигация по этажам
if (showNotifications) return 5 // Уведомления
if (showListOfDetectors) return 7 // Список датчиков
if (showSensors) return 8 // Сенсоры
return 2 // Навигация (базовая)
}
}),
{
name: 'navigation-store',
}
)
)
export default useNavigationStore