This commit is contained in:
cracklesparkle
2024-07-18 11:48:56 +09:00
parent 4283bd20bb
commit cf3fda43e4
20 changed files with 259 additions and 340 deletions

View File

@ -20,6 +20,7 @@
"@mui/material": "^5.15.20", "@mui/material": "^5.15.20",
"@mui/x-charts": "^7.8.0", "@mui/x-charts": "^7.8.0",
"@mui/x-data-grid": "^7.7.1", "@mui/x-data-grid": "^7.7.1",
"@uidotdev/usehooks": "^2.4.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"axios": "^1.7.2", "axios": "^1.7.2",
"file-type": "^19.0.0", "file-type": "^19.0.0",
@ -3897,6 +3898,18 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
} }
}, },
"node_modules/@uidotdev/usehooks": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.4.1.tgz",
"integrity": "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==",
"engines": {
"node": ">=16"
},
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
},
"node_modules/@ungap/structured-clone": { "node_modules/@ungap/structured-clone": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",

View File

@ -22,6 +22,7 @@
"@mui/material": "^5.15.20", "@mui/material": "^5.15.20",
"@mui/x-charts": "^7.8.0", "@mui/x-charts": "^7.8.0",
"@mui/x-data-grid": "^7.7.1", "@mui/x-data-grid": "^7.7.1",
"@uidotdev/usehooks": "^2.4.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"axios": "^1.7.2", "axios": "^1.7.2",
"file-type": "^19.0.0", "file-type": "^19.0.0",

View File

@ -1,5 +1,5 @@
import { Box, Chip, Divider, Paper, Typography } from '@mui/material' import { Divider, Paper, Typography } from '@mui/material'
import React, { PropsWithChildren, ReactElement, ReactNode } from 'react' import { PropsWithChildren } from 'react'
interface CardInfoProps extends PropsWithChildren { interface CardInfoProps extends PropsWithChildren {
label: string; label: string;

View File

@ -1,7 +1,5 @@
import { Cloud } from '@mui/icons-material'; import { Chip } from '@mui/material'
import { Chip, SvgIconTypeMap } from '@mui/material' import { ReactElement } from 'react'
import { OverridableComponent } from '@mui/material/OverridableComponent';
import React, { ReactElement } from 'react'
interface CardInfoChipProps { interface CardInfoChipProps {
status: boolean; status: boolean;

View File

@ -1,6 +1,4 @@
import { Box, Typography } from '@mui/material' import { Box, Typography } from '@mui/material'
import React from 'react'
interface CardInfoLabelProps { interface CardInfoLabelProps {
label: string; label: string;
value: string | number; value: string | number;

View File

@ -1,79 +0,0 @@
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>
)
}}
/>
)}
/>
)
}

View File

@ -3,8 +3,9 @@ import { IServer } from '../interfaces/servers'
import { useServerIps } from '../hooks/swrHooks' import { useServerIps } from '../hooks/swrHooks'
import FullFeaturedCrudGrid from './TableEditable' import FullFeaturedCrudGrid from './TableEditable'
import { GridColDef } from '@mui/x-data-grid' import { GridColDef } from '@mui/x-data-grid'
import { AxiosResponse } from 'axios'
function ServerData({ id, name, region_id }: IServer) { function ServerData({ id }: IServer) {
const { serverIps } = useServerIps(id, 0, 10) const { serverIps } = useServerIps(id, 0, 10)
const serverIpsColumns: GridColDef[] = [ const serverIpsColumns: GridColDef[] = [
@ -24,9 +25,16 @@ function ServerData({ id, name, region_id }: IServer) {
columns={serverIpsColumns} columns={serverIpsColumns}
actions actions
onRowClick={(params, event, details) => { onRowClick={(params, event, details) => {
console.log(params.id, event, details)
//setCurrentServerData(params.row) //setCurrentServerData(params.row)
//setServerDataOpen(true) //setServerDataOpen(true)
}} }}
onSave={undefined}
onDelete={function (data: any): Promise<AxiosResponse<any, any>> {
console.log(data)
throw new Error('Function not implemented.')
}}
loading={false}
/> />
} }
</Box> </Box>

View File

@ -1,7 +1,7 @@
import { AppBar, Autocomplete, CircularProgress, Dialog, IconButton, TextField, Toolbar } from '@mui/material' import { AppBar, Autocomplete, CircularProgress, Dialog, IconButton, TextField, Toolbar } from '@mui/material'
import React, { Fragment, useEffect, useState } from 'react' import { Fragment, useEffect, useState } from 'react'
import { IRegion } from '../interfaces/fuel' import { IRegion } from '../interfaces/fuel'
import { useHardwares, useRegions, useServerIps, useServers } from '../hooks/swrHooks' import { useHardwares, useServers } 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 } from '@mui/x-data-grid'
@ -95,41 +95,37 @@ export default function ServerHardware() {
: :
hardwares && hardwares &&
<FullFeaturedCrudGrid <FullFeaturedCrudGrid
autoComplete={ autoComplete={<Autocomplete
<Autocomplete open={open}
open={open} onOpen={() => {
onOpen={() => { setOpen(true)
setOpen(true) }}
}} onClose={() => {
onClose={() => { setOpen(false)
setOpen(false) }}
}} onInputChange={(_, value) => handleInputChange(value)}
onInputChange={(_, value) => handleInputChange(value)} onChange={(_, value) => handleOptionChange(value)}
onChange={(_, value) => handleOptionChange(value)} filterOptions={(x) => x}
filterOptions={(x) => x} isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name}
isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name} getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
getOptionLabel={(option: IRegion) => option.name ? option.name : ""} options={options}
options={options} loading={isLoading}
loading={isLoading} value={selectedOption}
value={selectedOption} renderInput={(params) => (
renderInput={(params) => ( <TextField
<TextField {...params}
{...params} label="Сервер"
label="Сервер" size='small'
size='small' InputProps={{
InputProps={{ ...params.InputProps,
...params.InputProps, endAdornment: (
endAdornment: ( <Fragment>
<Fragment> {isLoading ? <CircularProgress color="inherit" size={20} /> : null}
{isLoading ? <CircularProgress color="inherit" size={20} /> : null} {params.InputProps.endAdornment}
{params.InputProps.endAdornment} </Fragment>
</Fragment> )
) }} />
}} )} />}
/>
)}
/>
}
onSave={(id: any) => { onSave={(id: any) => {
console.log(id) console.log(id)
}} }}
@ -140,8 +136,7 @@ export default function ServerHardware() {
onRowClick={(params, event, details) => { onRowClick={(params, event, details) => {
setCurrentServerData(params.row) setCurrentServerData(params.row)
setServerDataOpen(true) setServerDataOpen(true)
}} }} loading={false} />
/>
} }
</> </>
) )

View File

@ -1,7 +1,7 @@
import { AppBar, Autocomplete, CircularProgress, Dialog, IconButton, TextField, Toolbar } from '@mui/material' import { AppBar, Autocomplete, CircularProgress, Dialog, IconButton, TextField, Toolbar } from '@mui/material'
import React, { Fragment, useEffect, useState } from 'react' import { Fragment, useEffect, useState } from 'react'
import { IRegion } from '../interfaces/fuel' import { IRegion } from '../interfaces/fuel'
import { useRegions, useServerIps, useServers } from '../hooks/swrHooks' import { useServerIps, useServers } 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 } from '@mui/x-data-grid'
@ -93,41 +93,37 @@ export default function ServerIpsView() {
: :
serverIps && serverIps &&
<FullFeaturedCrudGrid <FullFeaturedCrudGrid
autoComplete={ autoComplete={<Autocomplete
<Autocomplete open={open}
open={open} onOpen={() => {
onOpen={() => { setOpen(true)
setOpen(true) }}
}} onClose={() => {
onClose={() => { setOpen(false)
setOpen(false) }}
}} onInputChange={(_, value) => handleInputChange(value)}
onInputChange={(_, value) => handleInputChange(value)} onChange={(_, value) => handleOptionChange(value)}
onChange={(_, value) => handleOptionChange(value)} filterOptions={(x) => x}
filterOptions={(x) => x} isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name}
isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name} getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
getOptionLabel={(option: IRegion) => option.name ? option.name : ""} options={options}
options={options} loading={isLoading}
loading={isLoading} value={selectedOption}
value={selectedOption} renderInput={(params) => (
renderInput={(params) => ( <TextField
<TextField {...params}
{...params} size='small'
size='small' label="Сервер"
label="Сервер" InputProps={{
InputProps={{ ...params.InputProps,
...params.InputProps, endAdornment: (
endAdornment: ( <Fragment>
<Fragment> {isLoading ? <CircularProgress color="inherit" size={20} /> : null}
{isLoading ? <CircularProgress color="inherit" size={20} /> : null} {params.InputProps.endAdornment}
{params.InputProps.endAdornment} </Fragment>
</Fragment> )
) }} />
}} )} />}
/>
)}
/>
}
onSave={(id: any) => { onSave={(id: any) => {
console.log(id) console.log(id)
}} }}
@ -138,8 +134,7 @@ export default function ServerIpsView() {
onRowClick={(params, event, details) => { onRowClick={(params, event, details) => {
setCurrentServerData(params.row) setCurrentServerData(params.row)
setServerDataOpen(true) setServerDataOpen(true)
}} }} loading={false} />
/>
} }
</> </>
) )

View File

@ -1,34 +1,26 @@
import { AppBar, Autocomplete, CircularProgress, Dialog, IconButton, TextField, Toolbar } from '@mui/material' import { AppBar, Autocomplete, CircularProgress, Dialog, IconButton, TextField, Toolbar } from '@mui/material'
import React, { Fragment, useEffect, useState } from 'react' import { Fragment, useEffect, useState } from 'react'
import { IRegion } from '../interfaces/fuel' import { IRegion } from '../interfaces/fuel'
import { useHardwares, useRegions, useServerIps, useServers, useStorages } from '../hooks/swrHooks' import { useHardwares, useStorages } 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 } from '@mui/x-data-grid'
import { Close } from '@mui/icons-material' import { Close } from '@mui/icons-material'
import ServerData from './ServerData' import ServerData from './ServerData'
import { useDebounce } from '@uidotdev/usehooks'
export default function ServerStorage() { export default function ServerStorage() {
const [open, setOpen] = useState(false) const [open, setOpen] = 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 [selectedOption, setSelectedOption] = useState<IRegion | null>(null) const [selectedOption, setSelectedOption] = useState<IRegion | null>(null)
const { hardwares, isLoading } = useHardwares() const { hardwares, isLoading } = useHardwares()
const debouncedSearch = useDebounce(search, 500)
const [serverDataOpen, setServerDataOpen] = useState(false) const [serverDataOpen, setServerDataOpen] = useState(false)
const [currentServerData, setCurrentServerData] = useState<any | null>(null) const [currentServerData, setCurrentServerData] = useState<any | null>(null)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearch(search)
}, 500)
return () => {
clearTimeout(handler)
}
}, [search])
useEffect(() => { useEffect(() => {
if (hardwares) { if (hardwares) {
setOptions([...hardwares]) setOptions([...hardwares])
@ -92,41 +84,37 @@ export default function ServerStorage() {
: :
storages && storages &&
<FullFeaturedCrudGrid <FullFeaturedCrudGrid
autoComplete={ autoComplete={<Autocomplete
<Autocomplete open={open}
open={open} onOpen={() => {
onOpen={() => { setOpen(true)
setOpen(true) }}
}} onClose={() => {
onClose={() => { setOpen(false)
setOpen(false) }}
}} onInputChange={(_, value) => handleInputChange(value)}
onInputChange={(_, value) => handleInputChange(value)} onChange={(_, value) => handleOptionChange(value)}
onChange={(_, value) => handleOptionChange(value)} filterOptions={(x) => x}
filterOptions={(x) => x} isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name}
isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name} getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
getOptionLabel={(option: IRegion) => option.name ? option.name : ""} options={options}
options={options} loading={isLoading}
loading={isLoading} value={selectedOption}
value={selectedOption} renderInput={(params) => (
renderInput={(params) => ( <TextField
<TextField {...params}
{...params} size='small'
size='small' label="Hardware"
label="Hardware" InputProps={{
InputProps={{ ...params.InputProps,
...params.InputProps, endAdornment: (
endAdornment: ( <Fragment>
<Fragment> {isLoading ? <CircularProgress color="inherit" size={20} /> : null}
{isLoading ? <CircularProgress color="inherit" size={20} /> : null} {params.InputProps.endAdornment}
{params.InputProps.endAdornment} </Fragment>
</Fragment> )
) }} />
}} )} />}
/>
)}
/>
}
onSave={(id: any) => { onSave={(id: any) => {
console.log(id) console.log(id)
}} }}
@ -138,6 +126,7 @@ export default function ServerStorage() {
setCurrentServerData(params.row) setCurrentServerData(params.row)
setServerDataOpen(true) setServerDataOpen(true)
}} }}
loading={false}
/> />
} }
</> </>

View File

@ -1,5 +1,5 @@
import { AppBar, Autocomplete, Box, Chip, CircularProgress, Dialog, Divider, Grid, IconButton, Paper, TextField, Toolbar, Typography } from '@mui/material' import { AppBar, Autocomplete, Box, CircularProgress, Dialog, Grid, IconButton, TextField, Toolbar } from '@mui/material'
import React, { Fragment, useEffect, useState } from 'react' import { Fragment, useEffect, useState } from 'react'
import { IRegion } from '../interfaces/fuel' 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'
@ -11,47 +11,30 @@ import { IServersInfo } from '../interfaces/servers'
import CardInfo from './CardInfo/CardInfo' import CardInfo from './CardInfo/CardInfo'
import CardInfoLabel from './CardInfo/CardInfoLabel' import CardInfoLabel from './CardInfo/CardInfoLabel'
import CardInfoChip from './CardInfo/CardInfoChip' import CardInfoChip from './CardInfo/CardInfoChip'
import DataGridCellAutocomplete from './DataGridCellAutocomplete' import { useDebounce } from '@uidotdev/usehooks'
export default function ServersView() { export default function ServersView() {
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>("")
const debouncedSearch = useDebounce(search, 500)
const [selectedOption, setSelectedOption] = useState<IRegion | null>(null) const [selectedOption, setSelectedOption] = useState<IRegion | null>(null)
const { regions, isLoading } = useRegions(10, 1, debouncedSearch) const { regions, isLoading } = useRegions(10, 1, debouncedSearch)
const { serversInfo } = useServersInfo(selectedOption?.id) const { serversInfo } = useServersInfo(selectedOption?.id)
const [serverDataOpen, setServerDataOpen] = useState(false) const [serverDataOpen, setServerDataOpen] = useState(false)
const [currentServerData, setCurrentServerData] = useState<any | null>(null) const [currentServerData, setCurrentServerData] = useState<any | null>(null)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearch(search)
}, 500)
return () => {
clearTimeout(handler)
}
}, [search])
useEffect(() => { useEffect(() => {
if (regions) { if (regions) {
setOptions([...regions]) setOptions([...regions])
} }
}, [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 { servers, isLoading: serversLoading } = useServers(selectedOption?.id, 0, 10)
const serversColumns: GridColDef[] = [ const serversColumns: GridColDef[] = [
@ -60,16 +43,40 @@ export default function ServersView() {
{ {
field: 'region_id', field: 'region_id',
editable: true, editable: true,
renderCell: (params) => (
<div>
{params.value}
</div>
),
renderEditCell: (params: GridRenderCellParams) => ( renderEditCell: (params: GridRenderCellParams) => (
<DataGridCellAutocomplete <Autocomplete
selectedOption={params.value} sx={{ display: 'flex', flexGrow: '1' }}
setSelectedOption={(value: any) => { onInputChange={(_, value) => setSearch(value)}
onChange={(_, value) => {
params.value = value params.value = value
}} }}
isLoading={isLoading} isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name}
setDebouncedSearch={setDebouncedSearch} getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
options={options} options={options}
setOptions={setOptions} loading={isLoading}
value={params.value}
renderInput={(params) => (
<TextField
{...params}
size='small'
variant='standard'
label="Район"
InputProps={{
...params.InputProps,
endAdornment: (
<Fragment>
{isLoading ? <CircularProgress color="inherit" size={20} /> : null}
{params.InputProps.endAdornment}
</Fragment>
)
}}
/>
)}
/> />
), ),
width: 200 width: 200
@ -131,59 +138,47 @@ export default function ServersView() {
</Box> </Box>
} }
{serversLoading ? <FullFeaturedCrudGrid
<CircularProgress /> loading={serversLoading}
: autoComplete={
servers && <Autocomplete
<FullFeaturedCrudGrid onInputChange={(_, value) => setSearch(value)}
autoComplete={ onChange={(_, value) => setSelectedOption(value)}
<Autocomplete isOptionEqualToValue={(option: IRegion, value: IRegion) => option.id === value.id}
open={open} getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
onOpen={() => { options={options}
setOpen(true) loading={isLoading}
}} value={selectedOption}
onClose={() => { renderInput={(params) => (
setOpen(false) <TextField
}} {...params}
onInputChange={(_, value) => handleInputChange(value)} size='small'
onChange={(_, value) => handleOptionChange(value)} label="Район"
filterOptions={(x) => x} InputProps={{
isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name} ...params.InputProps,
getOptionLabel={(option: IRegion) => option.name ? option.name : ""} endAdornment: (
options={options} <Fragment>
loading={isLoading} {isLoading ? <CircularProgress color="inherit" size={20} /> : null}
value={selectedOption} {params.InputProps.endAdornment}
renderInput={(params) => ( </Fragment>
<TextField )
{...params} }}
size='small' />
label="Район" )}
InputProps={{ />
...params.InputProps, }
endAdornment: ( onSave={(id: any) => {
<Fragment> console.log(id)
{isLoading ? <CircularProgress color="inherit" size={20} /> : null} }}
{params.InputProps.endAdornment} onDelete={ServerService.removeServer}
</Fragment> initialRows={servers}
) columns={serversColumns}
}} actions
/> onRowClick={(params, event, details) => {
)} setCurrentServerData(params.row)
/> setServerDataOpen(true)
} }}
onSave={(id: any) => { />
console.log(id)
}}
onDelete={ServerService.removeServer}
initialRows={servers}
columns={serversColumns}
actions
onRowClick={(params, event, details) => {
setCurrentServerData(params.row)
setServerDataOpen(true)
}}
/>
}
</> </>
) )
} }

View File

@ -1,4 +1,4 @@
import * as React from 'react'; import { useEffect, useState } from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
@ -19,12 +19,8 @@ import {
GridRowModel, GridRowModel,
GridRowEditStopReasons, GridRowEditStopReasons,
GridSlots, GridSlots,
GridRowProps,
GridRow,
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;
@ -90,6 +86,7 @@ interface DataGridProps {
onSave: any; onSave: any;
onDelete: (data: any) => Promise<AxiosResponse<any, any>>; onDelete: (data: any) => Promise<AxiosResponse<any, any>>;
autoComplete?: React.ReactElement | null; autoComplete?: React.ReactElement | null;
loading: boolean;
} }
export default function FullFeaturedCrudGrid({ export default function FullFeaturedCrudGrid({
@ -99,10 +96,11 @@ export default function FullFeaturedCrudGrid({
onRowClick, onRowClick,
onSave, onSave,
onDelete, onDelete,
autoComplete autoComplete,
loading
}: DataGridProps) { }: DataGridProps) {
const [rows, setRows] = React.useState(initialRows); const [rows, setRows] = useState(initialRows);
const [rowModesModel, setRowModesModel] = React.useState<GridRowModesModel>({}); const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => { const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => {
if (params.reason === GridRowEditStopReasons.rowFocusOut) { if (params.reason === GridRowEditStopReasons.rowFocusOut) {
@ -147,6 +145,12 @@ export default function FullFeaturedCrudGrid({
setRowModesModel(newRowModesModel); setRowModesModel(newRowModesModel);
}; };
useEffect(() => {
if (initialRows) {
setRows(initialRows)
}
}, [initialRows])
const actionColumns: GridColDef[] = [ const actionColumns: GridColDef[] = [
{ {
field: 'actions', field: 'actions',
@ -210,7 +214,8 @@ export default function FullFeaturedCrudGrid({
}} }}
> >
<DataGrid <DataGrid
rows={rows} loading={loading}
rows={rows || []}
columns={actions ? [...columns, ...actionColumns] : columns} columns={actions ? [...columns, ...actionColumns] : columns}
editMode="row" editMode="row"
rowModesModel={rowModesModel} rowModesModel={rowModesModel}

View File

@ -184,12 +184,12 @@ export function useBoilers(limit?: number, page?: number, search?: string) {
// Servers // Servers
export function useServers(region_id?: number, offset?: number, limit?: number) { export function useServers(region_id?: number | null, 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}` : `/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,
} }
) )

View File

@ -11,7 +11,6 @@ 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, Dashboard, Factory, Home, People, Shield, Storage, } from '@mui/icons-material';
import { colors, 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';

View File

@ -9,7 +9,7 @@ import { createTheme } from '@mui/material'
import { ruRU } from '@mui/material/locale' import { ruRU } from '@mui/material/locale'
import { getDarkMode, usePrefStore } from "./store/preferences.ts"; import { getDarkMode, usePrefStore } from "./store/preferences.ts";
const darkTheme = createTheme( const mainTheme = createTheme(
{ {
typography: { typography: {
fontFamily: [ fontFamily: [
@ -28,6 +28,11 @@ const darkTheme = createTheme(
} }
} }
}, },
}
)
const darkTheme = createTheme(
{
palette: { palette: {
mode: "dark", mode: "dark",
primary: { main: '#1976d2' }, primary: { main: '#1976d2' },
@ -38,23 +43,6 @@ const darkTheme = createTheme(
const lightTheme = createTheme( const lightTheme = createTheme(
{ {
typography: {
fontFamily: [
'Inter'
].join(',')
},
components: {
MuiButtonBase: {
defaultProps: {
//disableRipple: true,
}
},
MuiButtonGroup: {
defaultProps: {
//disableRipple: true,
}
}
},
palette: { palette: {
mode: "light", mode: "light",
primary: { main: '#1976d2' }, primary: { main: '#1976d2' },
@ -82,7 +70,7 @@ function ThemedApp() {
}, []) }, [])
return ( return (
<ThemeProvider theme={prefStore.darkMode ? darkTheme : lightTheme}> <ThemeProvider theme={prefStore.darkMode ? { ...mainTheme, ...darkTheme } : { ...mainTheme, ...lightTheme }}>
<App /> <App />
</ThemeProvider> </ThemeProvider>
) )

View File

@ -1,6 +1,6 @@
import { Box, Typography } from '@mui/material' import { Box, Typography } from '@mui/material'
import { DataGrid, GridColDef } from '@mui/x-data-grid' import { DataGrid, GridColDef } from '@mui/x-data-grid'
import React, { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { IBoiler } from '../interfaces/fuel' import { IBoiler } from '../interfaces/fuel'
import { useBoilers } from '../hooks/swrHooks' import { useBoilers } from '../hooks/swrHooks'

View File

@ -9,6 +9,7 @@ export default function Servers() {
const [currentTab, setCurrentTab] = useState(0) const [currentTab, setCurrentTab] = useState(0)
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
console.log(event)
setCurrentTab(newValue); setCurrentTab(newValue);
} }

View File

@ -10,7 +10,7 @@ const config: AxiosRequestConfig = {
} }
export default class AuthService { export default class AuthService {
static async login(data: FormData) { static async login(data: URLSearchParams) {
return await axiosInstance.post(`/auth/login`, data, config) return await axiosInstance.post(`/auth/login`, data, config)
} }

View File

@ -5,10 +5,13 @@
], ],
"target": "ES2020", "target": "ES2020",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"], "lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
/* Bundler mode */ /* Bundler mode */
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
@ -16,13 +19,18 @@
"isolatedModules": true, "isolatedModules": true,
"noEmit": true, "noEmit": true,
"jsx": "react-jsx", "jsx": "react-jsx",
/* Linting */ /* Linting */
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },
"include": ["src"], "include": [
"references": [{ "path": "./tsconfig.node.json" }] "src"
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
} }

View File

@ -1706,6 +1706,11 @@
"@typescript-eslint/types" "7.13.0" "@typescript-eslint/types" "7.13.0"
eslint-visitor-keys "^3.4.3" eslint-visitor-keys "^3.4.3"
"@uidotdev/usehooks@^2.4.1":
version "2.4.1"
resolved "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.4.1.tgz"
integrity sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==
"@ungap/structured-clone@^1.2.0": "@ungap/structured-clone@^1.2.0":
version "1.2.0" version "1.2.0"
resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz"
@ -4167,7 +4172,7 @@ randomfill@^1.0.3:
randombytes "^2.0.5" randombytes "^2.0.5"
safe-buffer "^5.1.0" safe-buffer "^5.1.0"
"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^18.2.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0: "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^18.2.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, react-dom@>=18.0.0:
version "18.3.1" version "18.3.1"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz"
integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
@ -4220,7 +4225,7 @@ react-transition-group@^4.4.5:
loose-envify "^1.4.0" loose-envify "^1.4.0"
prop-types "^15.6.2" prop-types "^15.6.2"
"react@^16.11.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0", react@^18.2.0, react@^18.3.1, react@>=16.6.0, react@>=16.8, react@>=16.8.0: "react@^16.11.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0", react@^18.2.0, react@^18.3.1, react@>=16.6.0, react@>=16.8, react@>=16.8.0, react@>=18.0.0:
version "18.3.1" version "18.3.1"
resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz" resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz"
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==