Добавление переключения DCHP

This commit is contained in:
test 2025-06-26 17:31:34 +05:00
parent 3d4c59c5e5
commit e7d8652ac0
13 changed files with 241 additions and 26 deletions

View file

@ -11,11 +11,11 @@ const PaginateWrapper = styled.div`
align-items: center; align-items: center;
gap: 10px; gap: 10px;
` `
const Page = styled.button` const Page = styled.button<{ active?: boolean }>`
width: 36px; width: 36px;
height: 36px; height: 36px;
background: ${(props => props.theme.primary)}; background: ${(props) => props.active ? props.theme.borderColor : props.theme.primary};
color: ${(props => props.theme.textColor)}; color: ${(props) => props.active ? props.theme.primary : props.theme.textColor};
border: 1px solid ${(props => props.theme.borderColor)}; border: 1px solid ${(props => props.theme.borderColor)};
font-size: 16px; font-size: 16px;
cursor: pointer; cursor: pointer;
@ -23,7 +23,7 @@ const Page = styled.button`
transition: .4s ease; transition: .4s ease;
&:hover { &:hover {
background: ${(props => props.theme.background)}; background: ${(props) => props.active ? props.theme.textColor : props.theme.background};;
} }
` `
@ -32,9 +32,13 @@ interface PaginationProps {
totalPages: number; totalPages: number;
onPrevPage: () => void; onPrevPage: () => void;
onNextPage: () => void; onNextPage: () => void;
onPageChange: (page: number) => void;
} }
const Pagination = ({currentPage, totalPages, onPrevPage, onNextPage}: PaginationProps) => { const Pagination = ({currentPage, totalPages, onPrevPage, onNextPage, onPageChange}: PaginationProps) => {
const pageNumbers = Array.from({ length: totalPages }, (_, i) => i + 1);
return ( return (
<PaginateWrapper> <PaginateWrapper>
<Page <Page
@ -47,7 +51,15 @@ const Pagination = ({currentPage, totalPages, onPrevPage, onNextPage}: Paginatio
name={buttonNames[17]} name={buttonNames[17]}
/> />
</Page> </Page>
{pageNumbers.map((page, i) => (
<Page
key={i}
onClick={() => onPageChange(page)}
active={currentPage === page}
>
{page}
</Page>
))}
<Page <Page
onClick={onNextPage} onClick={onNextPage}
disabled={currentPage === totalPages} disabled={currentPage === totalPages}

View file

@ -3,12 +3,13 @@ import { Handle, Position } from '@xyflow/react';
import { Ip } from './style'; import { Ip } from './style';
import { Row, IpTitle, IpContent } from './style'; import { Row, IpTitle, IpContent } from './style';
import Input from '../../../../UI/inputs/input'; import Input from '../../../../UI/inputs/input';
import { useModalContext, usePannableStore } from '../../../../../store'; import {useIpStore, useModalContext, usePannableStore} from '../../../../../store';
import { ModalMap } from '../../../../modalwindows/windowsType'; import { ModalMap } from '../../../../modalwindows/windowsType';
import { validateIPAddress, validateSameSubnet } from '../../../../../utils/nodeController/validator'; import { validateIPAddress, validateSameSubnet } from '../../../../../utils/nodeController/validator';
import {changeIp} from '../../../../../const/types/types' import {changeIp} from '../../../../../const/types/types'
import { IconComponent } from "../../../../../const/buttons/icons"; import { IconComponent } from "../../../../../const/buttons/icons";
import { buttonNames } from "../../../../../const/buttons/names"; import { buttonNames } from "../../../../../const/buttons/names";
import {NetworkService} from "../../../../../api/request";
interface IIp { interface IIp {
ipType: string ipType: string
@ -45,6 +46,7 @@ export const IpNode = ({ data }: CustomNodeProps) => {
const { toggleEditingMode } = usePannableStore(); const { toggleEditingMode } = usePannableStore();
const [isInput, setIsInput] = useState(false) const [isInput, setIsInput] = useState(false)
const { openModal } = useModalContext(); const { openModal } = useModalContext();
const {ipAddress} = useIpStore();
const [ipData, setIpData] = useState<changeIp>( const [ipData, setIpData] = useState<changeIp>(
{ {
@ -146,7 +148,20 @@ export const IpNode = ({ data }: CustomNodeProps) => {
setIsInput(false) setIsInput(false)
}; };
const switchMethod = async ()=>{
const methodDCHP ={
"connection_id": ipData.connection,
"ipv4_updates": {
"ipv4.method": "auto",
}
}
try {
await NetworkService.saveIp(ipAddress, methodDCHP);
window.location.reload();
}catch (err){
console.error(err)
}
}
return ( return (
<> <>
@ -170,7 +185,10 @@ export const IpNode = ({ data }: CustomNodeProps) => {
</Row> </Row>
) : ( ) : (
<IconComponent name={buttonNames[7]} onClick={handleClick} width='18' height='18' setColor='white'/> <Row>
{typeMap[data.ipData.ipType] ==='Static' && <IconComponent name={buttonNames[18]} onClick={switchMethod} width='18' height='18' setColor='white'/>}
<IconComponent name={buttonNames[7]} onClick={handleClick} width='18' height='18' setColor='white'/>
</Row>
)} )}
</IpTitle> </IpTitle>

View file

@ -0,0 +1,47 @@
import { useSettingsStore } from '../stores/settingsStore';
export const SettingItem = ({ setting }: { setting: Setting }) => {
const { updateSetting } = useSettingsStore();
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
) => {
const value =
setting.type === 'boolean'
? (e.target as HTMLInputElement).checked
: e.target.value;
updateSetting(setting.id, value);
};
return (
<div>
<label>{setting.label}</label>
{setting.type === 'boolean' && (
<input
type="checkbox"
checked={setting.value}
onChange={handleChange}
/>
)}
{setting.type === 'select' && setting.options && (
<select value={setting.value} onChange={handleChange}>
{setting.options.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
)}
{setting.type === 'text' && (
<input
type="text"
value={setting.value}
onChange={handleChange}
/>
)}
</div>
);
};

View file

@ -0,0 +1,72 @@
// components/modalContainers/NoteDevice.tsx
import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../style";
import { MainButton } from "../../UI/buttons/mainButton";
import {useEffect, useState} from "react";
import styled from "styled-components";
import { useSettingsStore } from "../../../store/settingsStore";
interface Props {
onClose: () => void;
}
const SectionsSidebar = styled.div`
display: flex;
justify-content: start;
align-items: start;
flex-direction: column;
gap: 12px;
width: 20%;
`
const SectionsBTN = styled.button`
font-size: 18px;
border: none;
cursor: pointer;
`
const SectionActive = styled.div`
width: 80%;
background-color: red;
`
const sections = [
{id: 'nodesNetwork', label: 'Узлы подсети'},
{id: 'wifiNetwork', label: 'Точки доступа'},
{id: 'appearance', label: 'Внешний вид'},
]
export const Settings = ({onClose}: Props) => {
const {activeSection, setActiveSection, initializeSettings} = useSettingsStore();
useEffect(() => {
initializeSettings();
}, [initializeSettings]);
return (
<ModalContainer style={{ height: '60vh', width: '50vw'}}>
<ModalTitle>
<h3>Настройки</h3>
</ModalTitle>
<ModalContent style={{ display: 'flex', height:"100%", gap: '8px' }}>
<SectionsSidebar>
{sections.map((section) => (
<SectionsBTN>{section.label}</SectionsBTN>
))}
</SectionsSidebar>
<SectionActive>
{activeSection && <SettingItem setting={} />}
</SectionActive>
</ModalContent>
<ButtonContainer>
<MainButton
type="button"
size="small"
title='Сохранить'
onClick={onClose}
/>
</ButtonContainer>
</ModalContainer>
);
};

View file

@ -120,7 +120,7 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => {
); );
useDevicesStore.setState({ devices: updatedDevices }); useDevicesStore.setState({ devices: updatedDevices });
} }
onClose() onClose()
} }
} catch (err) { } catch (err) {

View file

@ -7,6 +7,7 @@ import {NoteDevice} from "./modalContainers/NoteDevice";
import { NoteWifi } from "./modalContainers/NoteWifi"; import { NoteWifi } from "./modalContainers/NoteWifi";
import {ConnectNetwork} from "./modalContainers/ConnectNetwork"; import {ConnectNetwork} from "./modalContainers/ConnectNetwork";
import {DisconnectConfirm} from "./modalContainers/disconnectConfirm"; import {DisconnectConfirm} from "./modalContainers/disconnectConfirm";
import {Settings} from "./modalContainers/Settings";
type Props = { type Props = {
isOpen: boolean, isOpen: boolean,
@ -74,6 +75,11 @@ export default function WindowComponent({ isOpen, type, data }: Props) {
{type === ModalMap['5'] && ( {type === ModalMap['5'] && (
<RightsConfirm onClose={closeLoader} /> <RightsConfirm onClose={closeLoader} />
)} )}
{type === ModalMap['10'] && (
<Settings
onClose={closeLoader}
/>
)}
</ModalWindow > </ModalWindow >
} }
</> </>

View file

@ -13,7 +13,7 @@ export const ModalWindow = styled.div`
height: 100%; height: 100%;
` `
export const ModalContainer = styled.div<{height?: string}>` export const ModalContainer = styled.div<{height?: string, width?:string}>`
border-radius: 12px; border-radius: 12px;
background: ${(props)=>props.theme.secondary}; background: ${(props)=>props.theme.secondary};
color: ${(props=>props.theme.textColor)}; color: ${(props=>props.theme.textColor)};
@ -25,7 +25,7 @@ export const ModalContainer = styled.div<{height?: string}>`
justify-content: space-between; justify-content: space-between;
gap: 15px; gap: 15px;
height: ${(props) => props.height ? props.height : '230px'}; height: ${(props) => props.height ? props.height : '230px'};
width: 520px; width: ${(props) => props.width ? props.width : '520px'};
` `
export const ModalTitle = styled.div` export const ModalTitle = styled.div`

View file

@ -1,15 +1,15 @@
import React, {useState} from 'react'; import React, {useState} from 'react';
import { DeviceUser } from '../../../store/useDeviceStore'; import { DeviceUser } from '../../../../../../store/useDeviceStore';
interface TableScannerProps { interface TableScannerProps {
devices: DeviceUser[]; devices: DeviceUser[];
} }
import styled from 'styled-components'; import styled from 'styled-components';
import {IconComponent} from "../../../const/buttons/icons"; import {IconComponent} from "../../../../../../const/buttons/icons";
import {buttonNames} from "../../../const/buttons/names"; import {buttonNames} from "../../../../../../const/buttons/names";
import {ModalMap} from "../../modalwindows/windowsType"; import {ModalMap} from "../../../../../modalwindows/windowsType";
import {useModalContext} from "../../../store"; import {useModalContext} from "../../../../../../store";
import Pagination from "../Pagination"; import Pagination from "../../../../../UI/Pagination";
const TableContainer = styled.div` const TableContainer = styled.div`
width: 100%; width: 100%;
@ -76,7 +76,7 @@ const TableScanner = ({devices}:TableScannerProps) => {
// Пагинация // Пагинация
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [devicesPerPage] = useState(10); const [devicesPerPage] = useState(15);
const indexOfLastDevice = currentPage * devicesPerPage; const indexOfLastDevice = currentPage * devicesPerPage;
const indexOfFirstDevice = indexOfLastDevice - devicesPerPage; const indexOfFirstDevice = indexOfLastDevice - devicesPerPage;
@ -89,6 +89,9 @@ const TableScanner = ({devices}:TableScannerProps) => {
const handleNextPage = () => { const handleNextPage = () => {
if(currentPage < totalPages) setCurrentPage(currentPage + 1); if(currentPage < totalPages) setCurrentPage(currentPage + 1);
} }
const onPageChange = (page: number) => {
setCurrentPage(page);
}
return ( return (
<TableContainer> <TableContainer>
@ -124,12 +127,15 @@ const TableScanner = ({devices}:TableScannerProps) => {
))} ))}
</tbody> </tbody>
</StyledTable> </StyledTable>
<Pagination {totalPages > 1 && (
currentPage={currentPage} <Pagination
totalPages={totalPages} onPageChange={onPageChange}
onPrevPage={handlePrevPage} currentPage={currentPage}
onNextPage={handleNextPage} totalPages={totalPages}
/> onPrevPage={handlePrevPage}
onNextPage={handleNextPage}
/>
)}
</TableContainer> </TableContainer>
); );
}; };

View file

@ -2,7 +2,7 @@ import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import DeviceCard from "../../../../../UI/Dropdown/DeviceCard"; import DeviceCard from "../../../../../UI/Dropdown/DeviceCard";
import {DeviceUser} from "../../../../../../store/useDeviceStore"; import {DeviceUser} from "../../../../../../store/useDeviceStore";
import TableScanner from "../../../../../UI/tables/TableScanner"; import TableScanner from "./TableScanner";
const ScanBtn = styled.button` const ScanBtn = styled.button`
position: relative; position: relative;

View file

@ -81,6 +81,11 @@ export const ToolBar = () => {
/> />
</IconContainer> </IconContainer>
</Button> </Button>
<Button>
<IconContainer>
<IconComponent name={buttonNames[3]} width="30" height="30" onClick={() => openModal(ModalMap['10'])} />
</IconContainer>
</Button>
</ButtonContainer> </ButtonContainer>
</Container> </Container>
) )

View file

@ -20,6 +20,7 @@ import refresh from '@iconify/icons-ic/baseline-refresh';
import close from '@iconify/icons-ic/round-close'; import close from '@iconify/icons-ic/round-close';
import wifiFind from '@iconify/icons-ic/baseline-wifi-find'; import wifiFind from '@iconify/icons-ic/baseline-wifi-find';
import noteEdit from '@iconify/icons-ph/note-pencil'; import noteEdit from '@iconify/icons-ph/note-pencil';
import swapHoriz from '@iconify/icons-ic/baseline-swap-horiz';
export const ICONS = { export const ICONS = {
[buttonNames[1]]: exitToApp, [buttonNames[1]]: exitToApp,
@ -39,6 +40,7 @@ export const ICONS = {
[buttonNames[15]]: wifiFind, [buttonNames[15]]: wifiFind,
[buttonNames[16]]: noteEdit, [buttonNames[16]]: noteEdit,
[buttonNames[17]]: arrowLeft, [buttonNames[17]]: arrowLeft,
[buttonNames[18]]: swapHoriz,
} as const; } as const;
type IconName = keyof typeof ICONS; type IconName = keyof typeof ICONS;

View file

@ -18,4 +18,5 @@ export const buttonNames: names ={
15: 'wifiFind', 15: 'wifiFind',
16: 'noteEdit', 16: 'noteEdit',
17: 'arrowLeft', 17: 'arrowLeft',
18: 'swapHoriz',
} }

View file

@ -0,0 +1,46 @@
import { create } from 'zustand';
export type Setting = {
id: string;
type: 'boolean' | 'select' | 'text' | 'number';
label: string;
value: any;
options?: { value: any; label: string }[];
sectionId: string;
};
type SettingsState = {
settings: Setting[];
activeSection: string | null;
setActiveSection: (sectionId: string | null) => void;
updateSetting: (id: string, value: any) => void;
initializeSettings: () => void;
};
export const useSettingsStore = create<SettingsState>((set) => ({
settings: [],
activeSection: null,
setActiveSection: (sectionId) => set({ activeSection: sectionId }),
updateSetting: (id, value) =>
set((state) => ({
settings: state.settings.map((setting) =>
setting.id === id ? { ...setting, value } : setting
),
})),
initializeSettings: () => {
const defaultSettings: Setting[] = [
{
id: 'rescan-nodes',
type: 'number',
label: 'nodesNetwork',
value: 3000,
sectionId: 'appearance',
},
];
set({ settings: defaultSettings });
},
}));