Files
tests/frontend_reactjs/src/pages/Servers.tsx
2024-07-12 17:44:44 +09:00

275 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { AppBar, Autocomplete, Box, Chip, CircularProgress, Dialog, Divider, Grid, IconButton, Paper, TextField, Toolbar, Typography, colors } from "@mui/material"
import { useBoilers, useCities, useRegions, useServers, useServersInfo } from "../hooks/swrHooks"
import { Fragment, useEffect, useState } from "react"
import { IBoiler, ICity, IRegion } from "../interfaces/fuel"
import { DataGrid, GridColDef } from "@mui/x-data-grid"
import ServerData from "../components/ServerData"
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() {
const [open, setOpen] = useState(false)
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 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 [citiesOpen, setCitiesOpen] = useState(false)
const [citiesPage, setCitiesPage] = useState(1)
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) => {
setCitiesSearch(value)
}
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 (
<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' }}>
<Typography variant='h6' fontWeight='600'>
Servers by region
</Typography>
<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>
)
}}
/>
)}
/>
<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 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
initialRows={servers}
columns={serversColumns}
actions
onRowClick={(params, event, details) => {
setCurrentServerData(params.row)
setServerDataOpen(true)
}}
/>
}
{/* <BarChart
xAxis={[{ scaleType: 'band', data: ['group A', 'group B', 'group C'] }]}
series={[{ data: [4, 3, 5] }, { data: [1, 6, 3] }, { data: [2, 5, 6] }]}
width={500}
height={300}
/> */}
</Box>
</Box>
)
}