Skip to content

Commit 397ceff

Browse files
committed
Add create new server button
1 parent 5127a50 commit 397ceff

File tree

6 files changed

+180
-31
lines changed

6 files changed

+180
-31
lines changed

frontend/src/environments/EnvironmentList.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { IconButton } from '@mui/material';
2-
import { DataGrid, GridColDef } from '@mui/x-data-grid';
2+
import { DataGrid, GridColDef, GridRowSelectionModel } from '@mui/x-data-grid';
33
import { IEnvironmentData } from './types';
44
import { memo, useMemo } from 'react';
55
import CheckIcon from '@mui/icons-material/Check';
@@ -84,8 +84,13 @@ const columns: GridColDef[] = [
8484

8585
export interface IEnvironmentListProps {
8686
images: IEnvironmentData[];
87-
default_cpu_limit: string;
88-
default_mem_limit: string;
87+
default_cpu_limit?: string;
88+
default_mem_limit?: string;
89+
hideRemoveButton?: boolean;
90+
pageSize?: number;
91+
selectable?: boolean;
92+
rowSelectionModel?: GridRowSelectionModel;
93+
setRowSelectionModel?: (selected: GridRowSelectionModel) => void;
8994
}
9095

9196
function _EnvironmentList(props: IEnvironmentListProps) {
@@ -95,12 +100,11 @@ function _EnvironmentList(props: IEnvironmentListProps) {
95100
newItem.cpu_limit =
96101
newItem.cpu_limit.length > 0
97102
? newItem.cpu_limit
98-
: props.default_cpu_limit;
103+
: props.default_cpu_limit ?? '2';
99104
newItem.mem_limit =
100105
newItem.mem_limit.length > 0
101106
? newItem.mem_limit
102-
: props.default_mem_limit;
103-
// newItem.status = 'building';
107+
: props.default_mem_limit ?? '2';
104108
return newItem;
105109
});
106110
}, [props]);
@@ -113,12 +117,21 @@ function _EnvironmentList(props: IEnvironmentListProps) {
113117
initialState={{
114118
pagination: {
115119
paginationModel: {
116-
pageSize: 100
120+
pageSize: props.pageSize ?? 100
117121
}
118122
}
119123
}}
120-
pageSizeOptions={[100]}
121-
disableRowSelectionOnClick
124+
pageSizeOptions={[props.pageSize ?? 100]}
125+
disableRowSelectionOnClick={!Boolean(props.selectable)}
126+
sx={{
127+
'& .MuiDataGrid-virtualScroller::-webkit-scrollbar': {
128+
overflow: rows.length > 0 ? 'auto' : 'hidden'
129+
}
130+
}}
131+
columnVisibilityModel={{ remove: !Boolean(props.hideRemoveButton) }}
132+
checkboxSelection={Boolean(props.selectable)}
133+
rowSelectionModel={props.rowSelectionModel}
134+
onRowSelectionModelChange={props.setRowSelectionModel}
122135
/>
123136
</Box>
124137
);

frontend/src/servers/App.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ import { AxiosContext } from '../common/AxiosContext';
88
import { useMemo } from 'react';
99
import { AxiosClient } from '../common/axiosclient';
1010
import { ServerList } from './ServersList';
11+
import { NewServerDialog } from './NewServerDialog';
12+
import { IEnvironmentData } from '../environments/types';
1113

1214
export interface IAppProps {
15+
images: IEnvironmentData[]
1316
server_data: IServerData[];
1417
allow_named_servers: boolean;
1518
named_server_limit_per_user: number;
@@ -22,12 +25,13 @@ export default function App(props: IAppProps) {
2225
return new AxiosClient({ baseUrl, xsrfToken });
2326
}, []);
2427
console.log('props', props);
25-
28+
2629
return (
2730
<ThemeProvider theme={customTheme}>
2831
<AxiosContext.Provider value={axios}>
2932
<ScopedCssBaseline>
3033
<Stack sx={{ padding: 1 }} spacing={1}>
34+
<NewServerDialog images={props.images}/>
3135
<ServerList servers={props.server_data} />
3236
</Stack>
3337
</ScopedCssBaseline>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { Box, Button, DialogContentText } from '@mui/material';
2+
import Dialog from '@mui/material/Dialog';
3+
import DialogActions from '@mui/material/DialogActions';
4+
import DialogContent from '@mui/material/DialogContent';
5+
import DialogTitle from '@mui/material/DialogTitle';
6+
import { Fragment, memo, useCallback, useState } from 'react';
7+
8+
// import { useAxios } from '../common/AxiosContext';
9+
import { IEnvironmentData } from '../environments/types';
10+
import { EnvironmentList } from '../environments/EnvironmentList';
11+
import { GridRowSelectionModel } from '@mui/x-data-grid';
12+
13+
export interface INewServerDialogProps {
14+
images: IEnvironmentData[];
15+
}
16+
17+
function _NewServerDialog(props: INewServerDialogProps) {
18+
// const axios = useAxios();
19+
const [open, setOpen] = useState(false);
20+
const handleOpen = () => {
21+
setOpen(true);
22+
};
23+
const handleClose = (
24+
event?: any,
25+
reason?: 'backdropClick' | 'escapeKeyDown'
26+
) => {
27+
if (reason && reason === 'backdropClick') {
28+
return;
29+
}
30+
setOpen(false);
31+
};
32+
33+
const [rowSelectionModel, setRowSelectionModel] =
34+
useState<GridRowSelectionModel>([]);
35+
const updateSelectedRow = useCallback(
36+
(selected: GridRowSelectionModel) => {
37+
if (selected.length > 1) {
38+
setRowSelectionModel([selected[selected.length - 1]]);
39+
} else {
40+
setRowSelectionModel(selected);
41+
}
42+
},
43+
[setRowSelectionModel]
44+
);
45+
return (
46+
<Fragment>
47+
<Box sx={{ display: 'flex', flexDirection: 'row-reverse' }}>
48+
<Button onClick={handleOpen} variant="contained">
49+
Create new Server
50+
</Button>
51+
</Box>
52+
<Dialog open={open} onClose={handleClose} fullWidth maxWidth={'md'}>
53+
<DialogTitle>Server Options</DialogTitle>
54+
<DialogContent>
55+
<DialogContentText>Select an environment</DialogContentText>
56+
<EnvironmentList
57+
images={props.images}
58+
hideRemoveButton={true}
59+
pageSize={10}
60+
selectable
61+
rowSelectionModel={rowSelectionModel}
62+
setRowSelectionModel={updateSelectedRow}
63+
/>
64+
</DialogContent>
65+
<DialogActions>
66+
<Button variant="contained" color="error" onClick={handleClose}>
67+
Cancel
68+
</Button>
69+
<Button
70+
variant="contained"
71+
color="primary"
72+
disabled={rowSelectionModel.length === 0}
73+
>
74+
Create Server
75+
</Button>
76+
</DialogActions>
77+
</Dialog>
78+
</Fragment>
79+
);
80+
}
81+
82+
export const NewServerDialog = memo(_NewServerDialog);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Typography } from '@mui/material';
2+
import Box from '@mui/material/Box';
3+
import { memo, useCallback } from 'react';
4+
5+
import { useAxios } from '../common/AxiosContext';
6+
import { ButtonWithConfirm } from '../common/ButtonWithConfirm';
7+
import { API_PREFIX } from './types';
8+
9+
interface IRemoveServerButton {
10+
user: string;
11+
server: string;
12+
}
13+
14+
function _RemoveServerButton(props: IRemoveServerButton) {
15+
const axios = useAxios();
16+
17+
const removeEnv = useCallback(async () => {
18+
const response = await axios.request({
19+
method: 'delete',
20+
path: API_PREFIX,
21+
data: { name: props.server }
22+
});
23+
if (response?.status === 'ok') {
24+
window.location.reload();
25+
} else {
26+
}
27+
}, [props.server, axios]);
28+
29+
return (
30+
<ButtonWithConfirm
31+
buttonLabel="Stop Server"
32+
dialogTitle="Stop Server"
33+
dialogBody={
34+
<Box>
35+
<Typography>
36+
Are you sure you want to stop the following server?
37+
</Typography>
38+
<pre>{props.server}</pre>
39+
</Box>
40+
}
41+
action={removeEnv}
42+
/>
43+
);
44+
}
45+
46+
export const RemoveServerButton = memo(_RemoveServerButton);

frontend/src/servers/ServersList.tsx

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { memo, useMemo } from 'react';
44
import { Box } from '@mui/system';
55
import { IServerData } from './types';
66
import { formatTime } from '../common/utils';
7+
import { RemoveServerButton } from './RemoveServerButton';
8+
import { Button } from '@mui/material';
79

810
const columns: GridColDef[] = [
911
{
@@ -12,43 +14,40 @@ const columns: GridColDef[] = [
1214
flex: 1
1315
},
1416
{
15-
field: 'url',
16-
headerName: 'URL',
17-
flex: 1,
18-
renderCell: params => {
19-
return (
20-
<a href={params.value} target="_blank">
21-
{params.value}
22-
</a>
23-
);
24-
}
17+
field: 'image',
18+
headerName: 'Image',
19+
flex: 1
2520
},
2621
{
2722
field: 'last_activity',
2823
headerName: 'Last activity',
29-
flex: 1,
30-
maxWidth: 150
31-
},
32-
{
33-
field: 'image',
34-
headerName: 'Image',
35-
flex:1,
24+
width: 150
3625
},
3726
{
3827
field: 'status',
3928
headerName: '',
40-
width: 150,
29+
width: 125,
4130
filterable: false,
4231
sortable: false,
4332
hideable: false,
33+
renderCell: params => {
34+
return <RemoveServerButton server={params.row.name} user={'alice'} />;
35+
}
4436
},
4537
{
4638
field: 'action',
4739
headerName: '',
48-
width: 100,
40+
width: 125,
4941
filterable: false,
5042
sortable: false,
51-
hideable: false
43+
hideable: false,
44+
renderCell: params => {
45+
return (
46+
<Button href={params.row.url} target="_blank">
47+
Open Server
48+
</Button>
49+
);
50+
}
5251
}
5352
];
5453

@@ -80,6 +79,11 @@ function _ServerList(props: IServerListProps) {
8079
}}
8180
pageSizeOptions={[100]}
8281
disableRowSelectionOnClick
82+
sx={{
83+
'& .MuiDataGrid-virtualScroller::-webkit-scrollbar': {
84+
overflow: rows.length > 0 ? 'auto' : 'hidden'
85+
}
86+
}}
8387
/>
8488
</Box>
8589
);

frontend/src/servers/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import App, { IAppProps } from './App';
1010

1111
const rootElement = document.getElementById('servers-root');
1212
const root = createRoot(rootElement!);
13-
console.log('AAAAAAAAAA');
1413

1514
const dataElement = document.getElementById('tljh-page-data');
1615
let configData: IAppProps = {
16+
images: [],
1717
server_data: [],
1818
allow_named_servers: false,
1919
named_server_limit_per_user: 0

0 commit comments

Comments
 (0)