Полная интернационализация
This commit is contained in:
parent
3315123029
commit
4286f28d1e
26 changed files with 1241 additions and 465 deletions
|
|
@ -8,6 +8,7 @@ import useNetworkStore from "../../../store/useDeviceStore";
|
|||
import RouterLoader from "../../UI/routerLoader";
|
||||
import {useTheme} from "styled-components";
|
||||
import {useIpStore} from "../../../store";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
interface Props {
|
||||
address: string;
|
||||
|
|
@ -19,53 +20,55 @@ interface Props {
|
|||
export const ConnectNetwork = ({address, onClose}: Props) => {
|
||||
const [password, setPassword] = useState('');
|
||||
const theme = useTheme();
|
||||
const { ipAddress } = useIpStore();
|
||||
const {ipAddress} = useIpStore();
|
||||
const [error, setError] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError('')
|
||||
setError('');
|
||||
const res = await NetworkService.connectNetwork(ipAddress, password, address);
|
||||
if(res.status === 200) {
|
||||
onClose()
|
||||
}else{
|
||||
setError(res.data.detail)
|
||||
if (res.status === 200) {
|
||||
onClose();
|
||||
} else {
|
||||
setError(res.data.detail);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error connection:', error);
|
||||
setError('Ошибка подключения к сети')
|
||||
}finally {
|
||||
console.error(t('connectionErrorLog'), error);
|
||||
setError(t('connectionError'));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<ModalContainer style={{ height: '230px' }}>
|
||||
<ModalContainer style={{height: '230px'}}>
|
||||
<ModalTitle>
|
||||
<h3>Подключение к сети</h3>
|
||||
<h3>{t('networkConnection')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<ModalContent style={{display: 'flex', flexDirection: 'column', gap: '8px'}}>
|
||||
<Input
|
||||
type="password"
|
||||
placeholderText='Введите пароль...'
|
||||
placeholderText={t('enterPassword')}
|
||||
value={password}
|
||||
id='deviceNote'
|
||||
onChange={(value) => setPassword(value)}
|
||||
/>
|
||||
<p style={{ color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right' }}>
|
||||
{error}
|
||||
</p>
|
||||
{error && (
|
||||
<p style={{color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right'}}>
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title="Отмена"
|
||||
title={t('cancel')}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<MainButton
|
||||
|
|
@ -73,12 +76,12 @@ export const ConnectNetwork = ({address, onClose}: Props) => {
|
|||
size="small"
|
||||
color={theme.successColor}
|
||||
hoverColor={theme.successColor}
|
||||
title='Подключиться'
|
||||
title={t('connect')}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
|
||||
{isLoading && <RouterLoader loaderText='Попытка подключения к сети'/>}
|
||||
{isLoading && <RouterLoader loaderText={t('connectingAttempt')}/>}
|
||||
</ModalContainer>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
// components/modalContainers/NoteDevice.tsx
|
||||
import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../style";
|
||||
import { MainButton } from "../../UI/buttons/mainButton";
|
||||
import Input from "../../UI/inputs/input";
|
||||
|
|
@ -6,6 +5,7 @@ import { useState } from "react";
|
|||
import {useIpStore} from "../../../store";
|
||||
import {NetworkService} from "../../../api/request";
|
||||
import useNetworkStore from "../../../store/useDeviceStore";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
interface Props {
|
||||
address: string;
|
||||
|
|
@ -20,6 +20,7 @@ export const NoteDevice = ({ address, onClose, currentNote, type}: Props) => {
|
|||
const [note, setNote] = useState(currentNote || '');
|
||||
const { ipAddress } = useIpStore();
|
||||
const { devices, setDevices } = useNetworkStore();
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
|
|
@ -50,13 +51,13 @@ export const NoteDevice = ({ address, onClose, currentNote, type}: Props) => {
|
|||
return (
|
||||
<ModalContainer style={{ height: '250px' }}>
|
||||
<ModalTitle>
|
||||
<h3>{currentNote ? 'Редактирование заметки' : 'Добавление заметки'}</h3>
|
||||
<h3>{currentNote ? t('note-edit') : t('note-add')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<Input
|
||||
type="text"
|
||||
placeholderText='Введите заметку...'
|
||||
placeholderText={t('note-enter')}
|
||||
value={note}
|
||||
id='deviceNote'
|
||||
onChange={(value) => setNote(value)}
|
||||
|
|
@ -67,13 +68,13 @@ export const NoteDevice = ({ address, onClose, currentNote, type}: Props) => {
|
|||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title="Отмена"
|
||||
title={t('cancel')}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title='Сохранить'
|
||||
title={t('save')}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { useState } from "react";
|
|||
import {NetworkService} from "../../../api/request";
|
||||
import useNetworkStore from "../../../store/useDeviceStore";
|
||||
import {useIpStore} from "../../../store";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
interface Props {
|
||||
address: string;
|
||||
|
|
@ -20,6 +21,7 @@ export const NoteWifi = ({ address, onClose, currentNote, type}: Props) => {
|
|||
const [note, setNote] = useState(currentNote || '');
|
||||
const { ipAddress } = useIpStore();
|
||||
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
|
|
@ -50,13 +52,13 @@ export const NoteWifi = ({ address, onClose, currentNote, type}: Props) => {
|
|||
return (
|
||||
<ModalContainer style={{ height: '220px' }}>
|
||||
<ModalTitle>
|
||||
<h3>{currentNote ? 'Редактирование заметки' : 'Добавление заметки'}</h3>
|
||||
<h3>{currentNote ? t('note-edit') : t('note-add')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<Input
|
||||
type="text"
|
||||
placeholderText='Введите заметку...'
|
||||
placeholderText={t('note-enter')}
|
||||
value={note}
|
||||
id='deviceNote'
|
||||
onChange={(value) => setNote(value)}
|
||||
|
|
@ -67,13 +69,13 @@ export const NoteWifi = ({ address, onClose, currentNote, type}: Props) => {
|
|||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title="Отмена"
|
||||
title={t('cancel')}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title='Сохранить'
|
||||
title={t('save')}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { MainButton } from "../../UI/buttons/mainButton";
|
|||
import { useState } from "react";
|
||||
import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer } from "../style"
|
||||
import { validateIPAddress } from "../../../utils/nodeController/validator";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
interface Props {
|
||||
onSave?: (data: string) => void;
|
||||
|
|
@ -12,6 +13,7 @@ interface Props {
|
|||
export const AddDevice = ({ onSave, onClose }: Props) => {
|
||||
const [ipAddress, setIpAddress] = useState('')
|
||||
const [error, setError] = useState(false)
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!validateIPAddress(ipAddress)) {
|
||||
|
|
@ -28,31 +30,39 @@ export const AddDevice = ({ onSave, onClose }: Props) => {
|
|||
|
||||
return (
|
||||
<ModalContainer style={{ height: '230px' }}>
|
||||
{/* <form onSubmit={handleSubmit}> */}
|
||||
<ModalTitle>
|
||||
<h3>Добавление узла</h3>
|
||||
<h3>{t('addNode')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
|
||||
<Input
|
||||
type="text"
|
||||
placeholderText='Введите адрес узла...'
|
||||
placeholderText={t('enterNodeAddress')}
|
||||
id="newIp"
|
||||
value={ipAddress}
|
||||
onChange={(value) => setIpAddress(value)}
|
||||
/>
|
||||
{error && (
|
||||
<p style={{ color: 'red', marginTop: '5px' }}>
|
||||
Некорректный адрес узла
|
||||
{t('invalidAddress')}
|
||||
</p>
|
||||
)}
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton type="button" size="small" title="Отмена" onClick={onClose} />
|
||||
<MainButton type="submit" size="small" title="Сохранить" onClick={handleSubmit} />
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title={t('cancel')}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<MainButton
|
||||
type="submit"
|
||||
size="small"
|
||||
title={t('save')}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
{/* </form> */}
|
||||
</ModalContainer>
|
||||
)
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer }
|
|||
import { useModalContext } from "../../../store"
|
||||
import { useIpStore } from "../../../store"
|
||||
import { MainButton } from "../../UI/buttons/mainButton"
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
type Props={
|
||||
message: string
|
||||
|
|
@ -10,28 +11,37 @@ type Props={
|
|||
export const ConnectionError = ({message}: Props)=>{
|
||||
const {closeModal} = useModalContext()
|
||||
const { setIpAddress } = useIpStore();
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
const close = ()=>{
|
||||
setIpAddress('localhost')
|
||||
closeModal()
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<ModalContainer>
|
||||
<ModalTitle>
|
||||
<h3>Соединение прервано</h3>
|
||||
<h3>{t('connectionLost')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
|
||||
<ModalContent>
|
||||
<ModalText>
|
||||
<h3>Произошёл разрыв с сервером</h3>
|
||||
<p>Пожалуйста, проверьте доступ к адресу и попробуйте снова.</p>
|
||||
<h3>{t('serverDisconnected')}</h3>
|
||||
<p>{t('checkAddressAndRetry')}</p>
|
||||
</ModalText>
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton title="Закрыть" onClick={close} size="small"/>
|
||||
<MainButton title="Повторить" onClick={closeModal} size="small"/>
|
||||
<MainButton
|
||||
title={t('close')}
|
||||
onClick={close}
|
||||
size="small"
|
||||
/>
|
||||
<MainButton
|
||||
title={t('retry')}
|
||||
onClick={closeModal}
|
||||
size="small"
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ModalContainer>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {ModalMap} from "../windowsType";
|
|||
import {useModalContext} from "../../../store";
|
||||
import styled from "styled-components";
|
||||
import { useSettingsStore } from '../../../store/settings/settingsStore';
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
const StyledTableList = styled.ul`
|
||||
display: flex;
|
||||
|
|
@ -42,7 +43,7 @@ const CustomizeNodes = () => {
|
|||
const updateSetting = useSettingsStore(state => state.updateSetting);
|
||||
const getSetting = useSettingsStore(state => state.getSetting);
|
||||
const initializeSettings = useSettingsStore(state => state.initializeSettings);
|
||||
|
||||
const {t} = useTranslation('common');
|
||||
const [dragIndex, setDragIndex] = useState<number | null>(null);
|
||||
|
||||
// Получаем текущие значения из хранилища
|
||||
|
|
@ -88,11 +89,22 @@ const CustomizeNodes = () => {
|
|||
initializeSettings();
|
||||
}, [initializeSettings]);
|
||||
|
||||
const getItemText = (item: string) => {
|
||||
if (item === 'Онлайн') return t('settings-status');
|
||||
if (item === 'Имя') return t('settings-name');
|
||||
if (item === 'Производитель') return t('settings-vendor');
|
||||
if (item === 'IP адрес') return t('settings-ipAddress');
|
||||
if (item === 'Открытые порты') return t('settings-openPorts');
|
||||
if (item === 'Последняя активность') return t('settings-lastActivity');
|
||||
if (item === 'Заметка') return t('settings-note');
|
||||
if (item === 'Действие') return t('settings-action');
|
||||
return item;
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalContainer style={{ height: '35vh', width: '80vw'}}>
|
||||
<ModalTitle>
|
||||
<h3>Кастомизация таблицы узлов подсети</h3>
|
||||
|
||||
<h3>{t('customizeTable-title')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{ display: 'flex', height: "100%", gap: '24px', padding: 0 }}>
|
||||
|
|
@ -110,7 +122,7 @@ const CustomizeNodes = () => {
|
|||
onDragEnd={handleDrop}
|
||||
onClick={() => handleSwitch(index)}
|
||||
>
|
||||
{item}
|
||||
{getItemText(item)}
|
||||
</StyledTableItem>
|
||||
))}
|
||||
</StyledTableList>
|
||||
|
|
@ -120,7 +132,7 @@ const CustomizeNodes = () => {
|
|||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title='Назад'
|
||||
title={t('common-back')}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {useState} from "react";
|
|||
import {NetworkService} from "../../../api/request";
|
||||
import RouterLoader from "../../UI/routerLoader";
|
||||
import {useTheme} from "styled-components";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
type Props={
|
||||
address: string,
|
||||
|
|
@ -16,6 +17,7 @@ export const DisconnectConfirm = ({address, onClose}: Props)=>{
|
|||
const { ipAddress } = useIpStore();
|
||||
const theme = useTheme()
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
const handleDisconnect = async () => {
|
||||
try {
|
||||
|
|
@ -34,21 +36,32 @@ export const DisconnectConfirm = ({address, onClose}: Props)=>{
|
|||
return(
|
||||
<ModalContainer>
|
||||
<ModalTitle>
|
||||
<h3>Отключение от Wi-Fi сети</h3>
|
||||
<h3>{t('wifiDisconnect-title')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
|
||||
<ModalContent>
|
||||
<ModalText>
|
||||
<h3>Подтверждение отключения</h3>
|
||||
<h3>{t('wifiDisconnect-confirm')}</h3>
|
||||
<p>{t('wifiDisconnect-message1')} + {address}. {t('wifiDisconnect-message2')}</p>
|
||||
<p>Вы собираетесь отключиться от сети "{address}". Все активные подключения будут прерваны.</p>
|
||||
</ModalText>
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton title="Отменить" onClick={onClose} size="small"/>
|
||||
<MainButton title="Отключиться" color={theme.errorColor} hoverColor={theme.errorColor} onClick={handleDisconnect} size="small"/>
|
||||
<MainButton
|
||||
title={t('cancel')}
|
||||
onClick={onClose}
|
||||
size="small"
|
||||
/>
|
||||
<MainButton
|
||||
title={t('wifiDisconnect-disconnect')}
|
||||
color={theme.errorColor}
|
||||
hoverColor={theme.errorColor}
|
||||
onClick={handleDisconnect}
|
||||
size="small"
|
||||
/>
|
||||
</ButtonContainer>
|
||||
{isLoading && <RouterLoader loaderText='Отключение от сети'/>}
|
||||
{isLoading && <RouterLoader loaderText={t('wifiDisconnect-progress')}/>}
|
||||
</ModalContainer>
|
||||
)
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import { useIpStore } from "../../../store";
|
|||
import { MainButton } from "../../UI/buttons/mainButton";
|
||||
import { useAdminStore } from "../../../store/adminStore";
|
||||
import { useDevicesStore } from "../../../store";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
type Props = {
|
||||
ipData: changeIp
|
||||
|
|
@ -52,6 +53,7 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => {
|
|||
const { ipAddress, setIpAddress } = useIpStore();
|
||||
const {isAdmin} = useAdminStore()
|
||||
const { devices, addDevice, deleteDevice } = useDevicesStore();
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
useEffect(() => {
|
||||
if (ipData && isAdmin) {
|
||||
|
|
@ -146,40 +148,48 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => {
|
|||
return (
|
||||
<>
|
||||
{loader && isAdmin ? (
|
||||
<Loader loaderText="Идет проверка корректности IP адреса" />
|
||||
<Loader loaderText={t('ipChange-checkingIp')} />
|
||||
) : (
|
||||
<ModalContainer {...(checkStatus ? { height: "unset" } : {})}>
|
||||
<ModalTitle>
|
||||
<h3>Изменение IP-Адреса</h3>
|
||||
<h3>{t('ipChange-title')}</h3>
|
||||
</ModalTitle>
|
||||
{checkStatus ? (
|
||||
<ModalContent>
|
||||
<Information>
|
||||
<h3>Общие сведения</h3>
|
||||
<h3>{t('ipChange-generalInfo')}</h3>
|
||||
<Info>
|
||||
<p>Интерфейс: {ipData.device}</p>
|
||||
<p>{t('ipChange-interface')}: {ipData.device}</p>
|
||||
<p>--{'>'}</p>
|
||||
<p>{ipData.connection}</p>
|
||||
</Info>
|
||||
<h3>Сведения об IP</h3>
|
||||
<h3>{t('ipChange-ipInfo')}</h3>
|
||||
<DataContainer>
|
||||
<CurrentData>
|
||||
<p>Текущий ip: {ipData.currentIp}</p>
|
||||
<p>Текущий шлюз: {ipData.currentGate}</p>
|
||||
<p>Текущий DNS: {ipData.currentDns}</p>
|
||||
<p>{t('ipChange-currentIp')}: {ipData.currentIp}</p>
|
||||
<p>{t('ipChange-currentGateway')}: {ipData.currentGate}</p>
|
||||
<p>{t('ipChange-currentDns')}: {ipData.currentDns}</p>
|
||||
</CurrentData>
|
||||
|
||||
<NewData>
|
||||
<p>Новый ip: {ipData.newIp}</p>
|
||||
<p>Новый шлюз: {ipData.newGate}</p>
|
||||
<p>Новый DNS: {ipData.newDns}</p>
|
||||
<p>{t('ipChange-newIp')}: {ipData.newIp}</p>
|
||||
<p>{t('ipChange-newGateway')}: {ipData.newGate}</p>
|
||||
<p>{t('ipChange-newDns')}: {ipData.newDns}</p>
|
||||
</NewData>
|
||||
</DataContainer>
|
||||
</Information>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton title="Отмена" size='small' onClick={() => cancelIp(currentIp)}/>
|
||||
<MainButton title="Сохранить" size='small' onClick={() => saveIp(newIP)}/>
|
||||
<MainButton
|
||||
title={t('cancel')}
|
||||
size='small'
|
||||
onClick={() => cancelIp(currentIp)}
|
||||
/>
|
||||
<MainButton
|
||||
title={t('save')}
|
||||
size='small'
|
||||
onClick={() => saveIp(newIP)}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ModalContent>
|
||||
) : (
|
||||
|
|
@ -188,19 +198,21 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => {
|
|||
<ModalText>
|
||||
{isAdmin ? (
|
||||
<>
|
||||
<h3>Произошла ошибка проверки корректности</h3>
|
||||
<ErrorMessage>{message}</ErrorMessage>
|
||||
<h3>{t('ipChange-checkError')}</h3>
|
||||
<ErrorMessage>{message}</ErrorMessage>
|
||||
</>
|
||||
|
||||
):(
|
||||
<h3>Для изменения сетевых настроек необходимо авторизироваться под администратором</h3>
|
||||
<h3>{t('ipChange-adminRequired')}</h3>
|
||||
)}
|
||||
|
||||
</ModalText>
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton title="Закрыть" size='small' onClick={smoothClose}/>
|
||||
<MainButton
|
||||
title={t('close')}
|
||||
size='small'
|
||||
onClick={smoothClose}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { useCheckAdmin } from "../../../utils/checkAdmin"
|
|||
|
||||
import { buttonNames } from "../../../const/buttons/names"
|
||||
import { IconComponent } from "../../../const/buttons/icons"
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
|
|
@ -17,9 +18,10 @@ interface Props {
|
|||
export const RightsConfirm = ({ onClose }: Props) => {
|
||||
|
||||
const { ipAddress } = useIpStore();
|
||||
const { isAdmin, setAdmin } = useAdminStore()
|
||||
const { isAdmin } = useAdminStore()
|
||||
const checkAdmin = useCheckAdmin();
|
||||
const [error, setError] = useState(false)
|
||||
const {t} = useTranslation('common');
|
||||
const [success, setSuccess] = useState({
|
||||
message: '',
|
||||
status: false
|
||||
|
|
@ -45,7 +47,7 @@ export const RightsConfirm = ({ onClose }: Props) => {
|
|||
setError(false)
|
||||
setSuccess(prevData => ({
|
||||
...prevData,
|
||||
message: 'Успешный вход',
|
||||
message: t('auth-successLogin'),
|
||||
status: true
|
||||
}));
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
|
@ -54,20 +56,20 @@ export const RightsConfirm = ({ onClose }: Props) => {
|
|||
setError(true)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error:', err);
|
||||
console.error(t('auth-error'), err);
|
||||
}
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
await NetworkService.logout(ipAddress);
|
||||
await checkAdmin();
|
||||
setSuccess(prevData => ({
|
||||
...prevData,
|
||||
message: 'Успешный выход',
|
||||
status: true
|
||||
}));
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
onClose();
|
||||
await NetworkService.logout(ipAddress);
|
||||
await checkAdmin();
|
||||
setSuccess(prevData => ({
|
||||
...prevData,
|
||||
message: t('auth-successLogout'),
|
||||
status: true
|
||||
}));
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
onClose();
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -76,60 +78,55 @@ export const RightsConfirm = ({ onClose }: Props) => {
|
|||
!isAdmin ? (
|
||||
<>
|
||||
<ModalTitle>
|
||||
<h3>Получение прав доступа</h3>
|
||||
<h3>{t('auth-getAccess')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
|
||||
<ModalContent style={{display: 'flex', flexDirection: 'column', gap: '10px'}}>
|
||||
|
||||
<h3 style={{marginLeft: '5px', fontSize: '14px'}}>Включить административный доступ</h3>
|
||||
<h3 style={{marginLeft: '5px', fontSize: '14px'}}>{t('auth-enableAdmin')}</h3>
|
||||
<Input
|
||||
type="password"
|
||||
placeholderText='Введите пароль администратора...'
|
||||
placeholderText={t('auth-enterAdminPass')}
|
||||
id="password"
|
||||
value={user.password}
|
||||
onChange={(value) => handleChange('password', value)}
|
||||
/>
|
||||
|
||||
|
||||
{error && (
|
||||
<p style={{ color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right' }}>
|
||||
Неверный пароль оператора
|
||||
{t('auth-wrongPass')}
|
||||
</p>
|
||||
)}
|
||||
|
||||
</ModalContent>
|
||||
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton size="small" title="Отмена" onClick={onClose} />
|
||||
<MainButton size="small" title="Отправить" onClick={() => login(user)} />
|
||||
<MainButton size="small" title={t('cancel')} onClick={onClose} />
|
||||
<MainButton size="small" title={t('submit')} onClick={() => login(user)} />
|
||||
</ButtonContainer>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ModalTitle>
|
||||
<h3>Переключиться на ограниченный доступ</h3>
|
||||
<h3>{t('auth-switchToLimited')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
|
||||
<ModalContent>
|
||||
<p>В данном режиме доступа ограничиваются полномочия администратора.</p>
|
||||
<p>{t('auth-limitedModeDesc')}</p>
|
||||
</ModalContent>
|
||||
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton size="small" title="Отмена" onClick={onClose} />
|
||||
<MainButton size="small" title="Ограничить доступ" onClick={logout} />
|
||||
<MainButton size="small" title={t('cancel')} onClick={onClose} />
|
||||
<MainButton size="small" title={t('auth-restrictAccess')} onClick={logout} />
|
||||
</ButtonContainer>
|
||||
|
||||
</>
|
||||
)
|
||||
):(
|
||||
<ModalContainer style={{display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column', marginTop: '15px'}}>
|
||||
<IconComponent name={buttonNames[8]} width="50" height="50"/>
|
||||
<h3>{success.message}</h3>
|
||||
<p style={{fontSize: '12px'}}>Это окно можно закрыть, либо оно пропадёт само</p>
|
||||
<MainButton size="small" title="Закрыть" onClick={onClose} style={{width: '30%'}}/>
|
||||
<p style={{fontSize: '12px'}}>{t('auth-closeHint')}</p>
|
||||
<MainButton size="small" title={t('close')} onClick={onClose} style={{width: '30%'}}/>
|
||||
</ModalContainer>
|
||||
)}
|
||||
|
||||
|
||||
</ModalContainer>
|
||||
)
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import { IconComponent } from "../const/buttons/icons"
|
|||
import { buttonNames } from "../const/buttons/names"
|
||||
import ThemeButton from "./UI/buttons/toggletheme"
|
||||
import About from "./workSpace/modules/appModules/about"
|
||||
import {useTranslation} from "../utils/i18n/hooks";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -73,6 +74,7 @@ export const CreateUser = () => {
|
|||
const [repeatPassword, setRepeatPassword] = useState('');
|
||||
const [passwordsMatch, setPasswordsMatch] = useState(true);
|
||||
const [about, setAbout] = useState(false)
|
||||
const {t} = useTranslation('common')
|
||||
|
||||
const [user, setUser] = useState({
|
||||
username: 'operator',
|
||||
|
|
@ -118,77 +120,81 @@ export const CreateUser = () => {
|
|||
setAbout(true)
|
||||
}
|
||||
return (
|
||||
|
||||
<>
|
||||
{!about ? (
|
||||
<Wrapper>
|
||||
<Container onSubmit={handleSubmit}>
|
||||
<Title>
|
||||
<IconComponent name={buttonNames[3]} width="52" height="52" />
|
||||
<h3>{t('operatorConfig-title')}</h3>
|
||||
{ipAddress != 'localhost' &&
|
||||
<p>{ipAddress}</p>
|
||||
}
|
||||
</Title>
|
||||
|
||||
<Container onSubmit={handleSubmit}>
|
||||
<Title>
|
||||
<IconComponent name={buttonNames[3]} width="52" height="52"></IconComponent>
|
||||
<h3>Настройка оператора узла</h3>
|
||||
<Content>
|
||||
<div>
|
||||
<p>{t('operatorConfig-instruction')}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
type="password"
|
||||
placeholderText={t('operatorConfig-passwordPlaceholder')}
|
||||
id="password"
|
||||
value={user.password}
|
||||
onChange={(value) => handleChange('password', value)}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="password"
|
||||
placeholderText={t('operatorConfig-repeatPasswordPlaceholder')}
|
||||
id="repeatPassword"
|
||||
value={repeatPassword}
|
||||
onChange={(value) => handleChange('repeatPassword', value)}
|
||||
style={{ marginTop: '10px' }}
|
||||
/>
|
||||
{!passwordsMatch && (
|
||||
<p style={{ color: 'red', marginTop: '5px' }}>
|
||||
{t('operatorConfig-passwordMismatch')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</Content>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
style={{ width: '100%' }}
|
||||
title={t('operatorConfig-submit')}
|
||||
type="submit"
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</Container>
|
||||
|
||||
<Menu>
|
||||
{ipAddress != 'localhost' &&
|
||||
<p>{ipAddress}</p>
|
||||
<MainButton
|
||||
title={t('common-back')}
|
||||
onClick={back}
|
||||
style={{marginRight: '10px'}}
|
||||
/>
|
||||
}
|
||||
|
||||
</Title>
|
||||
|
||||
<Content>
|
||||
|
||||
<div>
|
||||
<p>Перед началом работы необходимо создать пароль оператора приложения</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
type="password"
|
||||
placeholderText='Введите пароль...'
|
||||
id="password"
|
||||
value={user.password}
|
||||
onChange={(value) => handleChange('password', value)}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="password"
|
||||
placeholderText='Повторите пароль...'
|
||||
id="repeatPassword"
|
||||
value={repeatPassword}
|
||||
onChange={(value) => handleChange('repeatPassword', value)}
|
||||
style={{ marginTop: '10px' }}
|
||||
/>
|
||||
{!passwordsMatch && (
|
||||
<p style={{ color: 'red', marginTop: '5px' }}>
|
||||
Пароли не совпадают!
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</Content>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton style={{ width: '100%' }} title="Отправить" type="submit" />
|
||||
</ButtonContainer>
|
||||
|
||||
|
||||
</Container>
|
||||
|
||||
|
||||
<Menu>
|
||||
{ipAddress != 'localhost' &&
|
||||
<MainButton title="Назад" onClick={back} style={{marginRight: '10px'}}/>
|
||||
}
|
||||
<ThemeButton />
|
||||
<Button>
|
||||
<IconContainer>
|
||||
<IconComponent name={buttonNames[4]} width="30" height="30" onClick={toAbout}/>
|
||||
</IconContainer>
|
||||
</Button>
|
||||
<ThemeButton />
|
||||
<Button>
|
||||
<IconContainer>
|
||||
<IconComponent
|
||||
name={buttonNames[4]}
|
||||
width="30"
|
||||
height="30"
|
||||
onClick={toAbout}
|
||||
/>
|
||||
</IconContainer>
|
||||
</Button>
|
||||
</Menu>
|
||||
</Wrapper>
|
||||
): (
|
||||
</Wrapper>
|
||||
) : (
|
||||
<About from="reg" onBack={() => setAbout(false)} />
|
||||
)}
|
||||
</>
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import {MainButton} from "../UI/buttons/mainButton";
|
|||
import React from 'react';
|
||||
import {useModalContext} from "../../store";
|
||||
import {ModalMap} from "../modalwindows/windowsType";
|
||||
import {useTranslation} from "../../utils/i18n/hooks";
|
||||
|
||||
const SettingBlock = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -89,6 +90,7 @@ interface SettingItemProps {
|
|||
|
||||
const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: SettingItemProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation('common');
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { openModal } = useModalContext();
|
||||
|
||||
|
|
@ -96,7 +98,35 @@ const SettingItem = React.memo(({ setting, value, onChange, onValidationError }:
|
|||
if (setting.id === 'customize-nodes') {
|
||||
openModal(ModalMap['11']);
|
||||
} else if (setting.action) {
|
||||
setting.action();
|
||||
// Создаем обертку для действия с переводами
|
||||
const actionWithTranslations = async () => {
|
||||
try {
|
||||
// Временно заменяем alert на версию с переводами
|
||||
const originalAlert = window.alert;
|
||||
window.alert = (message: string) => {
|
||||
// Применяем переводы к сообщениям
|
||||
let translatedMessage = message;
|
||||
if (message.startsWith('settings-dataNotDeleted')) {
|
||||
translatedMessage = t('settings-dataNotDeleted') + message.replace('settings-dataNotDeleted', '');
|
||||
} else if (message === 'settings-exportSuccess') {
|
||||
translatedMessage = t('settings-exportSuccess');
|
||||
} else if (message.startsWith('settings-exportError')) {
|
||||
translatedMessage = t('settings-exportError') + message.replace('settings-exportError', '');
|
||||
}
|
||||
originalAlert(translatedMessage);
|
||||
};
|
||||
|
||||
if (setting.action) {
|
||||
await setting.action();
|
||||
}
|
||||
|
||||
// Восстанавливаем оригинальный alert
|
||||
window.alert = originalAlert;
|
||||
} catch (error) {
|
||||
console.error('Action error:', error);
|
||||
}
|
||||
};
|
||||
actionWithTranslations();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -106,19 +136,19 @@ const SettingItem = React.memo(({ setting, value, onChange, onValidationError }:
|
|||
let newError: string | null = null;
|
||||
|
||||
if (inputValue.trim() === '') {
|
||||
newError = 'Поле не может быть пустым';
|
||||
newError = t('settings-fieldRequired');
|
||||
onChange(null);
|
||||
}
|
||||
else if (isNaN(Number(inputValue))) {
|
||||
newError = 'Пожалуйста, введите число';
|
||||
newError = t('settings-enterNumber');
|
||||
}
|
||||
else {
|
||||
const numValue = Number(inputValue);
|
||||
if (setting.min !== undefined && numValue < setting.min) {
|
||||
newError = `Значение не может быть меньше ${setting.min}`;
|
||||
newError = t('settings-valueTooSmall') + setting.min;
|
||||
}
|
||||
else if (setting.max !== undefined && numValue > setting.max) {
|
||||
newError = `Значение не может быть больше ${setting.max}`;
|
||||
newError = t('settings-valueTooLarge') + setting.max;
|
||||
}
|
||||
|
||||
if (!newError) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {Setting, useSettingsStore} from "../../store/settings/settingsStore";
|
|||
import {SettingItem} from "./SettingItem";
|
||||
import {IconComponent} from "../../const/buttons/icons";
|
||||
import {buttonNames} from "../../const/buttons/names";
|
||||
import {useTranslation} from "../../utils/i18n/hooks";
|
||||
|
||||
|
||||
interface Props {
|
||||
|
|
@ -70,9 +71,12 @@ export const Settings = ({ onClose }: Props) => {
|
|||
const getSetting = useSettingsStore(state => state.getSetting);
|
||||
const resetToDefaults = useSettingsStore(state => state.resetToDefaults);
|
||||
const initializeSettings = useSettingsStore(state => state.initializeSettings);
|
||||
const updateSettingsConfig = useSettingsStore(state => state.updateSettingsConfig);
|
||||
|
||||
const theme = useTheme()
|
||||
const { t, language } = useTranslation('common');
|
||||
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
|
||||
const [currentLanguage, setCurrentLanguage] = useState(language);
|
||||
|
||||
// Проверка действий
|
||||
const handleValidationError = useCallback((fieldId: string, error: string | null) => {
|
||||
|
|
@ -89,22 +93,33 @@ export const Settings = ({ onClose }: Props) => {
|
|||
|
||||
const settingsBySection = useMemo(() => {
|
||||
return settingsConfig.reduce((acc, setting) => {
|
||||
if (!acc[setting.sectionId]) {
|
||||
acc[setting.sectionId] = [];
|
||||
}
|
||||
acc[setting.sectionId].push({
|
||||
// Применяем переводы к настройкам
|
||||
const translatedSetting = {
|
||||
...setting,
|
||||
sectionId: t(setting.sectionId),
|
||||
label: t(setting.label),
|
||||
defaultValue: typeof setting.defaultValue === 'string' && setting.defaultValue.startsWith('settings-') ? t(setting.defaultValue) : setting.defaultValue,
|
||||
options: setting.options ? setting.options.map((option: any) => ({
|
||||
...option,
|
||||
label: option.label.startsWith('settings-') ? t(option.label) : option.label
|
||||
})) : setting.options,
|
||||
value: settingsValues[setting.id] ?? setting.defaultValue
|
||||
});
|
||||
};
|
||||
|
||||
if (!acc[translatedSetting.sectionId]) {
|
||||
acc[translatedSetting.sectionId] = [];
|
||||
}
|
||||
acc[translatedSetting.sectionId].push(translatedSetting);
|
||||
return acc;
|
||||
}, {} as Record<string, Setting[]>);
|
||||
}, [settingsConfig, settingsValues]);
|
||||
}, [settingsConfig, settingsValues, t]);
|
||||
|
||||
const sections = useMemo(() => Object.keys(settingsBySection), [settingsBySection]);
|
||||
|
||||
useEffect(() => {
|
||||
updateSettingsConfig();
|
||||
initializeSettings();
|
||||
}, [initializeSettings]);
|
||||
}, [updateSettingsConfig, initializeSettings]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sections.length > 0 && !activeSection) {
|
||||
|
|
@ -112,10 +127,15 @@ export const Settings = ({ onClose }: Props) => {
|
|||
}
|
||||
}, [sections, activeSection, setActiveSection]);
|
||||
|
||||
// Обновляем текущий язык при изменении
|
||||
useEffect(() => {
|
||||
setCurrentLanguage(language);
|
||||
}, [language]);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
const hasErrors = Object.values(fieldErrors).some(error => error?.trim());
|
||||
if (hasErrors) {
|
||||
alert('Исправьте ошибки перед сохранением');
|
||||
alert(t('settings-fixErrors'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -126,13 +146,24 @@ export const Settings = ({ onClose }: Props) => {
|
|||
});
|
||||
|
||||
if (emptyRequiredFields.length > 0) {
|
||||
alert('Заполните все обязательные поля');
|
||||
alert(t('settings-fillRequired'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверяем, изменился ли язык
|
||||
const newLanguage = pendingChanges['language'] ?? settingsValues['language'];
|
||||
const languageChanged = newLanguage && newLanguage !== currentLanguage;
|
||||
|
||||
applyChanges();
|
||||
onClose();
|
||||
}, [fieldErrors, settingsConfig, pendingChanges, settingsValues, applyChanges, onClose]);
|
||||
|
||||
// Если язык изменился, перезагружаем приложение
|
||||
if (languageChanged) {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 100);
|
||||
}
|
||||
}, [fieldErrors, settingsConfig, pendingChanges, settingsValues, applyChanges, onClose, t, currentLanguage]);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
discardChanges();
|
||||
|
|
@ -141,14 +172,14 @@ export const Settings = ({ onClose }: Props) => {
|
|||
}, [discardChanges, onClose]);
|
||||
|
||||
const handleReset = () => {
|
||||
if (window.confirm('Вы уверены, что хотите сбросить все настройки к значениям по умолчанию?')) {
|
||||
if (window.confirm(t('settings-resetConfirm'))) {
|
||||
resetToDefaults();
|
||||
}
|
||||
};
|
||||
|
||||
// Обработка нажатий клавиш
|
||||
useEffect(() => {
|
||||
const handleKeyPress = (e: React.KeyboardEvent<HTMLElement>) => {
|
||||
const handleKeyPress = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter')
|
||||
handleSave();
|
||||
if (e.key === 'Escape')
|
||||
|
|
@ -158,12 +189,13 @@ export const Settings = ({ onClose }: Props) => {
|
|||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyPress);
|
||||
};
|
||||
}, []);
|
||||
}, [handleSave, handleCancel]);
|
||||
|
||||
|
||||
return (
|
||||
<ModalContainer style={{ height: '60vh', width: '50vw', minWidth: "800px" }}>
|
||||
<ModalTitle>
|
||||
<h3>Настройки</h3>
|
||||
<h3>{t('settings-title')}</h3>
|
||||
<CloseBtn>
|
||||
<IconComponent name={buttonNames[12]} width="30" height="30" onClick={handleCancel} />
|
||||
</CloseBtn>
|
||||
|
|
@ -204,7 +236,7 @@ export const Settings = ({ onClose }: Props) => {
|
|||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title='Сбросить настройки'
|
||||
title={t('settings-resetSettings')}
|
||||
onClick={handleReset}
|
||||
color={theme.offlineColor}
|
||||
hoverColor={theme.borderColor}
|
||||
|
|
@ -213,7 +245,7 @@ export const Settings = ({ onClose }: Props) => {
|
|||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title='Отменить'
|
||||
title={t('settings-cancel')}
|
||||
onClick={handleCancel}
|
||||
color={theme.offlineColor}
|
||||
hoverColor={theme.borderColor}
|
||||
|
|
@ -221,7 +253,7 @@ export const Settings = ({ onClose }: Props) => {
|
|||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title='Сохранить'
|
||||
title={t('settings-save')}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import styled, {useTheme} from "styled-components"
|
|||
import ThemeButton from "../../../UI/buttons/toggletheme"
|
||||
import {buttonNames} from "../../../../const/buttons/names";
|
||||
import {IconComponent} from "../../../../const/buttons/icons";
|
||||
import {useTranslation} from "../../../../utils/i18n/hooks";
|
||||
|
||||
type Props = {
|
||||
from: 'reg' | 'helloWindow' | 'workSpace'
|
||||
|
|
@ -112,7 +113,7 @@ const LinkBlock = styled.a`
|
|||
|
||||
const Copyright = styled.div`
|
||||
width: 100%;
|
||||
text-align: start;
|
||||
text-align: center;
|
||||
color: ${(props => props.theme.offlineColor)};
|
||||
`
|
||||
|
||||
|
|
@ -158,6 +159,7 @@ const MainTitle = styled.div`
|
|||
|
||||
export default function About({ from, onBack }: Props) {
|
||||
const { setWorkModule } = useWorkSpaceStore()
|
||||
const {t} = useTranslation('common')
|
||||
const getBackHandler = () => {
|
||||
switch (from) {
|
||||
case 'reg':
|
||||
|
|
@ -177,11 +179,12 @@ export default function About({ from, onBack }: Props) {
|
|||
return (
|
||||
<Wrapper>
|
||||
<Content>
|
||||
|
||||
<MainTitle>
|
||||
<div className={'title'}>
|
||||
<img src="./assets/images/gossys.png" alt="" />
|
||||
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}><h1>О приложении</h1></div>
|
||||
<img src="./assets/images/gossys.png" alt={t('about-logoAlt')} />
|
||||
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}>
|
||||
<h1>{t('about-title')}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<p className={'version'}>v 0.0.3</p>
|
||||
</MainTitle>
|
||||
|
|
@ -189,15 +192,15 @@ export default function About({ from, onBack }: Props) {
|
|||
<AboutLine/>
|
||||
|
||||
<Title>
|
||||
<p>ГоNets — минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p>
|
||||
<p>{t('about-description')}</p>
|
||||
</Title>
|
||||
|
||||
<Listing>
|
||||
<p>Возможности:</p>
|
||||
<p>{t('about-featuresTitle')}:</p>
|
||||
<List>
|
||||
<li>Визуализация активных сетевых ресурсов</li>
|
||||
<li>Настройка параметров локальных и удалённых сетевых ресурсов</li>
|
||||
<li>Управление взаимодействием ресурсов с Клиентом ГoNets</li>
|
||||
<li>{t('about-feature1')}</li>
|
||||
<li>{t('about-feature2')}</li>
|
||||
<li>{t('about-feature3')}</li>
|
||||
</List>
|
||||
</Listing>
|
||||
|
||||
|
|
@ -205,26 +208,28 @@ export default function About({ from, onBack }: Props) {
|
|||
<svg className="icon" viewBox="0 0 24 24">
|
||||
<path d="M12 2L1 21h22M12 6l7.53 13H4.47M11 10v4h2v-4m-2 6v2h2v-2"/>
|
||||
</svg>
|
||||
<p>Программное обеспечение исключительно для ознакомления и домашнего использования.</p>
|
||||
<a href={'https://gossys.dev/pages/projects/gonets'}>Перейти на ГоNets-PRO</a>
|
||||
<p>{t('about-disclaimer')}</p>
|
||||
<a href={'https://gossys.dev/pages/projects/gonets'}>
|
||||
{t('about-goToPro')}
|
||||
</a>
|
||||
</SubTitle>
|
||||
|
||||
<Links>
|
||||
<LinkBlock href={'https://gossys.dev/pages/projects/gonets'}>
|
||||
Домашняя страницa
|
||||
{t('about-homepage')}
|
||||
</LinkBlock>
|
||||
|
||||
<LinkBlock href={'https://wiki.gossys.dev/ru/gonets'}>
|
||||
Документация
|
||||
{t('about-documentation')}
|
||||
</LinkBlock>
|
||||
|
||||
<LinkBlock href={'https://wiki.gossys.dev/ru/lic/GosysPUB'}>
|
||||
Лицензия
|
||||
{t('about-license')}
|
||||
</LinkBlock>
|
||||
</Links>
|
||||
|
||||
<Copyright>
|
||||
Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.
|
||||
{t('about-copyright')}
|
||||
</Copyright>
|
||||
|
||||
<ButtonContainer>
|
||||
|
|
@ -235,7 +240,6 @@ export default function About({ from, onBack }: Props) {
|
|||
name={buttonNames[12]}
|
||||
onClick={backHandler}
|
||||
/>
|
||||
|
||||
)}
|
||||
|
||||
{from == 'reg' &&
|
||||
|
|
@ -243,7 +247,6 @@ export default function About({ from, onBack }: Props) {
|
|||
}
|
||||
</ButtonContainer>
|
||||
</Content>
|
||||
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
import { useWorkSpaceStore } from "../../../../store"
|
||||
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||
import { buttonNames } from "../../../../const/buttons/names"
|
||||
import {useTranslation} from "../../../../utils/i18n/hooks";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
|
|
@ -49,30 +50,33 @@
|
|||
`
|
||||
|
||||
export const HelloWindow = () => {
|
||||
|
||||
const {t} = useTranslation('common')
|
||||
const { setWorkModule } = useWorkSpaceStore()
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Container className="unselectable">
|
||||
<Title>
|
||||
<img src="./assets/images/logo.svg" alt="" style={{width: '152px'}}/>
|
||||
<img
|
||||
src="./assets/images/logo.svg"
|
||||
alt={t('main-logoAlt')}
|
||||
style={{width: '152px'}}
|
||||
/>
|
||||
</Title>
|
||||
|
||||
|
||||
<ButtonWrapper>
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
title="Настройки сети"
|
||||
title={t('main-networkSettings')}
|
||||
icon={buttonNames[3]}
|
||||
iconSize={{ width: '32', height: '32' }}
|
||||
onClick={() => setWorkModule(workSpaceNames[3])}
|
||||
/>
|
||||
<MainButton
|
||||
title="О программе"
|
||||
title={t('main-about')}
|
||||
icon={buttonNames[4]}
|
||||
iconSize={{ width: '32', height: '32' }}
|
||||
onClick={()=>setWorkModule(workSpaceNames[4])}
|
||||
onClick={() => setWorkModule(workSpaceNames[4])}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ButtonWrapper>
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ import {IconComponent} from "../../../../../../const/buttons/icons";
|
|||
import {buttonNames} from "../../../../../../const/buttons/names";
|
||||
import {ModalMap} from "../../../../../modalwindows/windowsType";
|
||||
import {useModalContext} from "../../../../../../store";
|
||||
import {useTranslation} from "../../../../../../utils/i18n/hooks";
|
||||
import Pagination from "../../../../../UI/Pagination";
|
||||
import {
|
||||
useDisabledColumns, useLanguage,
|
||||
useDisabledColumns,
|
||||
useNameSetting,
|
||||
useOrdersColumns,
|
||||
usePaginateSetting
|
||||
} from "../../../../../../hooks/useSettings";
|
||||
import {useTranslation} from "../../../../../../utils/i18n/hooks";
|
||||
|
||||
interface TableScannerProps {
|
||||
devices: DeviceUser[];
|
||||
|
|
@ -71,16 +71,19 @@ const StatusBadge = styled.span<{ status:string }>`
|
|||
`
|
||||
|
||||
export const TableScanner = ({ devices }: TableScannerProps) => {
|
||||
const {t} = useTranslation('common')
|
||||
const { openModal } = useModalContext();
|
||||
const nameSetting = useNameSetting();
|
||||
const ordersColumns = useOrdersColumns();
|
||||
const disabledColumns = useDisabledColumns();
|
||||
const paginateSetting = usePaginateSetting();
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [devicesPerPage, setDevicesPerPage] = useState(paginateSetting);
|
||||
|
||||
console.log(ordersColumns);
|
||||
|
||||
useEffect(() => {
|
||||
setDevicesPerPage(paginateSetting);
|
||||
}, [paginateSetting]);
|
||||
|
|
@ -98,14 +101,14 @@ export const TableScanner = ({ devices }: TableScannerProps) => {
|
|||
.filter(column => !disabledColumns.includes(ordersColumns.indexOf(column)))
|
||||
.map(column => {
|
||||
switch(column) {
|
||||
case t('tableScanner-online'): return 'status';
|
||||
case t('tableScanner-name'): return 'name';
|
||||
case t('tableScanner-vendor'): return 'vendor';
|
||||
case t('tableScanner-ip'): return 'ip';
|
||||
case t('tableScanner-ports'): return 'ports';
|
||||
case t('tableScanner-activity'): return 'activity';
|
||||
case t('tableScanner-note'): return 'note';
|
||||
case t('tableScanner-action'): return 'action';
|
||||
case 'Онлайн': return 'status';
|
||||
case 'Имя': return 'name';
|
||||
case 'Производитель': return 'vendor';
|
||||
case 'IP адрес': return 'ip';
|
||||
case 'Открытые порты': return 'ports';
|
||||
case 'Последняя активность': return 'activity';
|
||||
case 'Заметка': return 'note';
|
||||
case 'Действие': return 'action';
|
||||
default: return '';
|
||||
}
|
||||
})
|
||||
|
|
@ -136,7 +139,7 @@ export const TableScanner = ({ devices }: TableScannerProps) => {
|
|||
case 'status':
|
||||
return <TableCell key="status"><StatusBadge status={item.status} /></TableCell>;
|
||||
case 'name':
|
||||
return <TableCell key="name">{item.hostname || (nameSetting === 'ip' ? item.ip : t('tableScanner-unknown'))}</TableCell>;
|
||||
return <TableCell key="name">{item.hostname || (nameSetting === 'ip' ? item.ip : 'Неизвестно')}</TableCell>;
|
||||
case 'vendor':
|
||||
return <TableCell key="vendor">{item.vendor}</TableCell>;
|
||||
case 'ip':
|
||||
|
|
@ -168,12 +171,12 @@ export const TableScanner = ({ devices }: TableScannerProps) => {
|
|||
{visibleColumns.map(column => {
|
||||
switch (column) {
|
||||
case 'status': return <TableHeaderCell key="status"></TableHeaderCell>;
|
||||
case 'name': return <TableHeaderCell key="name">{t('tableScanner-name')}</TableHeaderCell>;
|
||||
case 'vendor': return <TableHeaderCell key="vendor">{t('tableScanner-vendor')}</TableHeaderCell>;
|
||||
case 'ip': return <TableHeaderCell key="ip">{t('tableScanner-ip')}</TableHeaderCell>;
|
||||
case 'ports': return <TableHeaderCell key="ports">{t('tableScanner-ports')}</TableHeaderCell>;
|
||||
case 'activity': return <TableHeaderCell key="activity">{t('tableScanner-activity')}</TableHeaderCell>;
|
||||
case 'note': return <TableHeaderCell key="note">{t('tableScanner-note')}</TableHeaderCell>;
|
||||
case 'name': return <TableHeaderCell key="name">{t('settings-name')}</TableHeaderCell>;
|
||||
case 'vendor': return <TableHeaderCell key="vendor">{t('settings-vendor')}</TableHeaderCell>;
|
||||
case 'ip': return <TableHeaderCell key="ip">{t('settings-ipAddress')}</TableHeaderCell>;
|
||||
case 'ports': return <TableHeaderCell key="ports">{t('settings-openPorts')}</TableHeaderCell>;
|
||||
case 'activity': return <TableHeaderCell key="activity">{t('settings-lastActivity')}</TableHeaderCell>;
|
||||
case 'note': return <TableHeaderCell key="note">{t('settings-note')}</TableHeaderCell>;
|
||||
case 'action': return <TableHeaderCell key="action"></TableHeaderCell>;
|
||||
default: return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import WifiDot, {WifiNetworkScanner} from "./modules/WifiDot";
|
|||
import ModalWifiScaner from "./modules/ModalWifiScaner";
|
||||
import useNetworkStore from "../../../../../store/useDeviceStore";
|
||||
import {useIpStore} from "../../../../../store";
|
||||
import {useTranslation} from "../../../../../utils/i18n/hooks";
|
||||
|
||||
// Анимации
|
||||
const ring1 = keyframes`
|
||||
|
|
@ -143,27 +144,28 @@ export function WifiScanner() {
|
|||
const [scanInitiated, setScanInitiated] = useState(false);
|
||||
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
|
||||
const [currentNetwork, setCurrentNetwork] = useState<WifiNetworkScanner | null>(null);
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const handleScan = async () => {
|
||||
const handleScan = async () => {
|
||||
setWifiNetworks([]);
|
||||
setCurrentNetwork(null);
|
||||
setLoading(true);
|
||||
setScanInitiated(true);
|
||||
try {
|
||||
const res = await NetworkService.scanWifi(ipAddress);
|
||||
const networks = res.data.access_points || [];
|
||||
setWifiNetworks(networks);
|
||||
} catch (err) {
|
||||
console.error(t('wifiScanner-scanError'), err);
|
||||
setWifiNetworks([]);
|
||||
setCurrentNetwork(null)
|
||||
setLoading(true);
|
||||
setScanInitiated(true);
|
||||
try {
|
||||
const res = await NetworkService.scanWifi(ipAddress);
|
||||
const networks = res.data.access_points || [];
|
||||
setWifiNetworks(networks);
|
||||
} catch (err) {
|
||||
console.error("Ошибка сканирования сети:", err);
|
||||
setWifiNetworks([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Wrapper scanInitiated={scanInitiated} loading={loading}>
|
||||
<TitleWrapper>Сканирование точек доступа Wi-Fi сети</TitleWrapper>
|
||||
<TitleWrapper>{t('wifiScanner-title')}</TitleWrapper>
|
||||
|
||||
<UserLocate loading={loading} scanInitiated={scanInitiated}>
|
||||
<span className="center circel"></span>
|
||||
|
|
@ -190,11 +192,15 @@ export function WifiScanner() {
|
|||
|
||||
{loading ? (
|
||||
<LoadingText>
|
||||
Сканирование точек доступа <ScanningText/>
|
||||
{t('wifiScanner-scanning')} <ScanningText/>
|
||||
</LoadingText>
|
||||
) : (
|
||||
<ScanBtn onClick={handleScan} disabled={loading} scanInitiated={wifiNetworks.length > 0 }>
|
||||
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать точки доступа'}
|
||||
<ScanBtn
|
||||
onClick={handleScan}
|
||||
disabled={loading}
|
||||
scanInitiated={wifiNetworks.length > 0}
|
||||
>
|
||||
{wifiNetworks.length > 0 ? t('wifiScanner-refresh') : t('wifiScanner-scan')}
|
||||
</ScanBtn>
|
||||
)}
|
||||
</Wrapper>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import SignalSVG from "../../../../../UI/signalSvg";
|
|||
import { useTheme } from 'styled-components';
|
||||
import useNetworkStore, {DeviceUser} from "../../../../../../store/useDeviceStore";
|
||||
import {NetworkService} from "../../../../../../api/request";
|
||||
import {useTranslation} from "../../../../../../utils/i18n/hooks";
|
||||
|
||||
const Modal = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -114,6 +115,7 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC
|
|||
const [signal, setSignal] = useState(1);
|
||||
const { wifiNetworks } = useNetworkStore();
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
useEffect(() => {
|
||||
const getStatus = async () => {
|
||||
|
|
@ -174,10 +176,9 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC
|
|||
<ModalTitle>
|
||||
<h4>{network.ssid}</h4>
|
||||
<div className={'functionalIcons'}>
|
||||
|
||||
<span onClick={openModalNote} style={{cursor: "pointer"}}>
|
||||
<IconComponent name={buttonNames[16]} width="30" height="30" />
|
||||
</span>
|
||||
<span onClick={openModalNote} style={{cursor: "pointer"}}>
|
||||
<IconComponent name={buttonNames[16]} width="30" height="30" />
|
||||
</span>
|
||||
<Status>
|
||||
<SignalSVG signal={signal} size={'36px'} color={theme.accent} />
|
||||
</Status>
|
||||
|
|
@ -185,40 +186,42 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC
|
|||
</ModalTitle>
|
||||
<ModalContent>
|
||||
<ModalRow>
|
||||
<h6>BSSID:</h6>
|
||||
<h6>{t('wifiModal-bssid')}:</h6>
|
||||
<p>{network.bssid}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Производитель:</h6>
|
||||
<p>{network.vendor}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Сигнал:</h6>
|
||||
<p>{network.signal} dBm</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Канал:</h6>
|
||||
<p>{network.channel}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Шифрование:</h6>
|
||||
<p>{network.encryption}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Частота:</h6>
|
||||
<p>{network.frequency}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Скрытая сеть:</h6>
|
||||
<p>{network.hidden ? 'Да' : 'Нет'}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Заметка:</h6>
|
||||
<p>{currentNetwork.note ? currentNetwork.note : '-'}</p>
|
||||
</ModalRow>
|
||||
<ModalConnect onClick={openModalNetwork}>{isConnected ? 'Отключиться' : 'Подключиться'}</ModalConnect>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<ModalRow>
|
||||
<h6>{t('wifiModal-vendor')}:</h6>
|
||||
<p>{network.vendor || t('wifiModal-unknown')}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>{t('wifiModal-signal')}:</h6>
|
||||
<p>{network.signal} dBm</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>{t('wifiModal-channel')}:</h6>
|
||||
<p>{network.channel}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>{t('wifiModal-encryption')}:</h6>
|
||||
<p>{network.encryption || t('wifiModal-none')}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>{t('wifiModal-frequency')}:</h6>
|
||||
<p>{network.frequency} MHz</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>{t('wifiModal-hidden')}:</h6>
|
||||
<p>{network.hidden ? t('common-yes') : t('common-no')}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>{t('wifiModal-note')}:</h6>
|
||||
<p>{currentNetwork.note || '-'}</p>
|
||||
</ModalRow>
|
||||
<ModalConnect onClick={openModalNetwork}>
|
||||
{isConnected ? t('wifiModal-disconnect') : t('wifiModal-connect')}
|
||||
</ModalConnect>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { useWorkSpaceStore } from "../../../../store"
|
|||
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||
import { IconComponent } from "../../../../const/buttons/icons"
|
||||
import { buttonNames } from "../../../../const/buttons/names"
|
||||
import {useTranslation} from "../../../../utils/i18n/hooks";
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -102,11 +103,12 @@ interface CurrentDevice {
|
|||
}
|
||||
|
||||
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 {openModal} = useModalContext();
|
||||
const {setIpAddress, ipAddress} = useIpStore();
|
||||
const {devices, addDevice, deleteDevice} = useDevicesStore()
|
||||
const {isOpen, collapseMenu} = useMenu(menusNames[1]);
|
||||
const {setWorkModule} = useWorkSpaceStore()
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
const [currentDevice, setCurrentDevice] = useState<CurrentDevice>({
|
||||
address: 'localhost',
|
||||
|
|
@ -163,8 +165,8 @@ export const DevicesBar = () => {
|
|||
const handleDelete = (number: number, adress: string) => {
|
||||
deleteDevice(number)
|
||||
|
||||
|
||||
if(adress == ipAddress){
|
||||
|
||||
if (adress == ipAddress) {
|
||||
setIpAddress('localhost')
|
||||
}
|
||||
};
|
||||
|
|
@ -173,7 +175,7 @@ export const DevicesBar = () => {
|
|||
const isAvailable = await checkWebSocketAvailability(ip);
|
||||
if (!isAvailable) {
|
||||
openModal(ModalMap['3'], {
|
||||
message: `Не удалось подключиться к серверу по адресу ${ip}`,
|
||||
message: t('nodes-connectionError') + ip,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -191,44 +193,46 @@ export const DevicesBar = () => {
|
|||
return (
|
||||
<Container>
|
||||
<Title>
|
||||
<p className="unselectable">Доступные узлы</p>
|
||||
<p className="unselectable">{t('nodes-availableNodes')}</p>
|
||||
</Title>
|
||||
|
||||
<DeviceBlock>
|
||||
<DeviceControl className="unselectable" onClick={() => switchWs(currentDevice.address)}>
|
||||
<p>Корневой адрес</p>
|
||||
<p>{t('nodes-rootAddress')}</p>
|
||||
</DeviceControl>
|
||||
{currentDevice.hostname == '' ? (
|
||||
<Text className="paddingP">Название: Загрузка<AnimDot /></Text>
|
||||
<Text className="paddingP">
|
||||
{t('nodes-name')}: {t('nodes-loading')}<AnimDot/>
|
||||
</Text>
|
||||
) : (
|
||||
<p className="paddingP">Название: {currentDevice.hostname}</p>
|
||||
<p className="paddingP">{t('nodes-name')}: {currentDevice.hostname}</p>
|
||||
)}
|
||||
</DeviceBlock>
|
||||
|
||||
<DevicesContainer>
|
||||
<DevicesHeader className="unselectable" onClick={collapseMenu}>
|
||||
<p>Дополнительные узлы</p>
|
||||
<IconComponent name={isOpen ? buttonNames[5] : buttonNames[6]} />
|
||||
<p>{t('nodes-additionalNodes')}</p>
|
||||
<IconComponent name={isOpen ? buttonNames[5] : buttonNames[6]}/>
|
||||
</DevicesHeader>
|
||||
|
||||
<DevicesContent $isactive={isOpen.toString()} >
|
||||
<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>
|
||||
<p>{t('nodes-node')} {device.number}</p>
|
||||
<IconComponent
|
||||
width="20"
|
||||
height="20"
|
||||
name={buttonNames[2]}
|
||||
setColor="white"
|
||||
onClick={(e: any) => {
|
||||
e.stopPropagation(); // Предотвращаем всплытие события
|
||||
e.stopPropagation();
|
||||
handleDelete(device.number, device.address);
|
||||
}}
|
||||
/>
|
||||
</DeviceControl>
|
||||
<p className="paddingP">Адрес: {device.address}</p>
|
||||
<p className="paddingP">{t('nodes-address')}: {device.address}</p>
|
||||
</DeviceBlock>
|
||||
))
|
||||
}
|
||||
|
|
@ -237,7 +241,6 @@ export const DevicesBar = () => {
|
|||
+
|
||||
</PlusBlock>
|
||||
</DevicesContent>
|
||||
|
||||
</DevicesContainer>
|
||||
</Container>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,25 +1,30 @@
|
|||
export const appearanceSettings = [
|
||||
{
|
||||
id: 'theme',
|
||||
sectionId: 'Внешний вид',
|
||||
label: 'Цветовая тема',
|
||||
type: 'select',
|
||||
defaultValue: 'light',
|
||||
options: [
|
||||
{ value: 'light', label: 'Светлая' },
|
||||
{ value: 'dark', label: 'Тёмная' },
|
||||
{ value: 'system', label: 'Как в системе' }
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'language',
|
||||
sectionId: 'Внешний вид',
|
||||
label: 'Язык интерфейса',
|
||||
type: 'select',
|
||||
defaultValue: 'ru',
|
||||
options: [
|
||||
{ value: 'ru', label: 'Русский' },
|
||||
{ value: 'en', label: 'Английский' },
|
||||
],
|
||||
},
|
||||
] as const;
|
||||
|
||||
const createAppearanceSettings = () => {
|
||||
return [
|
||||
{
|
||||
id: 'theme',
|
||||
sectionId: 'settings-sections-appearance',
|
||||
label: 'settings-colorTheme',
|
||||
type: 'select',
|
||||
defaultValue: 'light',
|
||||
options: [
|
||||
{ value: 'light', label: 'settings-light' },
|
||||
{ value: 'dark', label: 'settings-dark' },
|
||||
{ value: 'system', label: 'settings-system' }
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'language',
|
||||
sectionId: 'settings-sections-appearance',
|
||||
label: 'settings-interfaceLanguage',
|
||||
type: 'select',
|
||||
defaultValue: 'ru',
|
||||
options: [
|
||||
{ value: 'ru', label: 'settings-russian' },
|
||||
{ value: 'en', label: 'settings-english' },
|
||||
],
|
||||
},
|
||||
] as const;
|
||||
};
|
||||
|
||||
export { createAppearanceSettings };
|
||||
|
|
@ -8,30 +8,30 @@ const createNetworkSettings = () => {
|
|||
{
|
||||
id: 'clean-network',
|
||||
type: 'button',
|
||||
label: 'Очистить данные сессии',
|
||||
sectionId: 'Точки доступа',
|
||||
defaultValue: 'Очистить',
|
||||
label: 'settings-clearSessionData',
|
||||
sectionId: 'settings-sections-accessPoints',
|
||||
defaultValue: 'settings-clear',
|
||||
action: async ()=>{
|
||||
try {
|
||||
const res = await NetworkService.clearNetwork(ipAddress);
|
||||
console.log(res);
|
||||
}catch (e){
|
||||
console.error(e);
|
||||
alert('Данные не были удалены: '+ e)
|
||||
alert('settings-dataNotDeleted' + e)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'export-network',
|
||||
type: 'button',
|
||||
label: 'Экспорт данных точек доступа',
|
||||
sectionId: 'Точки доступа',
|
||||
defaultValue: 'Экспортировать',
|
||||
label: 'settings-exportNetworkData',
|
||||
sectionId: 'settings-sections-accessPoints',
|
||||
defaultValue: 'settings-export',
|
||||
action: async () => {
|
||||
try {
|
||||
const response = await NetworkService.exportNetwork(ipAddress);
|
||||
const { filePath } = await window.electronAPI.showSaveDialog({
|
||||
title: "Сохранить данные сетей",
|
||||
title: 'settings-saveNetworksTitle',
|
||||
defaultPath: "network_export.json",
|
||||
filters: [{ name: "JSON", extensions: ["json"] }],
|
||||
});
|
||||
|
|
@ -41,10 +41,10 @@ const createNetworkSettings = () => {
|
|||
filePath,
|
||||
JSON.stringify(response.data, null, 2)
|
||||
);
|
||||
alert("Данные успешно экспортированы!");
|
||||
alert('settings-exportSuccess');
|
||||
}
|
||||
} catch (e: any) {
|
||||
alert("Ошибка экспорта: " + e.message);
|
||||
alert('settings-exportError' + e.message);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -52,4 +52,4 @@ const createNetworkSettings = () => {
|
|||
] as const;
|
||||
};
|
||||
|
||||
export const networkSettings = createNetworkSettings();
|
||||
export { createNetworkSettings };
|
||||
|
|
@ -1,80 +1,79 @@
|
|||
import {useIpStore} from "../../ipStore";
|
||||
import {NetworkService} from "../../../api/request";
|
||||
|
||||
|
||||
const createNodesSettings = () => {
|
||||
const { ipAddress } = useIpStore.getState();
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'rescan-nodes',
|
||||
sectionId: 'Узлы подсети',
|
||||
label: 'Интервальное сканирование',
|
||||
type: 'boolean',
|
||||
defaultValue: false,
|
||||
},
|
||||
{
|
||||
id: 'interval-nodes',
|
||||
sectionId: 'Узлы подсети',
|
||||
label: 'Интервал сканирования узлов (мин)',
|
||||
type: 'number',
|
||||
defaultValue: 60,
|
||||
min: 1,
|
||||
max: 2880
|
||||
},
|
||||
{
|
||||
id: 'netnames-nodes',
|
||||
sectionId: 'Узлы подсети',
|
||||
label: 'Отображаемые имена при отсутствии',
|
||||
type: 'select',
|
||||
defaultValue: 'ip',
|
||||
options: [
|
||||
{ value: 'ip', label: 'IP адрес' },
|
||||
{ value: 'unknown', label: 'Неизвестно' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'paginate-nodes',
|
||||
type: 'number',
|
||||
label: 'Количество строк в таблице',
|
||||
sectionId: 'Узлы подсети',
|
||||
defaultValue: 20,
|
||||
min: 1,
|
||||
},
|
||||
{
|
||||
id: 'customize-nodes',
|
||||
type: 'button',
|
||||
label: 'Отображение узлов',
|
||||
sectionId: 'Узлы подсети',
|
||||
defaultValue: 'Настроить',
|
||||
},
|
||||
{
|
||||
id: 'clean-nodes',
|
||||
type: 'button',
|
||||
label: 'Очистить данные сессии',
|
||||
sectionId: 'Узлы подсети',
|
||||
defaultValue: 'Очистить',
|
||||
action: async ()=>{
|
||||
try {
|
||||
const res = await NetworkService.clearSubnet(ipAddress);
|
||||
console.log(res)
|
||||
}catch (e){
|
||||
console.error(e);
|
||||
alert('Данные не были удалены: '+ e)
|
||||
}
|
||||
{
|
||||
id: 'rescan-nodes',
|
||||
sectionId: 'settings-sections-subnetNodes',
|
||||
label: 'settings-intervalScanning',
|
||||
type: 'boolean',
|
||||
defaultValue: false,
|
||||
},
|
||||
{
|
||||
id: 'interval-nodes',
|
||||
sectionId: 'settings-sections-subnetNodes',
|
||||
label: 'settings-scanInterval',
|
||||
type: 'number',
|
||||
defaultValue: 60,
|
||||
min: 1,
|
||||
max: 2880
|
||||
},
|
||||
{
|
||||
id: 'netnames-nodes',
|
||||
sectionId: 'settings-sections-subnetNodes',
|
||||
label: 'settings-displayNames',
|
||||
type: 'select',
|
||||
defaultValue: 'ip',
|
||||
options: [
|
||||
{ value: 'ip', label: 'settings-ipAddress' },
|
||||
{ value: 'unknown', label: 'settings-unknown' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'paginate-nodes',
|
||||
type: 'number',
|
||||
label: 'settings-rowsPerPage',
|
||||
sectionId: 'settings-sections-subnetNodes',
|
||||
defaultValue: 20,
|
||||
min: 1,
|
||||
},
|
||||
{
|
||||
id: 'customize-nodes',
|
||||
type: 'button',
|
||||
label: 'settings-displaySettings',
|
||||
sectionId: 'settings-sections-subnetNodes',
|
||||
defaultValue: 'settings-configure',
|
||||
},
|
||||
{
|
||||
id: 'clean-nodes',
|
||||
type: 'button',
|
||||
label: 'settings-clearSessionData',
|
||||
sectionId: 'settings-sections-subnetNodes',
|
||||
defaultValue: 'settings-clear',
|
||||
action: async ()=>{
|
||||
try {
|
||||
const res = await NetworkService.clearSubnet(ipAddress);
|
||||
console.log(res)
|
||||
}catch (e){
|
||||
console.error(e);
|
||||
alert('settings-dataNotDeleted' + e)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "export-nodes",
|
||||
type: "button",
|
||||
label: "Экспорт данных узлов",
|
||||
sectionId: "Узлы подсети",
|
||||
defaultValue: "Экспортировать",
|
||||
label: "settings-exportData",
|
||||
sectionId: "settings-sections-subnetNodes",
|
||||
defaultValue: "settings-export",
|
||||
action: async () => {
|
||||
try {
|
||||
const response = await NetworkService.exportDevices(ipAddress);
|
||||
const { filePath } = await window.electronAPI.showSaveDialog({
|
||||
title: "Сохранить данные узлов",
|
||||
title: "settings-saveNodesTitle",
|
||||
defaultPath: "nodes_export.json",
|
||||
filters: [{ name: "JSON", extensions: ["json"] }],
|
||||
});
|
||||
|
|
@ -84,10 +83,10 @@ const createNodesSettings = () => {
|
|||
filePath,
|
||||
JSON.stringify(response.data, null, 2)
|
||||
);
|
||||
alert("Данные успешно экспортированы!");
|
||||
alert("settings-exportSuccess");
|
||||
}
|
||||
} catch (e: any) {
|
||||
alert("Ошибка экспорта: " + e.message);
|
||||
alert("settings-exportError" + e.message);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -95,26 +94,26 @@ const createNodesSettings = () => {
|
|||
id: 'table-columns-order',
|
||||
type: 'hidden',
|
||||
label: '',
|
||||
sectionId: 'Узлы подсети',
|
||||
sectionId: 'settings-sections-subnetNodes',
|
||||
defaultValue: [
|
||||
'Онлайн',
|
||||
'Имя',
|
||||
'Производитель',
|
||||
'IP адрес',
|
||||
'Открытые порты',
|
||||
'Последняя активность',
|
||||
'Заметка',
|
||||
'Действие'
|
||||
'settings-status',
|
||||
'settings-name',
|
||||
'settings-vendor',
|
||||
'settings-ipAddress',
|
||||
'settings-openPorts',
|
||||
'settings-lastActivity',
|
||||
'settings-note',
|
||||
'settings-action'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'table-columns-disabled',
|
||||
label: '',
|
||||
sectionId: 'Узлы подсети',
|
||||
sectionId: 'settings-sections-subnetNodes',
|
||||
type: 'hidden',
|
||||
defaultValue: []
|
||||
}
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const nodesSettings = createNodesSettings();
|
||||
export { createNodesSettings };
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import {nodesSettings} from "./config/nodesSettings";
|
||||
import { networkSettings } from './config/networkSettings';
|
||||
import {appearanceSettings} from "./config/appearanceSettings";
|
||||
import {createNodesSettings} from "./config/nodesSettings";
|
||||
import { createNetworkSettings } from './config/networkSettings';
|
||||
import {createAppearanceSettings} from "./config/appearanceSettings";
|
||||
|
||||
type SettingValue = any;
|
||||
type SettingsDictionary = Record<string, SettingValue>;
|
||||
|
|
@ -36,18 +36,13 @@ type SettingsState = {
|
|||
resetToDefaults: () => void;
|
||||
getSettingValue: <T = any>(id: string) => T;
|
||||
getNumberSetting: (id: string, fallback?: number) => number;
|
||||
updateSettingsConfig: () => void;
|
||||
};
|
||||
|
||||
const defaultSettings: Setting[] = [
|
||||
...nodesSettings,
|
||||
...networkSettings,
|
||||
...appearanceSettings,
|
||||
];
|
||||
|
||||
export const useSettingsStore = create<SettingsState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
settingsConfig: defaultSettings,
|
||||
settingsConfig: [],
|
||||
settingsValues: {},
|
||||
activeSection: null,
|
||||
pendingChanges: {},
|
||||
|
|
@ -90,6 +85,15 @@ export const useSettingsStore = create<SettingsState>()(
|
|||
set({ settingsValues: newValues });
|
||||
},
|
||||
|
||||
updateSettingsConfig: () => {
|
||||
const defaultSettings: Setting[] = [
|
||||
...createNodesSettings(),
|
||||
...createNetworkSettings(),
|
||||
...createAppearanceSettings(),
|
||||
];
|
||||
set({ settingsConfig: defaultSettings });
|
||||
},
|
||||
|
||||
getSetting: (id) => {
|
||||
const state = get();
|
||||
const setting = state.settingsConfig.find(s => s.id === id);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue