forked from VinokurovVE/tests
Servers: servers, ips, hw, storages
This commit is contained in:
@ -1,14 +1,33 @@
|
|||||||
import { Box } from '@mui/material'
|
import { Box } from '@mui/material'
|
||||||
import { IServer } from '../interfaces/servers'
|
import { IServer } from '../interfaces/servers'
|
||||||
import { useServerIps } from '../hooks/swrHooks'
|
import { useServerIps } from '../hooks/swrHooks'
|
||||||
|
import FullFeaturedCrudGrid from './TableEditable'
|
||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
|
||||||
function ServerData({ id, name, region_id }: IServer) {
|
function ServerData({ id, name, region_id }: IServer) {
|
||||||
const { serverIps } = useServerIps(id, 0, 10)
|
const { serverIps } = useServerIps(id, 0, 10)
|
||||||
|
|
||||||
|
const serverIpsColumns: GridColDef[] = [
|
||||||
|
{ field: 'id', headerName: 'ID', type: 'number' },
|
||||||
|
{ field: 'server_id', headerName: 'Server ID', type: 'number' },
|
||||||
|
{ field: 'name', headerName: 'Название', type: 'string' },
|
||||||
|
{ field: 'is_actual', headerName: 'Действителен', type: 'boolean' },
|
||||||
|
{ field: 'ip', headerName: 'IP', type: 'string' },
|
||||||
|
{ field: 'servername', headerName: 'Название сервера', type: 'string' },
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box sx={{ display: 'flex', flexDirection: 'column', p: '16px' }}>
|
||||||
{serverIps &&
|
{serverIps &&
|
||||||
JSON.stringify(serverIps)
|
<FullFeaturedCrudGrid
|
||||||
|
initialRows={serverIps}
|
||||||
|
columns={serverIpsColumns}
|
||||||
|
actions
|
||||||
|
onRowClick={(params, event, details) => {
|
||||||
|
//setCurrentServerData(params.row)
|
||||||
|
//setServerDataOpen(true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
146
frontend_reactjs/src/components/ServerHardware.tsx
Normal file
146
frontend_reactjs/src/components/ServerHardware.tsx
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import { AppBar, Autocomplete, CircularProgress, Dialog, IconButton, TextField, Toolbar } from '@mui/material'
|
||||||
|
import React, { Fragment, useEffect, useState } from 'react'
|
||||||
|
import { IRegion } from '../interfaces/fuel'
|
||||||
|
import { useHardwares, useRegions, useServerIps, useServers } from '../hooks/swrHooks'
|
||||||
|
import FullFeaturedCrudGrid from './TableEditable'
|
||||||
|
import ServerService from '../services/ServersService'
|
||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import { Close } from '@mui/icons-material'
|
||||||
|
import ServerData from './ServerData'
|
||||||
|
|
||||||
|
export default function ServerHardware() {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const [options, setOptions] = useState<IRegion[]>([])
|
||||||
|
const [search, setSearch] = useState<string | null>("")
|
||||||
|
const [debouncedSearch, setDebouncedSearch] = useState<string | null>("")
|
||||||
|
const [selectedOption, setSelectedOption] = useState<IRegion | null>(null)
|
||||||
|
const { servers, isLoading } = useServers()
|
||||||
|
|
||||||
|
const [serverDataOpen, setServerDataOpen] = useState(false)
|
||||||
|
const [currentServerData, setCurrentServerData] = useState<any | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedSearch(search)
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler)
|
||||||
|
}
|
||||||
|
}, [search])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (servers) {
|
||||||
|
setOptions([...servers])
|
||||||
|
}
|
||||||
|
}, [servers])
|
||||||
|
|
||||||
|
const handleInputChange = (value: string) => {
|
||||||
|
setSearch(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOptionChange = (value: IRegion | null) => {
|
||||||
|
setSelectedOption(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { hardwares, isLoading: serversLoading } = useHardwares(selectedOption?.id, 0, 10)
|
||||||
|
|
||||||
|
const hardwareColumns: GridColDef[] = [
|
||||||
|
{ field: 'id', headerName: 'ID', type: 'number' },
|
||||||
|
{ field: 'name', headerName: 'Название', type: 'string' },
|
||||||
|
{ field: 'server_id', headerName: 'Server ID', type: 'number' },
|
||||||
|
{ field: 'servername', headerName: 'Название сервера', type: 'string' },
|
||||||
|
{ field: 'os_info', headerName: 'ОС', type: 'string' },
|
||||||
|
{ field: 'ram', headerName: 'ОЗУ', type: 'string' },
|
||||||
|
{ field: 'processor', headerName: 'Проц.', type: 'string' },
|
||||||
|
{ field: 'storages_count', headerName: 'Кол-во хранилищ', type: 'number' },
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dialog
|
||||||
|
fullScreen
|
||||||
|
open={serverDataOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setServerDataOpen(false)
|
||||||
|
}}
|
||||||
|
aria-labelledby="modal-modal-title"
|
||||||
|
aria-describedby="modal-modal-description">
|
||||||
|
<AppBar sx={{ position: 'sticky' }}>
|
||||||
|
<Toolbar>
|
||||||
|
<IconButton
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
onClick={() => {
|
||||||
|
setServerDataOpen(false)
|
||||||
|
}}
|
||||||
|
aria-label="close"
|
||||||
|
>
|
||||||
|
<Close />
|
||||||
|
</IconButton>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
|
||||||
|
{currentServerData &&
|
||||||
|
<ServerData
|
||||||
|
id={currentServerData?.id}
|
||||||
|
region_id={currentServerData?.region_id}
|
||||||
|
name={currentServerData?.name}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Autocomplete
|
||||||
|
open={open}
|
||||||
|
onOpen={() => {
|
||||||
|
setOpen(true)
|
||||||
|
}}
|
||||||
|
onClose={() => {
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
onInputChange={(_, value) => handleInputChange(value)}
|
||||||
|
onChange={(_, value) => handleOptionChange(value)}
|
||||||
|
filterOptions={(x) => x}
|
||||||
|
isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name}
|
||||||
|
getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
|
||||||
|
options={options}
|
||||||
|
loading={isLoading}
|
||||||
|
value={selectedOption}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
label="Сервер"
|
||||||
|
InputProps={{
|
||||||
|
...params.InputProps,
|
||||||
|
endAdornment: (
|
||||||
|
<Fragment>
|
||||||
|
{isLoading ? <CircularProgress color="inherit" size={20} /> : null}
|
||||||
|
{params.InputProps.endAdornment}
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{serversLoading ?
|
||||||
|
<CircularProgress />
|
||||||
|
:
|
||||||
|
servers &&
|
||||||
|
<FullFeaturedCrudGrid
|
||||||
|
onSave={(id: any) => {
|
||||||
|
console.log(id)
|
||||||
|
}}
|
||||||
|
onDelete={ServerService.removeServer}
|
||||||
|
initialRows={hardwares}
|
||||||
|
columns={hardwareColumns}
|
||||||
|
actions
|
||||||
|
onRowClick={(params, event, details) => {
|
||||||
|
setCurrentServerData(params.row)
|
||||||
|
setServerDataOpen(true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
144
frontend_reactjs/src/components/ServerIpsView.tsx
Normal file
144
frontend_reactjs/src/components/ServerIpsView.tsx
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import { AppBar, Autocomplete, CircularProgress, Dialog, IconButton, TextField, Toolbar } from '@mui/material'
|
||||||
|
import React, { Fragment, useEffect, useState } from 'react'
|
||||||
|
import { IRegion } from '../interfaces/fuel'
|
||||||
|
import { useRegions, useServerIps, useServers } from '../hooks/swrHooks'
|
||||||
|
import FullFeaturedCrudGrid from './TableEditable'
|
||||||
|
import ServerService from '../services/ServersService'
|
||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import { Close } from '@mui/icons-material'
|
||||||
|
import ServerData from './ServerData'
|
||||||
|
|
||||||
|
export default function ServerIpsView() {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const [options, setOptions] = useState<IRegion[]>([])
|
||||||
|
const [search, setSearch] = useState<string | null>("")
|
||||||
|
const [debouncedSearch, setDebouncedSearch] = useState<string | null>("")
|
||||||
|
const [selectedOption, setSelectedOption] = useState<IRegion | null>(null)
|
||||||
|
const { servers, isLoading } = useServers()
|
||||||
|
|
||||||
|
const [serverDataOpen, setServerDataOpen] = useState(false)
|
||||||
|
const [currentServerData, setCurrentServerData] = useState<any | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedSearch(search)
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler)
|
||||||
|
}
|
||||||
|
}, [search])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (servers) {
|
||||||
|
setOptions([...servers])
|
||||||
|
}
|
||||||
|
}, [servers])
|
||||||
|
|
||||||
|
const handleInputChange = (value: string) => {
|
||||||
|
setSearch(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOptionChange = (value: IRegion | null) => {
|
||||||
|
setSelectedOption(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { serverIps, isLoading: serversLoading } = useServerIps(selectedOption?.id, 0, 10)
|
||||||
|
|
||||||
|
const serverIpsColumns: GridColDef[] = [
|
||||||
|
{ field: 'id', headerName: 'ID', type: 'number' },
|
||||||
|
{ field: 'server_id', headerName: 'Server ID', type: 'number' },
|
||||||
|
{ field: 'name', headerName: 'Название', type: 'string' },
|
||||||
|
{ field: 'is_actual', headerName: 'Действителен', type: 'boolean' },
|
||||||
|
{ field: 'ip', headerName: 'IP', type: 'string' },
|
||||||
|
{ field: 'servername', headerName: 'Название сервера', type: 'string' },
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dialog
|
||||||
|
fullScreen
|
||||||
|
open={serverDataOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setServerDataOpen(false)
|
||||||
|
}}
|
||||||
|
aria-labelledby="modal-modal-title"
|
||||||
|
aria-describedby="modal-modal-description">
|
||||||
|
<AppBar sx={{ position: 'sticky' }}>
|
||||||
|
<Toolbar>
|
||||||
|
<IconButton
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
onClick={() => {
|
||||||
|
setServerDataOpen(false)
|
||||||
|
}}
|
||||||
|
aria-label="close"
|
||||||
|
>
|
||||||
|
<Close />
|
||||||
|
</IconButton>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
|
||||||
|
{currentServerData &&
|
||||||
|
<ServerData
|
||||||
|
id={currentServerData?.id}
|
||||||
|
region_id={currentServerData?.region_id}
|
||||||
|
name={currentServerData?.name}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Autocomplete
|
||||||
|
open={open}
|
||||||
|
onOpen={() => {
|
||||||
|
setOpen(true)
|
||||||
|
}}
|
||||||
|
onClose={() => {
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
onInputChange={(_, value) => handleInputChange(value)}
|
||||||
|
onChange={(_, value) => handleOptionChange(value)}
|
||||||
|
filterOptions={(x) => x}
|
||||||
|
isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name}
|
||||||
|
getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
|
||||||
|
options={options}
|
||||||
|
loading={isLoading}
|
||||||
|
value={selectedOption}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
label="Сервер"
|
||||||
|
InputProps={{
|
||||||
|
...params.InputProps,
|
||||||
|
endAdornment: (
|
||||||
|
<Fragment>
|
||||||
|
{isLoading ? <CircularProgress color="inherit" size={20} /> : null}
|
||||||
|
{params.InputProps.endAdornment}
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{serversLoading ?
|
||||||
|
<CircularProgress />
|
||||||
|
:
|
||||||
|
servers &&
|
||||||
|
<FullFeaturedCrudGrid
|
||||||
|
onSave={(id: any) => {
|
||||||
|
console.log(id)
|
||||||
|
}}
|
||||||
|
onDelete={ServerService.removeServer}
|
||||||
|
initialRows={serverIps}
|
||||||
|
columns={serverIpsColumns}
|
||||||
|
actions
|
||||||
|
onRowClick={(params, event, details) => {
|
||||||
|
setCurrentServerData(params.row)
|
||||||
|
setServerDataOpen(true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
143
frontend_reactjs/src/components/ServerStorages.tsx
Normal file
143
frontend_reactjs/src/components/ServerStorages.tsx
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import { AppBar, Autocomplete, CircularProgress, Dialog, IconButton, TextField, Toolbar } from '@mui/material'
|
||||||
|
import React, { Fragment, useEffect, useState } from 'react'
|
||||||
|
import { IRegion } from '../interfaces/fuel'
|
||||||
|
import { useHardwares, useRegions, useServerIps, useServers, useStorages } from '../hooks/swrHooks'
|
||||||
|
import FullFeaturedCrudGrid from './TableEditable'
|
||||||
|
import ServerService from '../services/ServersService'
|
||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import { Close } from '@mui/icons-material'
|
||||||
|
import ServerData from './ServerData'
|
||||||
|
|
||||||
|
export default function ServerStorage() {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const [options, setOptions] = useState<IRegion[]>([])
|
||||||
|
const [search, setSearch] = useState<string | null>("")
|
||||||
|
const [debouncedSearch, setDebouncedSearch] = useState<string | null>("")
|
||||||
|
const [selectedOption, setSelectedOption] = useState<IRegion | null>(null)
|
||||||
|
const { hardwares, isLoading } = useHardwares()
|
||||||
|
|
||||||
|
const [serverDataOpen, setServerDataOpen] = useState(false)
|
||||||
|
const [currentServerData, setCurrentServerData] = useState<any | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedSearch(search)
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler)
|
||||||
|
}
|
||||||
|
}, [search])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hardwares) {
|
||||||
|
setOptions([...hardwares])
|
||||||
|
}
|
||||||
|
}, [hardwares])
|
||||||
|
|
||||||
|
const handleInputChange = (value: string) => {
|
||||||
|
setSearch(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOptionChange = (value: IRegion | null) => {
|
||||||
|
setSelectedOption(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { storages, isLoading: serversLoading } = useStorages(selectedOption?.id, 0, 10)
|
||||||
|
|
||||||
|
const storageColumns: GridColDef[] = [
|
||||||
|
{ field: 'id', headerName: 'ID', type: 'number' },
|
||||||
|
{ field: 'hardware_id', headerName: 'Hardware ID', type: 'number' },
|
||||||
|
{ field: 'name', headerName: 'Название', type: 'string' },
|
||||||
|
{ field: 'size', headerName: 'Размер', type: 'string' },
|
||||||
|
{ field: 'storage_type', headerName: 'Тип хранилища', type: 'string' },
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dialog
|
||||||
|
fullScreen
|
||||||
|
open={serverDataOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setServerDataOpen(false)
|
||||||
|
}}
|
||||||
|
aria-labelledby="modal-modal-title"
|
||||||
|
aria-describedby="modal-modal-description">
|
||||||
|
<AppBar sx={{ position: 'sticky' }}>
|
||||||
|
<Toolbar>
|
||||||
|
<IconButton
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
onClick={() => {
|
||||||
|
setServerDataOpen(false)
|
||||||
|
}}
|
||||||
|
aria-label="close"
|
||||||
|
>
|
||||||
|
<Close />
|
||||||
|
</IconButton>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
|
||||||
|
{currentServerData &&
|
||||||
|
<ServerData
|
||||||
|
id={currentServerData?.id}
|
||||||
|
region_id={currentServerData?.region_id}
|
||||||
|
name={currentServerData?.name}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Autocomplete
|
||||||
|
open={open}
|
||||||
|
onOpen={() => {
|
||||||
|
setOpen(true)
|
||||||
|
}}
|
||||||
|
onClose={() => {
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
onInputChange={(_, value) => handleInputChange(value)}
|
||||||
|
onChange={(_, value) => handleOptionChange(value)}
|
||||||
|
filterOptions={(x) => x}
|
||||||
|
isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name}
|
||||||
|
getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
|
||||||
|
options={options}
|
||||||
|
loading={isLoading}
|
||||||
|
value={selectedOption}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
label="Hardware"
|
||||||
|
InputProps={{
|
||||||
|
...params.InputProps,
|
||||||
|
endAdornment: (
|
||||||
|
<Fragment>
|
||||||
|
{isLoading ? <CircularProgress color="inherit" size={20} /> : null}
|
||||||
|
{params.InputProps.endAdornment}
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{serversLoading ?
|
||||||
|
<CircularProgress />
|
||||||
|
:
|
||||||
|
storages &&
|
||||||
|
<FullFeaturedCrudGrid
|
||||||
|
onSave={(id: any) => {
|
||||||
|
console.log(id)
|
||||||
|
}}
|
||||||
|
onDelete={ServerService.removeServer}
|
||||||
|
initialRows={storages}
|
||||||
|
columns={storageColumns}
|
||||||
|
actions
|
||||||
|
onRowClick={(params, event, details) => {
|
||||||
|
setCurrentServerData(params.row)
|
||||||
|
setServerDataOpen(true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
191
frontend_reactjs/src/components/ServersView.tsx
Normal file
191
frontend_reactjs/src/components/ServersView.tsx
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import { AppBar, Autocomplete, Box, Chip, CircularProgress, Dialog, Divider, Grid, IconButton, Paper, TextField, Toolbar, Typography } from '@mui/material'
|
||||||
|
import React, { Fragment, useEffect, useState } from 'react'
|
||||||
|
import { IRegion } from '../interfaces/fuel'
|
||||||
|
import { useRegions, useServers, useServersInfo } from '../hooks/swrHooks'
|
||||||
|
import FullFeaturedCrudGrid from './TableEditable'
|
||||||
|
import ServerService from '../services/ServersService'
|
||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import { Close, Cloud, CloudOff } from '@mui/icons-material'
|
||||||
|
import ServerData from './ServerData'
|
||||||
|
import { IServersInfo } from '../interfaces/servers'
|
||||||
|
|
||||||
|
export default function ServersView() {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const [options, setOptions] = useState<IRegion[]>([])
|
||||||
|
const [search, setSearch] = useState<string | null>("")
|
||||||
|
const [debouncedSearch, setDebouncedSearch] = useState<string | null>("")
|
||||||
|
const [selectedOption, setSelectedOption] = useState<IRegion | null>(null)
|
||||||
|
const { regions, isLoading } = useRegions(10, 1, debouncedSearch)
|
||||||
|
|
||||||
|
const { serversInfo } = useServersInfo(selectedOption?.id)
|
||||||
|
|
||||||
|
|
||||||
|
const [serverDataOpen, setServerDataOpen] = useState(false)
|
||||||
|
const [currentServerData, setCurrentServerData] = useState<any | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedSearch(search)
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler)
|
||||||
|
}
|
||||||
|
}, [search])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (regions) {
|
||||||
|
setOptions([...regions])
|
||||||
|
}
|
||||||
|
}, [regions])
|
||||||
|
|
||||||
|
const handleInputChange = (value: string) => {
|
||||||
|
setSearch(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOptionChange = (value: IRegion | null) => {
|
||||||
|
setSelectedOption(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { servers, isLoading: serversLoading } = useServers(selectedOption?.id, 0, 10)
|
||||||
|
|
||||||
|
const serversColumns: GridColDef[] = [
|
||||||
|
//{ field: 'id', headerName: 'ID', type: "number" },
|
||||||
|
{ field: 'name', headerName: 'Название', type: "string", editable: true },
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dialog
|
||||||
|
fullScreen
|
||||||
|
open={serverDataOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setServerDataOpen(false)
|
||||||
|
}}
|
||||||
|
aria-labelledby="modal-modal-title"
|
||||||
|
aria-describedby="modal-modal-description">
|
||||||
|
<AppBar sx={{ position: 'sticky' }}>
|
||||||
|
<Toolbar>
|
||||||
|
<IconButton
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
onClick={() => {
|
||||||
|
setServerDataOpen(false)
|
||||||
|
}}
|
||||||
|
aria-label="close"
|
||||||
|
>
|
||||||
|
<Close />
|
||||||
|
</IconButton>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
|
||||||
|
{currentServerData &&
|
||||||
|
<ServerData
|
||||||
|
id={currentServerData?.id}
|
||||||
|
region_id={currentServerData?.region_id}
|
||||||
|
name={currentServerData?.name}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Autocomplete
|
||||||
|
open={open}
|
||||||
|
onOpen={() => {
|
||||||
|
setOpen(true)
|
||||||
|
}}
|
||||||
|
onClose={() => {
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
onInputChange={(_, value) => handleInputChange(value)}
|
||||||
|
onChange={(_, value) => handleOptionChange(value)}
|
||||||
|
filterOptions={(x) => x}
|
||||||
|
isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name}
|
||||||
|
getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
|
||||||
|
options={options}
|
||||||
|
loading={isLoading}
|
||||||
|
value={selectedOption}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
label="Район"
|
||||||
|
InputProps={{
|
||||||
|
...params.InputProps,
|
||||||
|
endAdornment: (
|
||||||
|
<Fragment>
|
||||||
|
{isLoading ? <CircularProgress color="inherit" size={20} /> : null}
|
||||||
|
{params.InputProps.endAdornment}
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{servers &&
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%' }}>
|
||||||
|
{serversInfo &&
|
||||||
|
<Grid container spacing={{ xs: 2, md: 3 }} columns={{ xs: 1, sm: 1, md: 2, lg: 3, xl: 4 }}>
|
||||||
|
{serversInfo.map((serverInfo: IServersInfo) => (
|
||||||
|
<Grid key={`si-${serverInfo.id}`} item xs={1} sm={1} md={1}>
|
||||||
|
<Paper sx={{ display: 'flex', flexDirection: 'column', gap: '16px', p: '16px' }}>
|
||||||
|
<Typography fontWeight={600}>
|
||||||
|
{serverInfo.name}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<Typography>
|
||||||
|
Количество IP:
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="h6" fontWeight={600}>
|
||||||
|
{serverInfo.IPs_count}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<Typography>
|
||||||
|
Количество серверов:
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="h6" fontWeight={600}>
|
||||||
|
{serverInfo.servers_count}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Chip
|
||||||
|
icon={serverInfo.status === "Online" ? <Cloud /> : serverInfo.status === "Offline" ? <CloudOff /> : <CloudOff />}
|
||||||
|
variant="outlined"
|
||||||
|
label={serverInfo.status}
|
||||||
|
color={serverInfo.status === "Online" ? "success" : serverInfo.status === "Offline" ? "error" : "error"}
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
|
||||||
|
{serversLoading ?
|
||||||
|
<CircularProgress />
|
||||||
|
:
|
||||||
|
servers &&
|
||||||
|
<FullFeaturedCrudGrid
|
||||||
|
onSave={(id: any) => {
|
||||||
|
console.log(id)
|
||||||
|
}}
|
||||||
|
onDelete={ServerService.removeServer}
|
||||||
|
initialRows={servers}
|
||||||
|
columns={serversColumns}
|
||||||
|
actions
|
||||||
|
onRowClick={(params, event, details) => {
|
||||||
|
setCurrentServerData(params.row)
|
||||||
|
setServerDataOpen(true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -23,6 +23,7 @@ import {
|
|||||||
GridRow,
|
GridRow,
|
||||||
useGridApiContext,
|
useGridApiContext,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
|
import { AxiosResponse } from 'axios';
|
||||||
|
|
||||||
interface EditToolbarProps {
|
interface EditToolbarProps {
|
||||||
setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;
|
setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;
|
||||||
@ -71,14 +72,18 @@ interface DataGridProps {
|
|||||||
initialRows: GridRowsProp;
|
initialRows: GridRowsProp;
|
||||||
columns: GridColDef[];
|
columns: GridColDef[];
|
||||||
actions: boolean;
|
actions: boolean;
|
||||||
onRowClick: GridEventListener<"rowClick">
|
onRowClick: GridEventListener<"rowClick">;
|
||||||
|
onSave: any;
|
||||||
|
onDelete: (data: any) => Promise<AxiosResponse<any, any>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FullFeaturedCrudGrid({
|
export default function FullFeaturedCrudGrid({
|
||||||
initialRows,
|
initialRows,
|
||||||
columns,
|
columns,
|
||||||
actions = false,
|
actions = false,
|
||||||
onRowClick
|
onRowClick,
|
||||||
|
onSave,
|
||||||
|
onDelete
|
||||||
}: DataGridProps) {
|
}: DataGridProps) {
|
||||||
const [rows, setRows] = React.useState(initialRows);
|
const [rows, setRows] = React.useState(initialRows);
|
||||||
const [rowModesModel, setRowModesModel] = React.useState<GridRowModesModel>({});
|
const [rowModesModel, setRowModesModel] = React.useState<GridRowModesModel>({});
|
||||||
@ -95,10 +100,13 @@ export default function FullFeaturedCrudGrid({
|
|||||||
|
|
||||||
const handleSaveClick = (id: GridRowId) => () => {
|
const handleSaveClick = (id: GridRowId) => () => {
|
||||||
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } });
|
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } });
|
||||||
|
onSave?.(id)
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteClick = (id: GridRowId) => () => {
|
const handleDeleteClick = (id: GridRowId) => () => {
|
||||||
setRows(rows.filter((row) => row.id !== id));
|
setRows(rows.filter((row) => row.id !== id));
|
||||||
|
onDelete?.(id).then(response => {
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancelClick = (id: GridRowId) => () => {
|
const handleCancelClick = (id: GridRowId) => () => {
|
||||||
@ -196,7 +204,6 @@ export default function FullFeaturedCrudGrid({
|
|||||||
processRowUpdate={processRowUpdate}
|
processRowUpdate={processRowUpdate}
|
||||||
slots={{
|
slots={{
|
||||||
toolbar: EditToolbar as GridSlots['toolbar'],
|
toolbar: EditToolbar as GridSlots['toolbar'],
|
||||||
row: CustomRow as GridSlots['row']
|
|
||||||
}}
|
}}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
toolbar: { setRows, setRowModesModel, columns },
|
toolbar: { setRows, setRowModesModel, columns },
|
||||||
@ -205,23 +212,3 @@ export default function FullFeaturedCrudGrid({
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function CustomRow(props: GridRowProps) {
|
|
||||||
const { id, row, rowId, ...other } = props;
|
|
||||||
const apiRef = useGridApiContext();
|
|
||||||
|
|
||||||
// Custom styles or logic can go here
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
component="div"
|
|
||||||
{...other}
|
|
||||||
>
|
|
||||||
{Object.entries(row).map(([field, value]) => (
|
|
||||||
<Box key={field} sx={{ flex: 1, padding: '0 8px' }}>
|
|
||||||
{apiRef.current.getCellParams(rowId, field).formattedValue as React.ReactNode}
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
@ -186,10 +186,11 @@ export function useBoilers(limit?: number, page?: number, search?: string) {
|
|||||||
|
|
||||||
export function useServers(region_id?: number, offset?: number, limit?: number) {
|
export function useServers(region_id?: number, offset?: number, limit?: number) {
|
||||||
const { data, error, isLoading } = useSWR(
|
const { data, error, isLoading } = useSWR(
|
||||||
region_id ? `/api/servers?region_id=${region_id}&offset=${offset || 0}&limit=${limit || 10}` : null,
|
region_id ? `/api/servers?region_id=${region_id}&offset=${offset || 0}&limit=${limit || 10}` : `/api/servers?offset=${offset || 0}&limit=${limit || 10}`,
|
||||||
(url) => fetcher(url, BASE_URL.servers),
|
(url: string) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false
|
revalidateOnFocus: false,
|
||||||
|
revalidateOnMount: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -205,7 +206,8 @@ export function useServersInfo(region_id?: number, offset?: number, limit?: numb
|
|||||||
region_id ? `/api/servers_info?region_id=${region_id}&offset=${offset || 0}&limit=${limit || 10}` : null,
|
region_id ? `/api/servers_info?region_id=${region_id}&offset=${offset || 0}&limit=${limit || 10}` : null,
|
||||||
(url) => fetcher(url, BASE_URL.servers),
|
(url) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false
|
revalidateOnFocus: false,
|
||||||
|
revalidateOnMount: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -221,7 +223,8 @@ export function useServer(server_id?: number) {
|
|||||||
server_id ? `/api/server/${server_id}` : null,
|
server_id ? `/api/server/${server_id}` : null,
|
||||||
(url) => fetcher(url, BASE_URL.servers),
|
(url) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false
|
revalidateOnFocus: false,
|
||||||
|
revalidateOnMount: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -232,12 +235,13 @@ export function useServer(server_id?: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useServerIps(server_id?: number, offset?: number, limit?: number) {
|
export function useServerIps(server_id?: number | null, offset?: number, limit?: number) {
|
||||||
const { data, error, isLoading } = useSWR(
|
const { data, error, isLoading } = useSWR(
|
||||||
server_id ? `/api/server_ips?server_id=${server_id}&offset=${offset || 0}&limit=${limit || 10}` : null,
|
server_id ? `/api/server_ips?server_id=${server_id}&offset=${offset || 0}&limit=${limit || 10}` : `/api/server_ips?offset=${offset || 0}&limit=${limit || 10}`,
|
||||||
(url) => fetcher(url, BASE_URL.servers),
|
(url: string) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false
|
revalidateOnFocus: false,
|
||||||
|
revalidateOnMount: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -252,10 +256,11 @@ export function useServerIps(server_id?: number, offset?: number, limit?: number
|
|||||||
|
|
||||||
export function useHardwares(server_id?: number, offset?: number, limit?: number) {
|
export function useHardwares(server_id?: number, offset?: number, limit?: number) {
|
||||||
const { data, error, isLoading } = useSWR(
|
const { data, error, isLoading } = useSWR(
|
||||||
server_id ? `/api/hardwares?server_id=${server_id}&offset=${offset || 0}&limit=${limit || 10}` : null,
|
server_id ? `/api/hardwares?server_id=${server_id}&offset=${offset || 0}&limit=${limit || 10}` : `/api/hardwares?offset=${offset || 0}&limit=${limit || 10}`,
|
||||||
(url) => fetcher(url, BASE_URL.servers),
|
(url: string) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false
|
revalidateOnFocus: false,
|
||||||
|
revalidateOnMount: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -287,10 +292,11 @@ export function useHardware(hardware_id?: number) {
|
|||||||
|
|
||||||
export function useStorages(hardware_id?: number, offset?: number, limit?: number) {
|
export function useStorages(hardware_id?: number, offset?: number, limit?: number) {
|
||||||
const { data, error, isLoading } = useSWR(
|
const { data, error, isLoading } = useSWR(
|
||||||
hardware_id ? `/api/storages?hardware_id=${hardware_id}&offset=${offset || 0}&limit=${limit || 10}` : null,
|
hardware_id ? `/api/storages?hardware_id=${hardware_id}&offset=${offset || 0}&limit=${limit || 10}` : `/api/storages?offset=${offset || 0}&limit=${limit || 10}`,
|
||||||
(url) => fetcher(url, BASE_URL.servers),
|
(url: string) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false
|
revalidateOnFocus: false,
|
||||||
|
revalidateOnMount: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,267 +1,66 @@
|
|||||||
import { AppBar, Autocomplete, Box, Chip, CircularProgress, Dialog, Divider, Grid, IconButton, Paper, TextField, Toolbar, Typography, colors } from "@mui/material"
|
import { Box, Tab, Tabs } from "@mui/material"
|
||||||
import { useBoilers, useCities, useRegions, useServers, useServersInfo } from "../hooks/swrHooks"
|
import { useState } from "react"
|
||||||
import { Fragment, useEffect, useState } from "react"
|
import ServersView from "../components/ServersView"
|
||||||
import { IBoiler, ICity, IRegion } from "../interfaces/fuel"
|
import ServerIpsView from "../components/ServerIpsView"
|
||||||
import { DataGrid, GridColDef } from "@mui/x-data-grid"
|
import ServerHardware from "../components/ServerHardware"
|
||||||
import ServerData from "../components/ServerData"
|
import ServerStorage from "../components/ServerStorages"
|
||||||
import { IServer, IServersInfo } from "../interfaces/servers"
|
|
||||||
import { Close, Cloud, CloudOff, Storage } from "@mui/icons-material"
|
|
||||||
import { BarChart } from "@mui/x-charts"
|
|
||||||
import FullFeaturedCrudGrid from "../components/TableEditable"
|
|
||||||
|
|
||||||
export default function Servers() {
|
export default function Servers() {
|
||||||
const [open, setOpen] = useState(false)
|
const [currentTab, setCurrentTab] = useState(0)
|
||||||
const [options, setOptions] = useState<IRegion[]>([])
|
|
||||||
const [search, setSearch] = useState<string | null>(null)
|
|
||||||
const [debouncedSearch, setDebouncedSearch] = useState<string | null>("")
|
|
||||||
const [selectedOption, setSelectedOption] = useState<IRegion | null>(null)
|
|
||||||
const { regions, isLoading } = useRegions(10, 1, debouncedSearch)
|
|
||||||
|
|
||||||
useEffect(() => {
|
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||||
const handler = setTimeout(() => {
|
setCurrentTab(newValue);
|
||||||
setDebouncedSearch(search)
|
|
||||||
}, 500)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearTimeout(handler)
|
|
||||||
}
|
|
||||||
}, [search])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (regions) {
|
|
||||||
setOptions([...regions])
|
|
||||||
}
|
|
||||||
}, [regions])
|
|
||||||
|
|
||||||
const handleInputChange = (value: string) => {
|
|
||||||
setSearch(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOptionChange = (value: IRegion | null) => {
|
interface TabPanelProps {
|
||||||
setSelectedOption(value)
|
children?: React.ReactNode;
|
||||||
|
index: number;
|
||||||
|
value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [citiesOpen, setCitiesOpen] = useState(false)
|
function CustomTabPanel(props: TabPanelProps) {
|
||||||
const [citiesPage, setCitiesPage] = useState(1)
|
const { children, value, index, ...other } = props;
|
||||||
const [citiesSearch, setCitiesSearch] = useState('')
|
|
||||||
const [debouncedCitySearch, setDebouncedCitySearch] = useState('')
|
|
||||||
const { cities, isLoading: citiesLoading } = useCities(10, citiesPage, debouncedCitySearch)
|
|
||||||
const [citiesOptions, setCitiesOptions] = useState<ICity[]>([])
|
|
||||||
const [selectedCityOption, setSelectedCityOption] = useState<ICity | null>(null)
|
|
||||||
|
|
||||||
const handleCityInputChange = (value: string) => {
|
return (
|
||||||
setCitiesSearch(value)
|
<div
|
||||||
|
role="tabpanel"
|
||||||
|
hidden={value !== index}
|
||||||
|
id={`simple-tabpanel-${index}`}
|
||||||
|
aria-labelledby={`simple-tab-${index}`}
|
||||||
|
{...other}
|
||||||
|
>
|
||||||
|
{value === index && <Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>{children}</Box>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCityOptionChange = (value: ICity | null) => {
|
|
||||||
setSelectedCityOption(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (cities) {
|
|
||||||
setCitiesOptions([...cities])
|
|
||||||
}
|
|
||||||
}, [cities])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handler = setTimeout(() => {
|
|
||||||
setDebouncedCitySearch(citiesSearch)
|
|
||||||
}, 500)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearTimeout(handler)
|
|
||||||
}
|
|
||||||
}, [citiesSearch])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setCitiesPage(1)
|
|
||||||
setCitiesSearch("")
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const { servers, isLoading: serversLoading } = useServers(selectedOption?.id, 0, 10)
|
|
||||||
|
|
||||||
const serversColumns: GridColDef[] = [
|
|
||||||
//{ field: 'id', headerName: 'ID', type: "number" },
|
|
||||||
{ field: 'name', headerName: 'Название', type: "string", editable: true },
|
|
||||||
]
|
|
||||||
|
|
||||||
const [serverDataOpen, setServerDataOpen] = useState(false)
|
|
||||||
const [currentServerData, setCurrentServerData] = useState<any | null>(null)
|
|
||||||
|
|
||||||
const { serversInfo } = useServersInfo(selectedOption?.id)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%' }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%' }}>
|
||||||
<Dialog
|
|
||||||
fullScreen
|
|
||||||
open={serverDataOpen}
|
|
||||||
onClose={() => {
|
|
||||||
setServerDataOpen(false)
|
|
||||||
}}
|
|
||||||
aria-labelledby="modal-modal-title"
|
|
||||||
aria-describedby="modal-modal-description">
|
|
||||||
<AppBar sx={{ position: 'sticky' }}>
|
|
||||||
<Toolbar>
|
|
||||||
<IconButton
|
|
||||||
edge="start"
|
|
||||||
color="inherit"
|
|
||||||
onClick={() => {
|
|
||||||
setServerDataOpen(false)
|
|
||||||
}}
|
|
||||||
aria-label="close"
|
|
||||||
>
|
|
||||||
<Close />
|
|
||||||
</IconButton>
|
|
||||||
</Toolbar>
|
|
||||||
</AppBar>
|
|
||||||
|
|
||||||
{currentServerData &&
|
|
||||||
<ServerData
|
|
||||||
id={currentServerData?.id}
|
|
||||||
region_id={currentServerData?.region_id}
|
|
||||||
name={currentServerData?.name}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%', p: '16px' }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%', p: '16px' }}>
|
||||||
<Typography variant='h6' fontWeight='600'>
|
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||||
Servers by region
|
<Tabs value={currentTab} onChange={handleTabChange} aria-label="basic tabs example">
|
||||||
</Typography>
|
<Tab label="Серверы" />
|
||||||
|
<Tab label="IP-адреса" />
|
||||||
<Autocomplete
|
<Tab label="Hardware" />
|
||||||
open={open}
|
<Tab label="Storages" />
|
||||||
onOpen={() => {
|
</Tabs>
|
||||||
setOpen(true)
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
|
||||||
setOpen(false)
|
|
||||||
}}
|
|
||||||
onInputChange={(_, value) => handleInputChange(value)}
|
|
||||||
onChange={(_, value) => handleOptionChange(value)}
|
|
||||||
filterOptions={(x) => x}
|
|
||||||
isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name}
|
|
||||||
getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
|
|
||||||
options={options}
|
|
||||||
loading={isLoading}
|
|
||||||
value={selectedOption}
|
|
||||||
renderInput={(params) => (
|
|
||||||
<TextField
|
|
||||||
{...params}
|
|
||||||
label="Район"
|
|
||||||
InputProps={{
|
|
||||||
...params.InputProps,
|
|
||||||
endAdornment: (
|
|
||||||
<Fragment>
|
|
||||||
{isLoading ? <CircularProgress color="inherit" size={20} /> : null}
|
|
||||||
{params.InputProps.endAdornment}
|
|
||||||
</Fragment>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Autocomplete
|
|
||||||
hidden
|
|
||||||
|
|
||||||
open={citiesOpen}
|
|
||||||
onOpen={() => {
|
|
||||||
setCitiesOpen(true)
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
|
||||||
setCitiesOpen(false)
|
|
||||||
}}
|
|
||||||
onInputChange={(_, value) => handleCityInputChange(value)}
|
|
||||||
onChange={(_, value) => handleCityOptionChange(value)}
|
|
||||||
filterOptions={(x) => x}
|
|
||||||
isOptionEqualToValue={(option: ICity, value: ICity) => option.name === value.name}
|
|
||||||
getOptionLabel={(option: ICity) => option.name ? option.name : ""}
|
|
||||||
options={citiesOptions}
|
|
||||||
loading={citiesLoading}
|
|
||||||
value={selectedCityOption}
|
|
||||||
renderInput={(params) => (
|
|
||||||
<TextField
|
|
||||||
{...params}
|
|
||||||
label="Город"
|
|
||||||
InputProps={{
|
|
||||||
...params.InputProps,
|
|
||||||
endAdornment: (
|
|
||||||
<Fragment>
|
|
||||||
{citiesLoading ? <CircularProgress color="inherit" size={20} /> : null}
|
|
||||||
{params.InputProps.endAdornment}
|
|
||||||
</Fragment>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{servers &&
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%' }}>
|
|
||||||
<Typography variant='h6' fontWeight='600'>
|
|
||||||
Информация
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
{serversInfo &&
|
|
||||||
<Grid container spacing={{ xs: 2, md: 3 }} columns={{ xs: 1, sm: 1, md: 2, lg: 3, xl: 4 }}>
|
|
||||||
{serversInfo.map((serverInfo: IServersInfo) => (
|
|
||||||
<Grid key={`si-${serverInfo.id}`} item xs={1} sm={1} md={1}>
|
|
||||||
<Paper sx={{ display: 'flex', flexDirection: 'column', gap: '16px', p: '16px' }}>
|
|
||||||
<Typography fontWeight={600}>
|
|
||||||
{serverInfo.name}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
||||||
<Typography>
|
|
||||||
Количество IP:
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography variant="h6" fontWeight={600}>
|
|
||||||
{serverInfo.IPs_count}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
<CustomTabPanel value={currentTab} index={0}>
|
||||||
<Typography>
|
<ServersView />
|
||||||
Количество серверов:
|
</CustomTabPanel>
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography variant="h6" fontWeight={600}>
|
<CustomTabPanel value={currentTab} index={1}>
|
||||||
{serverInfo.servers_count}
|
<ServerIpsView />
|
||||||
</Typography>
|
</CustomTabPanel>
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Chip
|
<CustomTabPanel value={currentTab} index={2}>
|
||||||
icon={serverInfo.status === "Online" ? <Cloud /> : serverInfo.status === "Offline" ? <CloudOff /> : <CloudOff />}
|
<ServerHardware />
|
||||||
variant="outlined"
|
</CustomTabPanel>
|
||||||
label={serverInfo.status}
|
|
||||||
color={serverInfo.status === "Online" ? "success" : serverInfo.status === "Offline" ? "error" : "error"}
|
|
||||||
/>
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
}
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
|
|
||||||
{serversLoading ?
|
<CustomTabPanel value={currentTab} index={3}>
|
||||||
<CircularProgress />
|
<ServerStorage />
|
||||||
:
|
</CustomTabPanel>
|
||||||
servers &&
|
|
||||||
<FullFeaturedCrudGrid
|
|
||||||
initialRows={servers}
|
|
||||||
columns={serversColumns}
|
|
||||||
actions
|
|
||||||
onRowClick={(params, event, details) => {
|
|
||||||
setCurrentServerData(params.row)
|
|
||||||
setServerDataOpen(true)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{/* <BarChart
|
{/* <BarChart
|
||||||
xAxis={[{ scaleType: 'band', data: ['group A', 'group B', 'group C'] }]}
|
xAxis={[{ scaleType: 'band', data: ['group A', 'group B', 'group C'] }]}
|
||||||
|
Reference in New Issue
Block a user