forked from VinokurovVE/tests
DataGrid cell autocomplete
This commit is contained in:
@ -15,6 +15,90 @@ import Documents from "./pages/Documents"
|
|||||||
import Reports from "./pages/Reports"
|
import Reports from "./pages/Reports"
|
||||||
import Boilers from "./pages/Boilers"
|
import Boilers from "./pages/Boilers"
|
||||||
import Servers from "./pages/Servers"
|
import Servers from "./pages/Servers"
|
||||||
|
import { Api, Assignment, Cloud, Factory, Home, Login, People, Shield, Storage } from "@mui/icons-material"
|
||||||
|
|
||||||
|
export const pages = [
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
path: "/auth/signin",
|
||||||
|
icon: <Login />,
|
||||||
|
component: <SignIn />,
|
||||||
|
drawer: false,
|
||||||
|
dashboard: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
path: "/auth/signup",
|
||||||
|
icon: <Login />,
|
||||||
|
component: <SignUp />,
|
||||||
|
drawer: false,
|
||||||
|
dashboard: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Главная",
|
||||||
|
path: "/",
|
||||||
|
icon: <Home />,
|
||||||
|
component: <Main />,
|
||||||
|
drawer: true,
|
||||||
|
dashboard: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Пользователи",
|
||||||
|
path: "/user",
|
||||||
|
icon: <People />,
|
||||||
|
component: <Users />,
|
||||||
|
drawer: true,
|
||||||
|
dashboard: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Роли",
|
||||||
|
path: "/role",
|
||||||
|
icon: <Shield />,
|
||||||
|
component: <Roles />,
|
||||||
|
drawer: true,
|
||||||
|
dashboard: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Документы",
|
||||||
|
path: "/documents",
|
||||||
|
icon: <Storage />,
|
||||||
|
component: <Documents />,
|
||||||
|
drawer: true,
|
||||||
|
dashboard: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Отчеты",
|
||||||
|
path: "/reports",
|
||||||
|
icon: <Assignment />,
|
||||||
|
component: <Reports />,
|
||||||
|
drawer: true,
|
||||||
|
dashboard: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Серверы",
|
||||||
|
path: "/servers",
|
||||||
|
icon: <Cloud />,
|
||||||
|
component: <Servers />,
|
||||||
|
drawer: true,
|
||||||
|
dashboard: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Котельные",
|
||||||
|
path: "/boilers",
|
||||||
|
icon: <Factory />,
|
||||||
|
component: <Boilers />,
|
||||||
|
drawer: true,
|
||||||
|
dashboard: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "API Test",
|
||||||
|
path: "/api-test",
|
||||||
|
icon: <Api />,
|
||||||
|
component: <ApiTest />,
|
||||||
|
drawer: true,
|
||||||
|
dashboard: true
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
@ -44,19 +128,15 @@ function App() {
|
|||||||
<Router>
|
<Router>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<MainLayout />}>
|
<Route element={<MainLayout />}>
|
||||||
<Route path="/auth/signin" element={<SignIn />} />
|
{pages.filter((page) => !page.dashboard).map((page, index) => (
|
||||||
<Route path="/auth/signup" element={<SignUp />} />
|
<Route key={`ml-${index}`} path={page.path} element={page.component} />
|
||||||
|
))}
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route element={auth.isAuthenticated ? <DashboardLayout /> : <Navigate to={"/auth/signin"} />}>
|
<Route element={auth.isAuthenticated ? <DashboardLayout /> : <Navigate to={"/auth/signin"} />}>
|
||||||
<Route path="/" element={<Main />} />
|
{pages.filter((page) => page.dashboard).map((page, index) => (
|
||||||
<Route path="/user" element={<Users />} />
|
<Route key={`dl-${index}`} path={page.path} element={page.component} />
|
||||||
<Route path="/role" element={<Roles />} />
|
))}
|
||||||
<Route path="/documents" element={<Documents />} />
|
|
||||||
<Route path="/reports" element={<Reports />} />
|
|
||||||
<Route path="/servers" element={<Servers />} />
|
|
||||||
<Route path="/boilers" element={<Boilers />} />
|
|
||||||
<Route path="/api-test" element={<ApiTest />} />
|
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
23
frontend_reactjs/src/components/CardInfo/CardInfo.tsx
Normal file
23
frontend_reactjs/src/components/CardInfo/CardInfo.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Box, Chip, Divider, Paper, Typography } from '@mui/material'
|
||||||
|
import React, { PropsWithChildren, ReactElement, ReactNode } from 'react'
|
||||||
|
|
||||||
|
interface CardInfoProps extends PropsWithChildren {
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CardInfo({
|
||||||
|
children,
|
||||||
|
label
|
||||||
|
}: CardInfoProps) {
|
||||||
|
return (
|
||||||
|
<Paper sx={{ display: 'flex', flexDirection: 'column', gap: '16px', p: '16px' }}>
|
||||||
|
<Typography fontWeight={600}>
|
||||||
|
{label}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
27
frontend_reactjs/src/components/CardInfo/CardInfoChip.tsx
Normal file
27
frontend_reactjs/src/components/CardInfo/CardInfoChip.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Cloud } from '@mui/icons-material';
|
||||||
|
import { Chip, SvgIconTypeMap } from '@mui/material'
|
||||||
|
import { OverridableComponent } from '@mui/material/OverridableComponent';
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
|
||||||
|
interface CardInfoChipProps {
|
||||||
|
status: boolean;
|
||||||
|
label: string;
|
||||||
|
iconOn: ReactElement
|
||||||
|
iconOff: ReactElement
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CardInfoChip({
|
||||||
|
status,
|
||||||
|
label,
|
||||||
|
iconOn,
|
||||||
|
iconOff
|
||||||
|
}: CardInfoChipProps) {
|
||||||
|
return (
|
||||||
|
<Chip
|
||||||
|
icon={status ? iconOn : iconOff}
|
||||||
|
variant="outlined"
|
||||||
|
label={label}
|
||||||
|
color={status ? "success" : "error"}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
24
frontend_reactjs/src/components/CardInfo/CardInfoLabel.tsx
Normal file
24
frontend_reactjs/src/components/CardInfo/CardInfoLabel.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Box, Typography } from '@mui/material'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
interface CardInfoLabelProps {
|
||||||
|
label: string;
|
||||||
|
value: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CardInfoLabel({
|
||||||
|
label,
|
||||||
|
value
|
||||||
|
}: CardInfoLabelProps) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<Typography>
|
||||||
|
{label}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="h6" fontWeight={600}>
|
||||||
|
{value}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
79
frontend_reactjs/src/components/DataGridCellAutocomplete.tsx
Normal file
79
frontend_reactjs/src/components/DataGridCellAutocomplete.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { Autocomplete, CircularProgress, TextField } from '@mui/material'
|
||||||
|
import React, { Fragment, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
interface DataGridCellAutocompleteProps {
|
||||||
|
selectedOption: any;
|
||||||
|
setSelectedOption: any;
|
||||||
|
isLoading: boolean;
|
||||||
|
setDebouncedSearch: any;
|
||||||
|
options: any;
|
||||||
|
setOptions: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DataGridCellAutocomplete({
|
||||||
|
selectedOption,
|
||||||
|
setSelectedOption,
|
||||||
|
isLoading,
|
||||||
|
setDebouncedSearch,
|
||||||
|
options,
|
||||||
|
setOptions
|
||||||
|
}: DataGridCellAutocompleteProps) {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const [search, setSearch] = useState<string | null>("")
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedSearch(search)
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler)
|
||||||
|
}
|
||||||
|
}, [search])
|
||||||
|
|
||||||
|
const handleInputChange = (value: string) => {
|
||||||
|
setSearch(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOptionChange = (value: any | null) => {
|
||||||
|
setSelectedOption(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Autocomplete
|
||||||
|
sx={{ flexGrow: '1' }}
|
||||||
|
open={open}
|
||||||
|
onOpen={() => {
|
||||||
|
setOpen(true)
|
||||||
|
}}
|
||||||
|
onClose={() => {
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
onInputChange={(_, value) => handleInputChange(value)}
|
||||||
|
onChange={(_, value) => handleOptionChange(value)}
|
||||||
|
filterOptions={(x) => x}
|
||||||
|
isOptionEqualToValue={(option: any, value: any) => option.name === value.name}
|
||||||
|
getOptionLabel={(option: any) => option.name ? option.name : ""}
|
||||||
|
options={options}
|
||||||
|
loading={isLoading}
|
||||||
|
value={selectedOption}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
size='small'
|
||||||
|
label="Район"
|
||||||
|
variant='standard'
|
||||||
|
InputProps={{
|
||||||
|
...params.InputProps,
|
||||||
|
endAdornment: (
|
||||||
|
<Fragment>
|
||||||
|
{isLoading ? <CircularProgress color="inherit" size={20} /> : null}
|
||||||
|
{params.InputProps.endAdornment}
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
@ -90,6 +90,12 @@ export default function ServerHardware() {
|
|||||||
}
|
}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
{serversLoading ?
|
||||||
|
<CircularProgress />
|
||||||
|
:
|
||||||
|
hardwares &&
|
||||||
|
<FullFeaturedCrudGrid
|
||||||
|
autoComplete={
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
open={open}
|
open={open}
|
||||||
onOpen={() => {
|
onOpen={() => {
|
||||||
@ -110,6 +116,7 @@ export default function ServerHardware() {
|
|||||||
<TextField
|
<TextField
|
||||||
{...params}
|
{...params}
|
||||||
label="Сервер"
|
label="Сервер"
|
||||||
|
size='small'
|
||||||
InputProps={{
|
InputProps={{
|
||||||
...params.InputProps,
|
...params.InputProps,
|
||||||
endAdornment: (
|
endAdornment: (
|
||||||
@ -122,12 +129,7 @@ export default function ServerHardware() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
{serversLoading ?
|
|
||||||
<CircularProgress />
|
|
||||||
:
|
|
||||||
servers &&
|
|
||||||
<FullFeaturedCrudGrid
|
|
||||||
onSave={(id: any) => {
|
onSave={(id: any) => {
|
||||||
console.log(id)
|
console.log(id)
|
||||||
}}
|
}}
|
||||||
|
@ -88,6 +88,12 @@ export default function ServerIpsView() {
|
|||||||
}
|
}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
{serversLoading ?
|
||||||
|
<CircularProgress />
|
||||||
|
:
|
||||||
|
serverIps &&
|
||||||
|
<FullFeaturedCrudGrid
|
||||||
|
autoComplete={
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
open={open}
|
open={open}
|
||||||
onOpen={() => {
|
onOpen={() => {
|
||||||
@ -107,6 +113,7 @@ export default function ServerIpsView() {
|
|||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
{...params}
|
{...params}
|
||||||
|
size='small'
|
||||||
label="Сервер"
|
label="Сервер"
|
||||||
InputProps={{
|
InputProps={{
|
||||||
...params.InputProps,
|
...params.InputProps,
|
||||||
@ -120,12 +127,7 @@ export default function ServerIpsView() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
{serversLoading ?
|
|
||||||
<CircularProgress />
|
|
||||||
:
|
|
||||||
servers &&
|
|
||||||
<FullFeaturedCrudGrid
|
|
||||||
onSave={(id: any) => {
|
onSave={(id: any) => {
|
||||||
console.log(id)
|
console.log(id)
|
||||||
}}
|
}}
|
||||||
|
@ -87,6 +87,12 @@ export default function ServerStorage() {
|
|||||||
}
|
}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
{serversLoading ?
|
||||||
|
<CircularProgress />
|
||||||
|
:
|
||||||
|
storages &&
|
||||||
|
<FullFeaturedCrudGrid
|
||||||
|
autoComplete={
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
open={open}
|
open={open}
|
||||||
onOpen={() => {
|
onOpen={() => {
|
||||||
@ -106,6 +112,7 @@ export default function ServerStorage() {
|
|||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
{...params}
|
{...params}
|
||||||
|
size='small'
|
||||||
label="Hardware"
|
label="Hardware"
|
||||||
InputProps={{
|
InputProps={{
|
||||||
...params.InputProps,
|
...params.InputProps,
|
||||||
@ -119,12 +126,7 @@ export default function ServerStorage() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
{serversLoading ?
|
|
||||||
<CircularProgress />
|
|
||||||
:
|
|
||||||
storages &&
|
|
||||||
<FullFeaturedCrudGrid
|
|
||||||
onSave={(id: any) => {
|
onSave={(id: any) => {
|
||||||
console.log(id)
|
console.log(id)
|
||||||
}}
|
}}
|
||||||
|
@ -4,13 +4,18 @@ import { IRegion } from '../interfaces/fuel'
|
|||||||
import { useRegions, useServers, useServersInfo } from '../hooks/swrHooks'
|
import { useRegions, useServers, useServersInfo } from '../hooks/swrHooks'
|
||||||
import FullFeaturedCrudGrid from './TableEditable'
|
import FullFeaturedCrudGrid from './TableEditable'
|
||||||
import ServerService from '../services/ServersService'
|
import ServerService from '../services/ServersService'
|
||||||
import { GridColDef } from '@mui/x-data-grid'
|
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid'
|
||||||
import { Close, Cloud, CloudOff } from '@mui/icons-material'
|
import { Close, Cloud, CloudOff } from '@mui/icons-material'
|
||||||
import ServerData from './ServerData'
|
import ServerData from './ServerData'
|
||||||
import { IServersInfo } from '../interfaces/servers'
|
import { IServersInfo } from '../interfaces/servers'
|
||||||
|
import CardInfo from './CardInfo/CardInfo'
|
||||||
|
import CardInfoLabel from './CardInfo/CardInfoLabel'
|
||||||
|
import CardInfoChip from './CardInfo/CardInfoChip'
|
||||||
|
import DataGridCellAutocomplete from './DataGridCellAutocomplete'
|
||||||
|
|
||||||
export default function ServersView() {
|
export default function ServersView() {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
const [editOpen, setEditOpen] = useState(false)
|
||||||
const [options, setOptions] = useState<IRegion[]>([])
|
const [options, setOptions] = useState<IRegion[]>([])
|
||||||
const [search, setSearch] = useState<string | null>("")
|
const [search, setSearch] = useState<string | null>("")
|
||||||
const [debouncedSearch, setDebouncedSearch] = useState<string | null>("")
|
const [debouncedSearch, setDebouncedSearch] = useState<string | null>("")
|
||||||
@ -52,6 +57,23 @@ export default function ServersView() {
|
|||||||
const serversColumns: GridColDef[] = [
|
const serversColumns: GridColDef[] = [
|
||||||
//{ field: 'id', headerName: 'ID', type: "number" },
|
//{ field: 'id', headerName: 'ID', type: "number" },
|
||||||
{ field: 'name', headerName: 'Название', type: "string", editable: true },
|
{ field: 'name', headerName: 'Название', type: "string", editable: true },
|
||||||
|
{
|
||||||
|
field: 'region_id',
|
||||||
|
editable: true,
|
||||||
|
renderEditCell: (params: GridRenderCellParams) => (
|
||||||
|
<DataGridCellAutocomplete
|
||||||
|
selectedOption={params.value}
|
||||||
|
setSelectedOption={(value: any) => {
|
||||||
|
params.value = value
|
||||||
|
}}
|
||||||
|
isLoading={isLoading}
|
||||||
|
setDebouncedSearch={setDebouncedSearch}
|
||||||
|
options={options}
|
||||||
|
setOptions={setOptions}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
width: 200
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -88,6 +110,33 @@ export default function ServersView() {
|
|||||||
}
|
}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
{serversInfo &&
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%' }}>
|
||||||
|
<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}>
|
||||||
|
<CardInfo label={serverInfo.name}>
|
||||||
|
<CardInfoLabel label='Количество IP' value={serverInfo.IPs_count} />
|
||||||
|
<CardInfoLabel label='Количество серверов' value={serverInfo.servers_count} />
|
||||||
|
<CardInfoChip
|
||||||
|
status={serverInfo.status === "Online"}
|
||||||
|
label={serverInfo.status}
|
||||||
|
iconOn={<Cloud />}
|
||||||
|
iconOff={<CloudOff />}
|
||||||
|
/>
|
||||||
|
</CardInfo>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
|
||||||
|
{serversLoading ?
|
||||||
|
<CircularProgress />
|
||||||
|
:
|
||||||
|
servers &&
|
||||||
|
<FullFeaturedCrudGrid
|
||||||
|
autoComplete={
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
open={open}
|
open={open}
|
||||||
onOpen={() => {
|
onOpen={() => {
|
||||||
@ -107,6 +156,7 @@ export default function ServersView() {
|
|||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
{...params}
|
{...params}
|
||||||
|
size='small'
|
||||||
label="Район"
|
label="Район"
|
||||||
InputProps={{
|
InputProps={{
|
||||||
...params.InputProps,
|
...params.InputProps,
|
||||||
@ -120,59 +170,7 @@ export default function ServersView() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{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) => {
|
onSave={(id: any) => {
|
||||||
console.log(id)
|
console.log(id)
|
||||||
}}
|
}}
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
useGridApiContext,
|
useGridApiContext,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
|
import { Autocomplete } from '@mui/material';
|
||||||
|
|
||||||
interface EditToolbarProps {
|
interface EditToolbarProps {
|
||||||
setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;
|
setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;
|
||||||
@ -31,10 +32,11 @@ interface EditToolbarProps {
|
|||||||
newModel: (oldModel: GridRowModesModel) => GridRowModesModel,
|
newModel: (oldModel: GridRowModesModel) => GridRowModesModel,
|
||||||
) => void;
|
) => void;
|
||||||
columns: GridColDef[];
|
columns: GridColDef[];
|
||||||
|
autoComplete?: React.ReactElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditToolbar(props: EditToolbarProps) {
|
function EditToolbar(props: EditToolbarProps) {
|
||||||
const { setRows, setRowModesModel, columns } = props;
|
const { setRows, setRowModesModel, columns, autoComplete } = props;
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
const id = Date.now().toString(36)
|
const id = Date.now().toString(36)
|
||||||
@ -50,6 +52,12 @@ function EditToolbar(props: EditToolbarProps) {
|
|||||||
} else {
|
} else {
|
||||||
newValues[column.field] = undefined
|
newValues[column.field] = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (column.field === 'region_id') {
|
||||||
|
// column.valueGetter = (value: any) => {
|
||||||
|
// console.log(value)
|
||||||
|
// }
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
setRows((oldRows) => [...oldRows, { id, ...newValues, isNew: true }]);
|
setRows((oldRows) => [...oldRows, { id, ...newValues, isNew: true }]);
|
||||||
@ -60,7 +68,13 @@ function EditToolbar(props: EditToolbarProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridToolbarContainer>
|
<GridToolbarContainer sx={{ px: '16px', py: '16px' }}>
|
||||||
|
{autoComplete &&
|
||||||
|
<Box sx={{ flexGrow: '1' }}>
|
||||||
|
{autoComplete}
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
|
||||||
<Button color="primary" startIcon={<AddIcon />} onClick={handleClick}>
|
<Button color="primary" startIcon={<AddIcon />} onClick={handleClick}>
|
||||||
Добавить
|
Добавить
|
||||||
</Button>
|
</Button>
|
||||||
@ -75,6 +89,7 @@ interface DataGridProps {
|
|||||||
onRowClick: GridEventListener<"rowClick">;
|
onRowClick: GridEventListener<"rowClick">;
|
||||||
onSave: any;
|
onSave: any;
|
||||||
onDelete: (data: any) => Promise<AxiosResponse<any, any>>;
|
onDelete: (data: any) => Promise<AxiosResponse<any, any>>;
|
||||||
|
autoComplete?: React.ReactElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FullFeaturedCrudGrid({
|
export default function FullFeaturedCrudGrid({
|
||||||
@ -83,7 +98,8 @@ export default function FullFeaturedCrudGrid({
|
|||||||
actions = false,
|
actions = false,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
onSave,
|
onSave,
|
||||||
onDelete
|
onDelete,
|
||||||
|
autoComplete
|
||||||
}: 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>({});
|
||||||
@ -206,7 +222,7 @@ export default function FullFeaturedCrudGrid({
|
|||||||
toolbar: EditToolbar as GridSlots['toolbar'],
|
toolbar: EditToolbar as GridSlots['toolbar'],
|
||||||
}}
|
}}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
toolbar: { setRows, setRowModesModel, columns },
|
toolbar: { setRows, setRowModesModel, columns, autoComplete },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -189,8 +189,7 @@ export function useServers(region_id?: number, offset?: number, limit?: number)
|
|||||||
region_id ? `/api/servers?region_id=${region_id}&offset=${offset || 0}&limit=${limit || 10}` : `/api/servers?offset=${offset || 0}&limit=${limit || 10}`,
|
region_id ? `/api/servers?region_id=${region_id}&offset=${offset || 0}&limit=${limit || 10}` : `/api/servers?offset=${offset || 0}&limit=${limit || 10}`,
|
||||||
(url: string) => fetcher(url, BASE_URL.servers),
|
(url: string) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false
|
||||||
revalidateOnMount: false
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -203,11 +202,10 @@ export function useServers(region_id?: number, offset?: number, limit?: number)
|
|||||||
|
|
||||||
export function useServersInfo(region_id?: number, offset?: number, limit?: number) {
|
export function useServersInfo(region_id?: number, offset?: number, limit?: number) {
|
||||||
const { data, error, isLoading } = useSWR(
|
const { data, error, isLoading } = useSWR(
|
||||||
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}` : `/api/servers_info?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
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -224,7 +222,6 @@ export function useServer(server_id?: number) {
|
|||||||
(url) => fetcher(url, BASE_URL.servers),
|
(url) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
revalidateOnMount: false
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -241,7 +238,6 @@ export function useServerIps(server_id?: number | null, offset?: number, limit?:
|
|||||||
(url: string) => fetcher(url, BASE_URL.servers),
|
(url: string) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
revalidateOnMount: false
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -260,7 +256,6 @@ export function useHardwares(server_id?: number, offset?: number, limit?: number
|
|||||||
(url: string) => fetcher(url, BASE_URL.servers),
|
(url: string) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
revalidateOnMount: false
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -296,7 +291,6 @@ export function useStorages(hardware_id?: number, offset?: number, limit?: numbe
|
|||||||
(url: string) => fetcher(url, BASE_URL.servers),
|
(url: string) => fetcher(url, BASE_URL.servers),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
revalidateOnMount: false
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,13 +11,14 @@ import Divider from '@mui/material/Divider';
|
|||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import Container from '@mui/material/Container';
|
import Container from '@mui/material/Container';
|
||||||
import MenuIcon from '@mui/icons-material/Menu';
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
import { Api, Assignment, Cloud, Factory, Home, People, Shield, Storage, } from '@mui/icons-material';
|
import { Api, Assignment, Cloud, Dashboard, Factory, Home, People, Shield, Storage, } from '@mui/icons-material';
|
||||||
import { ListItem, ListItemButton, ListItemIcon, ListItemText, } from '@mui/material';
|
import { colors, ListItem, ListItemButton, ListItemIcon, ListItemText, } from '@mui/material';
|
||||||
import { Outlet, useNavigate } from 'react-router-dom';
|
import { Outlet, useNavigate } from 'react-router-dom';
|
||||||
import { UserData } from '../interfaces/auth';
|
import { UserData } from '../interfaces/auth';
|
||||||
import { getUserData, useAuthStore } from '../store/auth';
|
import { getUserData, useAuthStore } from '../store/auth';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import AccountMenu from '../components/AccountMenu';
|
import AccountMenu from '../components/AccountMenu';
|
||||||
|
import { pages } from '../App';
|
||||||
|
|
||||||
const drawerWidth: number = 240;
|
const drawerWidth: number = 240;
|
||||||
|
|
||||||
@ -69,49 +70,6 @@ const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open'
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const pages = [
|
|
||||||
{
|
|
||||||
label: "Главная",
|
|
||||||
path: "/",
|
|
||||||
icon: <Home />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Пользователи",
|
|
||||||
path: "/user",
|
|
||||||
icon: <People />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Роли",
|
|
||||||
path: "/role",
|
|
||||||
icon: <Shield />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Документы",
|
|
||||||
path: "/documents",
|
|
||||||
icon: <Storage />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Отчеты",
|
|
||||||
path: "/reports",
|
|
||||||
icon: <Assignment />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Серверы",
|
|
||||||
path: "/servers",
|
|
||||||
icon: <Cloud />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Котельные",
|
|
||||||
path: "/boilers",
|
|
||||||
icon: <Factory />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "API Test",
|
|
||||||
path: "/api-test",
|
|
||||||
icon: <Api />
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export default function DashboardLayout() {
|
export default function DashboardLayout() {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const innerTheme = createTheme(theme)
|
const innerTheme = createTheme(theme)
|
||||||
@ -186,17 +144,12 @@ export default function DashboardLayout() {
|
|||||||
<Typography variant="caption">{userData?.login}</Typography>
|
<Typography variant="caption">{userData?.login}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* <IconButton color="inherit">
|
|
||||||
<Badge badgeContent={0} color="secondary">
|
|
||||||
<AccountCircle />
|
|
||||||
</Badge>
|
|
||||||
</IconButton> */}
|
|
||||||
|
|
||||||
<AccountMenu />
|
<AccountMenu />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
|
||||||
<Drawer variant="permanent" open={open}>
|
<Drawer variant="permanent" open={open}>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
sx={{
|
sx={{
|
||||||
@ -214,7 +167,7 @@ export default function DashboardLayout() {
|
|||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<List component="nav">
|
<List component="nav">
|
||||||
{pages.map((item, index) => (
|
{pages.filter((page) => page.drawer).map((item, index) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
key={index}
|
key={index}
|
||||||
disablePadding
|
disablePadding
|
||||||
@ -231,6 +184,7 @@ export default function DashboardLayout() {
|
|||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={item.label}
|
primary={item.label}
|
||||||
|
sx={{ color: location.pathname === item.path ? colors.blue[700] : innerTheme.palette.text.primary }}
|
||||||
/>
|
/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
@ -253,10 +207,7 @@ export default function DashboardLayout() {
|
|||||||
<Toolbar />
|
<Toolbar />
|
||||||
<Container
|
<Container
|
||||||
maxWidth="lg"
|
maxWidth="lg"
|
||||||
sx={{
|
sx={{ mt: 4, mb: 4 }}
|
||||||
mt: 4,
|
|
||||||
mb: 4
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -1,268 +1,10 @@
|
|||||||
import { AppBar, Autocomplete, Box, Chip, CircularProgress, Dialog, Divider, Grid, IconButton, Paper, TextField, Toolbar, Typography, colors } from "@mui/material"
|
import { Box } 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"
|
|
||||||
|
|
||||||
export default function ApiTest() {
|
export default function ApiTest() {
|
||||||
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" },
|
|
||||||
]
|
|
||||||
|
|
||||||
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', alignItems: 'center', gap: '8px' }}>
|
|
||||||
<Storage />
|
|
||||||
<Typography variant='h6' fontWeight='600'>
|
|
||||||
Servers
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
|
|
||||||
<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
|
|
||||||
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 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 &&
|
|
||||||
<DataGrid
|
|
||||||
rowSelection={false}
|
|
||||||
rows={servers}
|
|
||||||
columns={serversColumns}
|
|
||||||
onRowClick={(params, event, details) => {
|
|
||||||
setCurrentServerData(params.row)
|
|
||||||
setServerDataOpen(true)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
Reference in New Issue
Block a user