244 lines
No EOL
7.3 KiB
TypeScript
244 lines
No EOL
7.3 KiB
TypeScript
import styled from "styled-components"
|
||
import { useState, useEffect } from "react"
|
||
import { useModalContext } from "../../../../store"
|
||
import { ModalMap } from "../../../modalwindows/windowsType"
|
||
import { NetworkService } from "../../../../api/request"
|
||
import { AnimDot } from "../../../UI/animDot"
|
||
import { useMenu } from "../../../../hooks/useMenu"
|
||
import { useIpStore, useDevicesStore } from "../../../../store"
|
||
import { menusNames } from "../../../../const/menus/othersMenus/names"
|
||
import { useWorkSpaceStore } from "../../../../store"
|
||
import { workSpaceNames } from "../../../../const/workspace/names"
|
||
import { IconComponent } from "../../../../const/buttons/icons"
|
||
import { buttonNames } from "../../../../const/buttons/names"
|
||
|
||
const Container = styled.div`
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 15px;
|
||
padding: 20px;
|
||
width: 210px;
|
||
p{
|
||
margin: 0;
|
||
}
|
||
`
|
||
|
||
const Title = styled.div`
|
||
padding: 5px 0;
|
||
width: 100%;
|
||
text-align: center;
|
||
border-bottom: 1px solid ${props => props.theme.text};
|
||
`
|
||
|
||
const DeviceBlock = styled.div`
|
||
border-radius: 8px;
|
||
border: 2px solid ${props => props.theme.accent};
|
||
display: flex;
|
||
flex-direction: column;
|
||
width: 100%;
|
||
|
||
.paddingP{
|
||
padding: 7px 5px;
|
||
}
|
||
`
|
||
|
||
const DeviceControl = styled.div`
|
||
padding: 5px 8px;
|
||
background: ${props => props.theme.accent};
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
color: white;
|
||
cursor: pointer;
|
||
`
|
||
|
||
const PlusBlock = styled.div`
|
||
font-size: 24px;
|
||
cursor: pointer;
|
||
border-radius: 8px;
|
||
border: 2px dotted ${props => props.theme.accent};
|
||
width: 100%;
|
||
text-align: center;
|
||
padding: 5px 0;
|
||
`
|
||
|
||
const Text = styled.div`
|
||
display: flex;
|
||
gap: 3px;
|
||
align-items: center;
|
||
`
|
||
|
||
const DevicesContainer = styled.div`
|
||
width: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
`
|
||
|
||
const DevicesHeader = styled.div`
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
|
||
.hControl{
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
}
|
||
`
|
||
|
||
const DevicesContent = styled.div<{ $isactive: string }>`
|
||
${props => props.$isactive === 'true' && `display: flex;`}
|
||
${props => props.$isactive === 'false' && `display: none;`}
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 15px;
|
||
`
|
||
|
||
interface CurrentDevice {
|
||
hostname: string
|
||
address: string
|
||
}
|
||
|
||
export const DevicesBar = () => {
|
||
const { openModal } = useModalContext();
|
||
const { setIpAddress, ipAddress } = useIpStore();
|
||
const { devices, addDevice, deleteDevice } = useDevicesStore()
|
||
const { isOpen, collapseMenu } = useMenu(menusNames[1]);
|
||
const { setWorkModule } = useWorkSpaceStore()
|
||
|
||
const [currentDevice, setCurrentDevice] = useState<CurrentDevice>({
|
||
address: 'localhost',
|
||
hostname: ''
|
||
})
|
||
|
||
const checkWebSocketAvailability = async (ip: string): Promise<boolean> => {
|
||
return new Promise((resolve) => {
|
||
const testSocket = new WebSocket(`wss://${ip}:8001/api/v1`);
|
||
const timeout = setTimeout(() => {
|
||
testSocket.close();
|
||
resolve(false);
|
||
}, 500
|
||
);
|
||
|
||
testSocket.onopen = () => {
|
||
clearTimeout(timeout);
|
||
testSocket.close();
|
||
resolve(true);
|
||
};
|
||
|
||
testSocket.onerror = () => {
|
||
clearTimeout(timeout);
|
||
testSocket.close();
|
||
resolve(false);
|
||
};
|
||
});
|
||
};
|
||
|
||
useEffect(() => {
|
||
const fetchHostame = async () => {
|
||
let response = await NetworkService.getHostname();
|
||
if (response.status === 200) {
|
||
setCurrentDevice(prevData => ({
|
||
...prevData,
|
||
hostname: response.data.hostname
|
||
}))
|
||
}
|
||
};
|
||
fetchHostame();
|
||
}, [])
|
||
|
||
const handleAdd = () => {
|
||
openModal(ModalMap['4'], {
|
||
onSave: handleAddDeviceComplete,
|
||
});
|
||
};
|
||
|
||
const handleAddDeviceComplete = (data: string) => {
|
||
addDevice(data);
|
||
switchWs(data);
|
||
};
|
||
|
||
const handleDelete = (number: number, adress: string) => {
|
||
deleteDevice(number)
|
||
|
||
|
||
if(adress == ipAddress){
|
||
setIpAddress('localhost')
|
||
}
|
||
};
|
||
|
||
const switchWs = async (ip: string) => {
|
||
const isAvailable = await checkWebSocketAvailability(ip);
|
||
if (!isAvailable) {
|
||
openModal(ModalMap['3'], {
|
||
message: `Не удалось подключиться к серверу по адресу ${ip}`,
|
||
});
|
||
return;
|
||
}
|
||
|
||
setIpAddress(ip);
|
||
setWorkModule(workSpaceNames[3]);
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (devices.length === 0) {
|
||
setIpAddress('localhost');
|
||
}
|
||
}, [devices]);
|
||
|
||
return (
|
||
<Container>
|
||
<Title>
|
||
<p className="unselectable">Доступные узлы</p>
|
||
</Title>
|
||
|
||
<DeviceBlock>
|
||
<DeviceControl className="unselectable" onClick={() => switchWs(currentDevice.address)}>
|
||
<p>Корневой адрес</p>
|
||
</DeviceControl>
|
||
{currentDevice.hostname == '' ? (
|
||
<Text className="paddingP">Название: Загрузка<AnimDot /></Text>
|
||
) : (
|
||
<p className="paddingP">Название: {currentDevice.hostname}</p>
|
||
)}
|
||
</DeviceBlock>
|
||
|
||
<DevicesContainer>
|
||
<DevicesHeader className="unselectable" onClick={collapseMenu}>
|
||
<p>Дополнительные узлы</p>
|
||
<IconComponent name={isOpen ? buttonNames[5] : buttonNames[6]} />
|
||
</DevicesHeader>
|
||
|
||
<DevicesContent $isactive={isOpen.toString()} >
|
||
{devices.length > 0 &&
|
||
devices.map((device, index) => (
|
||
<DeviceBlock key={index}>
|
||
<DeviceControl className="unselectable" onClick={() => switchWs(device.address)}>
|
||
<p>Узел {device.number}</p>
|
||
<IconComponent
|
||
width="20"
|
||
height="20"
|
||
name={buttonNames[2]}
|
||
setColor="white"
|
||
onClick={(e: any) => {
|
||
e.stopPropagation(); // Предотвращаем всплытие события
|
||
handleDelete(device.number, device.address);
|
||
}}
|
||
/>
|
||
</DeviceControl>
|
||
<p className="paddingP">Адрес: {device.address}</p>
|
||
</DeviceBlock>
|
||
))
|
||
}
|
||
|
||
<PlusBlock className="unselectable" onClick={handleAdd}>
|
||
+
|
||
</PlusBlock>
|
||
</DevicesContent>
|
||
|
||
</DevicesContainer>
|
||
</Container>
|
||
)
|
||
} |