forked from VinokurovVE/tests
8 changed files with 727 additions and 292 deletions
-
23frontend_reactjs/src/components/ServerData.tsx
-
146frontend_reactjs/src/components/ServerHardware.tsx
-
144frontend_reactjs/src/components/ServerIpsView.tsx
-
143frontend_reactjs/src/components/ServerStorages.tsx
-
191frontend_reactjs/src/components/ServersView.tsx
-
33frontend_reactjs/src/components/TableEditable.tsx
-
36frontend_reactjs/src/hooks/swrHooks.ts
-
303frontend_reactjs/src/pages/Servers.tsx
@ -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) |
|||
}} |
|||
/> |
|||
} |
|||
</> |
|||
) |
|||
} |
@ -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) |
|||
}} |
|||
/> |
|||
} |
|||
</> |
|||
) |
|||
} |
@ -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) |
|||
}} |
|||
/> |
|||
} |
|||
</> |
|||
) |
|||
} |
@ -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) |
|||
}} |
|||
/> |
|||
} |
|||
</> |
|||
) |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue