Инициализация в новом репозитории
This commit is contained in:
commit
eb1f18f53e
374 changed files with 226678 additions and 0 deletions
|
|
@ -0,0 +1,69 @@
|
|||
import styled from "styled-components"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
|
||||
const Container = styled.div`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 25px;
|
||||
text-align: center;
|
||||
|
||||
h1, h2, h3, p{
|
||||
margin: 0;
|
||||
}
|
||||
`
|
||||
|
||||
const Content = styled.div`
|
||||
max-width: 450px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 100px;
|
||||
button{
|
||||
width: 250px;
|
||||
}
|
||||
`
|
||||
|
||||
const Row = styled.div`
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
margin-left: 50px;
|
||||
`
|
||||
|
||||
const Column = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
`
|
||||
|
||||
export default function About() {
|
||||
const navigate = useNavigate()
|
||||
const redirect = (path: string) => {
|
||||
navigate(path)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Content>
|
||||
<Row>
|
||||
<h1>GoNetMan</h1>
|
||||
<p>v 0.0.1</p>
|
||||
</Row>
|
||||
|
||||
<Column>
|
||||
<h3>О программе</h3>
|
||||
GoNetMan - Это приложение представляющее собой панель управления сетевыми настройками в удобном и понятном графическом виде.
|
||||
</Column>
|
||||
|
||||
<p>Copyright © ООО "Гибридные офисные системы"</p>
|
||||
|
||||
<button onClick={()=>redirect('/')}>В меню</button>
|
||||
</Content>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import styled from "styled-components"
|
||||
import { IconComponent } from "../../../../const/buttons/icons"
|
||||
import { buttonNames } from "../../../../const/buttons/names"
|
||||
import { useWorkSpaceStore } from "../../../../store"
|
||||
import { useModalContext } from "../../../../store"
|
||||
import { useAdminStore } from "../../../../store/adminStore"
|
||||
import { ModalMap } from "../../../modalwindows/windowsType"
|
||||
import { QuitButton } from "../../../UI/buttons/quitButton"
|
||||
import { usePosStore } from "../../../../store/posStore"
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
background-color: ${(props) => props.theme.primary};
|
||||
padding: 0 5px;
|
||||
`
|
||||
|
||||
const ButtonContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const IconContainer = styled.div`
|
||||
padding: 10px;
|
||||
|
||||
svg{
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
|
||||
export const Header = () => {
|
||||
const {setNullPos} = usePosStore()
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ButtonContainer>
|
||||
<IconContainer>
|
||||
<IconComponent name={buttonNames[11]} width="30" height="30" onClick={()=>setNullPos(true)}/>
|
||||
</IconContainer>
|
||||
|
||||
<QuitButton size="small"/>
|
||||
</ButtonContainer>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import styled from "styled-components"
|
||||
import { MainButton } from "../../../UI/buttons/mainButton"
|
||||
import { useWorkSpaceStore } from "../../../../store"
|
||||
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||
import { buttonNames } from "../../../../const/buttons/names"
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${(props => props.theme.background)};
|
||||
background-image: url('./assets/images/back1.svg');
|
||||
background-size: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: bottom;
|
||||
`
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 52px;
|
||||
margin-bottom: 100px;
|
||||
`
|
||||
|
||||
const ButtonWrapper = styled.div`
|
||||
display: flex;
|
||||
gap: 75px;
|
||||
flex-direction: column;
|
||||
`
|
||||
|
||||
const ButtonContainer = styled.div`
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
flex-direction: column;
|
||||
`
|
||||
|
||||
const Title = styled.div`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 10px;
|
||||
|
||||
h1{
|
||||
font-size: 52px;
|
||||
}
|
||||
`
|
||||
|
||||
export const HelloWindow = () => {
|
||||
|
||||
const { setWorkModule } = useWorkSpaceStore()
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Container className="unselectable">
|
||||
<Title>
|
||||
<img src="./assets/images/logo.svg" alt="" style={{width: '152px'}}/>
|
||||
</Title>
|
||||
|
||||
|
||||
<ButtonWrapper>
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
title="Настройки сети"
|
||||
icon={buttonNames[3]}
|
||||
iconSize={{ width: '32', height: '32' }}
|
||||
onClick={() => setWorkModule(workSpaceNames[3])}
|
||||
/>
|
||||
<MainButton
|
||||
title="О программе"
|
||||
icon={buttonNames[4]}
|
||||
iconSize={{ width: '32', height: '32' }}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ButtonWrapper>
|
||||
</Container>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
import { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { useTheme } from 'styled-components';
|
||||
import { usePosStore } from '../../../../store/posStore';
|
||||
|
||||
import { ReactFlow, useNodesState, useEdgesState, Background, addEdge, applyEdgeChanges, Controls } from '@xyflow/react';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import '../../../../assets/css/flow.css'
|
||||
|
||||
import { nodeTypes } from '../../../customeNodes/types/types';
|
||||
import { ModalMap } from '../../../modalwindows/windowsType';
|
||||
|
||||
import {
|
||||
useGroupStore,
|
||||
useModalContext,
|
||||
usePannableStore
|
||||
} from '../../../../store'
|
||||
|
||||
import {
|
||||
autosavePosition,
|
||||
resetPosition,
|
||||
processNodeChanges,
|
||||
initNetworkIntData,
|
||||
groupSort,
|
||||
collapseGroup,
|
||||
updateWifiData,
|
||||
useInitCanvas
|
||||
} from '../../../../utils/nodeController';
|
||||
|
||||
import {
|
||||
ResponseData,
|
||||
ResponseNetwork,
|
||||
IEdge,
|
||||
Interfaces,
|
||||
} from '../../../../const/types/types';
|
||||
|
||||
type Props = {
|
||||
startConnection: boolean
|
||||
ws: string
|
||||
}
|
||||
|
||||
const proOptions = { hideAttribution: true, };
|
||||
|
||||
export const NetworkInterfaces = ({ startConnection, ws }: Props) => {
|
||||
|
||||
const initializationAttempt = useRef(0);
|
||||
const [requestError, setRequestError] = useState({ status: false, message: '' })
|
||||
|
||||
const { openModal, closeLoader } = useModalContext();
|
||||
const [loader, setLoader] = useState(true)
|
||||
const theme = useTheme();
|
||||
|
||||
const [socket, setSocket] = useState<WebSocket | null>(null);
|
||||
const [data, setData] = useState<ResponseData[]>([]);
|
||||
const [wifiData, setWifi] = useState<ResponseNetwork>()
|
||||
const [hasInited, setHasInited] = useState(false);
|
||||
const { initedNodes, initedEdges, handleInit } = useInitCanvas();
|
||||
|
||||
const [interfaces, setInterfaces] = useState<Interfaces[]>([]);
|
||||
const [edgeData, setEdgeData] = useState<IEdge[]>([]);
|
||||
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState([] as any[]);
|
||||
const [edges, setEdges] = useEdgesState([] as any[]);
|
||||
const { isPanDisabled } = usePannableStore()
|
||||
|
||||
const [changeArray, setChangeArray] = useState<any[]>([]);
|
||||
const { groups } = useGroupStore();
|
||||
const {isNullPos, setNullPos} = usePosStore()
|
||||
|
||||
useEffect(() => {
|
||||
if (startConnection) {
|
||||
|
||||
openModal(ModalMap['2'], 'Идет загрузка данных');
|
||||
|
||||
setNodes([])
|
||||
setEdges([])
|
||||
setHasInited(false)
|
||||
initializationAttempt.current = 0;
|
||||
|
||||
const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`);
|
||||
|
||||
let connectionTimeout;
|
||||
setSocket(newSocket);
|
||||
|
||||
connectionTimeout = setTimeout(() => {
|
||||
if (newSocket.readyState !== WebSocket.OPEN) {
|
||||
newSocket.close();
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
newSocket.onopen = () => {
|
||||
console.log('WebSocket connection established');
|
||||
};
|
||||
|
||||
newSocket.onmessage = (event) => {
|
||||
const newData = JSON.parse(event.data);
|
||||
|
||||
setData(() => [
|
||||
newData.activation,
|
||||
newData.deactivation,
|
||||
]);
|
||||
};
|
||||
|
||||
newSocket.onerror = (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
newSocket.onclose = () => {
|
||||
console.log('WebSocket connection closed');
|
||||
setRequestError({
|
||||
status: true,
|
||||
message: 'Соединение с сервером разорвано'
|
||||
})
|
||||
};
|
||||
|
||||
return () => {
|
||||
newSocket.close();
|
||||
closeLoader()
|
||||
};
|
||||
}
|
||||
}, [startConnection, ws]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (data.length > 0) {
|
||||
const { interfaces: newInterfaces, edgeData: newEdgeData } = initNetworkIntData(data);
|
||||
const activationData = data[0].interfaces;
|
||||
|
||||
const wifi = activationData.find((el: any) => el.type === 'WIFI');
|
||||
|
||||
setInterfaces(newInterfaces);
|
||||
setEdgeData(newEdgeData);
|
||||
if (wifi) { setWifi(wifi); }
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (interfaces.length > 0 && !hasInited) {
|
||||
handleInit(edgeData, interfaces)
|
||||
setHasInited(true)
|
||||
}
|
||||
}, [interfaces, edgeData, hasInited])
|
||||
|
||||
useEffect(() => {
|
||||
if (initializationAttempt.current === 0 && !requestError.status) {
|
||||
const checkInterval = setInterval(() => {
|
||||
|
||||
if(initedNodes.length> 0 && initedNodes.length<=4){
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
if (initedNodes.length > 0 && initedEdges.length > 0) {
|
||||
setNodes(initedNodes);
|
||||
setEdges(initedEdges);
|
||||
console.log("Ноды и эджи успешно инициализированы");
|
||||
initializationAttempt.current = 1;
|
||||
clearInterval(checkInterval);
|
||||
clearTimeout(timeoutId);
|
||||
} else {
|
||||
console.log("Попытка инициализации нод и эджей...");
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.error("Ошибка: слишком долгая загрузка данных");
|
||||
clearInterval(checkInterval);
|
||||
setRequestError({
|
||||
status: true,
|
||||
message: 'Превышено время ожидания'
|
||||
})
|
||||
}, 60000);
|
||||
|
||||
return () => {
|
||||
clearInterval(checkInterval);
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
}
|
||||
}, [initedNodes, initedEdges, requestError]);
|
||||
|
||||
useEffect(() => {
|
||||
if (nodes.length === 0) {
|
||||
initializationAttempt.current = 0;
|
||||
} else {
|
||||
initializationAttempt.current = 1;
|
||||
}
|
||||
}, [nodes]);
|
||||
|
||||
useEffect(() => {
|
||||
if ((nodes.length > 0 && loader == true)) {
|
||||
setLoader(false)
|
||||
closeLoader()
|
||||
}
|
||||
}, [nodes, loader])
|
||||
|
||||
const onEdgesChange = useCallback(
|
||||
(changes: any) => setEdges((eds) => applyEdgeChanges(changes, eds)),
|
||||
[setEdges],
|
||||
);
|
||||
|
||||
const onConnect = useCallback(
|
||||
(connection: any) => setEdges((eds) => addEdge(connection, eds)),
|
||||
[setEdges],
|
||||
);
|
||||
|
||||
const handleNodesChange = (changes: any) => {
|
||||
onNodesChange(changes);
|
||||
|
||||
const newData = processNodeChanges(changes, data);
|
||||
|
||||
if (newData) {
|
||||
setChangeArray((prevArray) => [...prevArray, newData]);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
autosavePosition(changeArray, socket);
|
||||
}
|
||||
setChangeArray([]);
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [changeArray, socket]);
|
||||
|
||||
useEffect(() => {
|
||||
const sortedGroups = groupSort(groups, nodes)
|
||||
setNodes(collapseGroup(sortedGroups, nodes));
|
||||
}, [groups]);
|
||||
|
||||
useEffect(() => {
|
||||
if (wifiData) {
|
||||
setNodes(updateWifiData(wifiData, nodes));
|
||||
}
|
||||
}, [wifiData])
|
||||
|
||||
useEffect(() => {
|
||||
if (requestError.status) {
|
||||
setTimeout(() => {
|
||||
openModal(ModalMap['3'], {
|
||||
message: requestError.message,
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
}, [requestError]);
|
||||
|
||||
useEffect(() => {
|
||||
if(isNullPos){
|
||||
if(socket && socket.readyState === WebSocket.OPEN){
|
||||
resetPosition(socket, setNullPos, nodes, setNodes)
|
||||
}
|
||||
}
|
||||
}, [isNullPos]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={handleNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
style={{ background: '#F7F9FB' }}
|
||||
minZoom={0}
|
||||
maxZoom={4}
|
||||
fitView
|
||||
fitViewOptions={{ padding: 0.1 }}
|
||||
attributionPosition="bottom-left"
|
||||
snapToGrid
|
||||
snapGrid={[20, 20]}
|
||||
panOnDrag={!isPanDisabled}
|
||||
nodeTypes={nodeTypes}
|
||||
proOptions={proOptions}
|
||||
>
|
||||
<Background gap={20} size={1} bgColor={theme.background} />
|
||||
<Controls fitViewOptions={{ padding: 0.1 }} />
|
||||
</ReactFlow>
|
||||
</>
|
||||
);
|
||||
};
|
||||
17
FRONT/DEV/src/components/workSpace/sideBar/sideBar.tsx
Normal file
17
FRONT/DEV/src/components/workSpace/sideBar/sideBar.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { ToolBar } from "./toolBar/toolBar"
|
||||
import { ToolSpace } from "./toolSpace/toolSpace"
|
||||
import styled from "styled-components"
|
||||
|
||||
const Container = styled.div`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
`
|
||||
|
||||
export const SideBar = ()=>{
|
||||
return(
|
||||
<Container>
|
||||
<ToolBar/>
|
||||
<ToolSpace/>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import styled from "styled-components"
|
||||
import { IconComponent as Icon } from "../../../../const/menus/sideBar/icons"
|
||||
import { useToolBar } from "../../../../hooks/useToolBar"
|
||||
import { useTheme } from "styled-components"
|
||||
|
||||
type Props = {
|
||||
menuId: string
|
||||
}
|
||||
|
||||
const Button = styled.div`
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
const IconContainer = styled.div`
|
||||
padding: 10px;
|
||||
`
|
||||
|
||||
export const ToolBarButton = ({ menuId }: Props) => {
|
||||
const { isActive, collapseMenu } = useToolBar(menuId);
|
||||
const theme = useTheme();
|
||||
|
||||
const iconColor = isActive ? theme.textColor : theme.active;
|
||||
|
||||
return (
|
||||
<Button onClick={collapseMenu}>
|
||||
<IconContainer>
|
||||
<Icon name={menuId} width="30" height="30" setColor={iconColor} />
|
||||
</IconContainer>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import { useToolBarStore } from "../../../../store"
|
||||
import { ToolBarButton } from "./toolBar-button"
|
||||
import styled from "styled-components"
|
||||
import ThemeButton from "../../../UI/buttons/toggletheme"
|
||||
import { IconComponent } from "../../../../const/buttons/icons"
|
||||
import { buttonNames } from "../../../../const/buttons/names"
|
||||
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||
|
||||
import { useModalContext } from "../../../../store"
|
||||
import { useAdminStore } from "../../../../store/adminStore"
|
||||
import { ModalMap } from "../../../modalwindows/windowsType"
|
||||
import { useWorkSpaceStore } from "../../../../store"
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: ${(props) => props.theme.primary};
|
||||
padding: 5px 0;
|
||||
`
|
||||
|
||||
const ButtonContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const Button = styled.div`
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
const IconContainer = styled.div`
|
||||
padding: 10px;
|
||||
color: ${props => props.theme.text};
|
||||
`
|
||||
|
||||
export const ToolBar = () => {
|
||||
const { toolBar } = useToolBarStore()
|
||||
const toolBarMenu = toolBar?.toolSpace
|
||||
|
||||
const { openModal } = useModalContext()
|
||||
const {isAdmin} = useAdminStore()
|
||||
const {setWorkModule} = useWorkSpaceStore()
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ButtonContainer>
|
||||
{toolBarMenu && toolBarMenu.map((menu) => (
|
||||
<ToolBarButton menuId={menu.id} key={menu.id} />
|
||||
))}
|
||||
</ButtonContainer>
|
||||
|
||||
<ButtonContainer>
|
||||
|
||||
<Button>
|
||||
<IconContainer>
|
||||
<IconComponent name={isAdmin ? buttonNames[10] : buttonNames[13]} width="30" height="30" onClick={() => openModal(ModalMap['5'])}/>
|
||||
</IconContainer>
|
||||
</Button>
|
||||
|
||||
<ThemeButton />
|
||||
|
||||
<Button>
|
||||
<IconContainer>
|
||||
<IconComponent name={buttonNames[4]} width="30" height="30" onClick={()=>setWorkModule(workSpaceNames[2])}/>
|
||||
</IconContainer>
|
||||
</Button>
|
||||
</ButtonContainer>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
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;
|
||||
|
||||
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)
|
||||
setIpAddress(data)
|
||||
};
|
||||
|
||||
const handleDelete = (number: number, adress: string) => {
|
||||
deleteDevice(number)
|
||||
|
||||
|
||||
if(adress == ipAddress){
|
||||
setIpAddress('localhost')
|
||||
}
|
||||
};
|
||||
|
||||
const switchWs = async (ip: string) => {
|
||||
// Проверяем доступность WebSocket
|
||||
const isAvailable = await checkWebSocketAvailability(ip);
|
||||
if (!isAvailable) {
|
||||
openModal(ModalMap['3'], {
|
||||
message: `Не удалось подключиться к серверу по адресу ${ip}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Если WebSocket доступен, обновляем IP и переключаем модуль
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import { useToolBarStore } from "../../../../store"
|
||||
import { useToolBar } from "../../../../hooks/useToolBar"
|
||||
import { Space } from "../../../../const/menus/sideBar/components"
|
||||
import styled from "styled-components"
|
||||
|
||||
const Container = styled.div`
|
||||
background-color: ${(props) => props.theme.secondary};
|
||||
width: 250px;
|
||||
font-size: 14px;
|
||||
flex: 3;
|
||||
`
|
||||
|
||||
export const ToolSpace = () => {
|
||||
const { toolBar } = useToolBarStore()
|
||||
const name = toolBar?.current || ''
|
||||
const { isActive, isOpen } = useToolBar(name)
|
||||
|
||||
if (!isActive || !isOpen) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Space name={name} />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
82
FRONT/DEV/src/components/workSpace/workSpace.tsx
Normal file
82
FRONT/DEV/src/components/workSpace/workSpace.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { useEffect, useState } from "react"
|
||||
import { HelloWindow } from "./modules/appModules/helloWindow";
|
||||
import { NetworkInterfaces } from "./modules/workModules/networkInterfaces";
|
||||
import styled from "styled-components"
|
||||
import { useIpStore, useToolBarStore } from "../../store";
|
||||
import { SideBar } from "./sideBar/sideBar";
|
||||
import { useWorkSpaceStore } from "../../store";
|
||||
import { workSpaceNames } from "../../const/workspace/names";
|
||||
import { Header } from "./modules/appModules/header";
|
||||
import { ReactFlowProvider } from "@xyflow/react";
|
||||
|
||||
const Section = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
`
|
||||
|
||||
const FlowContainer = styled.div`
|
||||
display: flex;
|
||||
height: 100%;
|
||||
`
|
||||
|
||||
const WorkSection = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
`
|
||||
|
||||
export default function WorkSpace() {
|
||||
const { ipAddress, setIpAddress } = useIpStore();
|
||||
const { workModule } = useWorkSpaceStore()
|
||||
const [currentWs, setCurrentWs] = useState(ipAddress);
|
||||
const initMenus = useToolBarStore((state) => state.initialize);
|
||||
const initModule = useWorkSpaceStore((state) => state.initialize)
|
||||
|
||||
useEffect(() => {
|
||||
if (!localStorage.getItem('toolBar-storage')) {
|
||||
initMenus();
|
||||
}
|
||||
}, [initMenus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!localStorage.getItem('workSpace-storage')) {
|
||||
initModule();
|
||||
}
|
||||
}, [initModule]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentWs(ipAddress);
|
||||
}, [ipAddress]);
|
||||
|
||||
useEffect(() => {
|
||||
setIpAddress(currentWs);
|
||||
}, [currentWs]);
|
||||
|
||||
console.log('aboba');
|
||||
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<FlowContainer>
|
||||
<SideBar />
|
||||
|
||||
{workModule === workSpaceNames[1] && <HelloWindow /> }
|
||||
|
||||
{workModule === workSpaceNames[3] &&
|
||||
<WorkSection>
|
||||
<Header />
|
||||
<ReactFlowProvider>
|
||||
<NetworkInterfaces
|
||||
key={currentWs}
|
||||
startConnection={true}
|
||||
ws={currentWs}
|
||||
/>
|
||||
</ReactFlowProvider>
|
||||
</WorkSection>
|
||||
}
|
||||
</FlowContainer>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue