mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-24 13:41:07 +08:00
Separate dialog components
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import React, { useState } from "react"
|
||||
import {
|
||||
Dialog,
|
||||
useTheme,
|
||||
FormLabel,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
FormControl,
|
||||
Button,
|
||||
DialogActions,
|
||||
MenuItem,
|
||||
Select,
|
||||
} from "@mui/material"
|
||||
import { actionCreator } from "../action"
|
||||
import { DebouncedInputTextField } from "./Debounce"
|
||||
import { useRecoilValue } from "recoil"
|
||||
import { studySummariesState } from "../state"
|
||||
import RemoveIcon from "@mui/icons-material/Remove"
|
||||
import AddIcon from "@mui/icons-material/Add"
|
||||
|
||||
type UsePreferenceDialogReturn = [() => void, () => JSX.Element]
|
||||
|
||||
export const useCreateStudyDialog = (): UsePreferenceDialogReturn => {
|
||||
const theme = useTheme()
|
||||
const action = actionCreator()
|
||||
|
||||
const [newStudyName, setNewStudyName] = useState("")
|
||||
const [openNewStudyDialog, setOpenNewStudyDialog] = useState(false)
|
||||
const [directions, setDirections] = useState<StudyDirection[]>(["minimize"])
|
||||
const studies = useRecoilValue<StudySummary[]>(studySummariesState)
|
||||
const newStudyNameAlreadyUsed = studies.some(
|
||||
(v) => v.study_name === newStudyName
|
||||
)
|
||||
|
||||
const handleCloseNewStudyDialog = () => {
|
||||
setOpenNewStudyDialog(false)
|
||||
setNewStudyName("")
|
||||
setDirections(["minimize"])
|
||||
}
|
||||
|
||||
const handleCreateNewStudy = () => {
|
||||
action.createNewStudy(newStudyName, directions)
|
||||
setOpenNewStudyDialog(false)
|
||||
setNewStudyName("")
|
||||
setDirections(["minimize"])
|
||||
}
|
||||
|
||||
const openDialog = () => {
|
||||
setOpenNewStudyDialog(true)
|
||||
}
|
||||
|
||||
const renderCreateNewStudyDialog = () => {
|
||||
return (
|
||||
<Dialog
|
||||
open={openNewStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseNewStudyDialog()
|
||||
}}
|
||||
aria-labelledby="create-study-dialog-title"
|
||||
>
|
||||
<DialogTitle id="create-study-dialog-title">New Study</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Please enter the study name and directions here.
|
||||
</DialogContentText>
|
||||
<DebouncedInputTextField
|
||||
onChange={(s) => {
|
||||
setNewStudyName(s)
|
||||
}}
|
||||
delay={500}
|
||||
textFieldProps={{
|
||||
autoFocus: true,
|
||||
fullWidth: true,
|
||||
error: newStudyNameAlreadyUsed,
|
||||
helperText: newStudyNameAlreadyUsed
|
||||
? `"${newStudyName}" is already used`
|
||||
: "",
|
||||
label: "Study name",
|
||||
type: "text",
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
{directions.map((d, i) => (
|
||||
<DialogContent key={i}>
|
||||
<FormControl component="fieldset" fullWidth={true}>
|
||||
<FormLabel component="legend">Objective {i}:</FormLabel>
|
||||
<Select
|
||||
value={directions[i]}
|
||||
onChange={(e) => {
|
||||
const newVal: StudyDirection[] = [...directions]
|
||||
newVal[i] = e.target.value as StudyDirection
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
<MenuItem value="minimize">Minimize</MenuItem>
|
||||
<MenuItem value="maximize">Maximize</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</DialogContent>
|
||||
))}
|
||||
<DialogContent>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<AddIcon />}
|
||||
sx={{ marginRight: theme.spacing(1) }}
|
||||
onClick={() => {
|
||||
const newVal: StudyDirection[] = [...directions, "minimize"]
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<RemoveIcon />}
|
||||
sx={{ marginRight: theme.spacing(1) }}
|
||||
disabled={directions.length <= 1}
|
||||
onClick={() => {
|
||||
const newVal: StudyDirection[] = [...directions]
|
||||
newVal.pop()
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCloseNewStudyDialog} color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCreateNewStudy}
|
||||
color="primary"
|
||||
disabled={
|
||||
newStudyName === "" ||
|
||||
newStudyNameAlreadyUsed ||
|
||||
directions.length === 0
|
||||
}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
return [openDialog, renderCreateNewStudyDialog]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import React, { useState } from "react"
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
Button,
|
||||
DialogActions,
|
||||
} from "@mui/material"
|
||||
import { actionCreator } from "../action"
|
||||
|
||||
type UsePreferenceDialogReturn = [(studyId: number) => void, () => JSX.Element]
|
||||
|
||||
export const useDeleteStudyDialog = (): UsePreferenceDialogReturn => {
|
||||
const action = actionCreator()
|
||||
|
||||
const [openDeleteStudyDialog, setOpenDeleteStudyDialog] = useState(false)
|
||||
const [deleteStudyID, setDeleteStudyID] = useState(-1)
|
||||
|
||||
const handleCloseDeleteStudyDialog = () => {
|
||||
setOpenDeleteStudyDialog(false)
|
||||
setDeleteStudyID(-1)
|
||||
}
|
||||
|
||||
const handleDeleteStudy = () => {
|
||||
action.deleteStudy(deleteStudyID)
|
||||
setOpenDeleteStudyDialog(false)
|
||||
setDeleteStudyID(-1)
|
||||
}
|
||||
|
||||
const openDialog = (studyId: number) => {
|
||||
setDeleteStudyID(studyId)
|
||||
setOpenDeleteStudyDialog(true)
|
||||
}
|
||||
|
||||
const renderCreateNewStudyDialog = () => {
|
||||
return (
|
||||
<Dialog
|
||||
open={openDeleteStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseDeleteStudyDialog()
|
||||
}}
|
||||
aria-labelledby="delete-study-dialog-title"
|
||||
>
|
||||
<DialogTitle id="delete-study-dialog-title">Delete study</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Are you sure you want to delete a study (id={deleteStudyID})?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCloseDeleteStudyDialog} color="primary">
|
||||
No
|
||||
</Button>
|
||||
<Button onClick={handleDeleteStudy} color="primary">
|
||||
Yes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
return [openDialog, renderCreateNewStudyDialog]
|
||||
}
|
||||
@@ -9,33 +9,13 @@ import {
|
||||
Card,
|
||||
Grid,
|
||||
Box,
|
||||
Button,
|
||||
IconButton,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogActions,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
Menu,
|
||||
MenuItem,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Select,
|
||||
useTheme,
|
||||
InputAdornment,
|
||||
SvgIcon,
|
||||
CardContent,
|
||||
} from "@mui/material"
|
||||
import {
|
||||
Add,
|
||||
AddBox,
|
||||
Delete,
|
||||
Refresh,
|
||||
Remove,
|
||||
Search,
|
||||
} from "@mui/icons-material"
|
||||
import { AddBox, Delete, Refresh, Search } from "@mui/icons-material"
|
||||
|
||||
import { actionCreator } from "../action"
|
||||
import { DataGrid, DataGridColumn } from "./DataGrid"
|
||||
@@ -44,6 +24,8 @@ import { studySummariesState } from "../state"
|
||||
import Brightness7Icon from "@mui/icons-material/Brightness7"
|
||||
import Brightness4Icon from "@mui/icons-material/Brightness4"
|
||||
import { useSnackbar } from "notistack"
|
||||
import { useDeleteStudyDialog } from "./DeleteStudyDialog"
|
||||
import { useCreateStudyDialog } from "./CreateStudyDialog"
|
||||
|
||||
export const StudyList: FC<{
|
||||
toggleColorMode: () => void
|
||||
@@ -51,21 +33,6 @@ export const StudyList: FC<{
|
||||
const theme = useTheme()
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
const [newStudySelectionAnchorEl, setNewStudySelectionAnchorEl] =
|
||||
React.useState<null | HTMLElement>(null)
|
||||
const openNewStudySelection = Boolean(newStudySelectionAnchorEl)
|
||||
const [
|
||||
openNewSingleObjectiveStudyDialog,
|
||||
setOpenNewSingleObjectiveStudyDialog,
|
||||
] = React.useState(false)
|
||||
const [
|
||||
openNewMultiObjectiveStudyDialog,
|
||||
setOpenNewMultiObjectiveStudyDialog,
|
||||
] = React.useState(false)
|
||||
const [openDeleteStudyDialog, setOpenDeleteStudyDialog] =
|
||||
React.useState(false)
|
||||
const [deleteStudyID, setDeleteStudyID] = React.useState(-1)
|
||||
const [newStudyName, setNewStudyName] = React.useState("")
|
||||
const [studyFilterText, setStudyFilterText] = React.useState<string>("")
|
||||
const studyFilter = (row: StudySummary) => {
|
||||
const keywords = studyFilterText.split(" ")
|
||||
@@ -76,11 +43,11 @@ export const StudyList: FC<{
|
||||
return row.study_name.indexOf(k) >= 0
|
||||
})
|
||||
}
|
||||
const [openDeleteStudyDialog, renderDeleteStudyDialog] =
|
||||
useDeleteStudyDialog()
|
||||
const [openCreateStudyDialog, renderCreateStudyDialog] =
|
||||
useCreateStudyDialog()
|
||||
|
||||
const [maximize, setMaximize] = React.useState(false)
|
||||
const [directions, setDirections] = React.useState<StudyDirection[]>([
|
||||
"minimize",
|
||||
])
|
||||
const linkColor = useMemo(
|
||||
() =>
|
||||
theme.palette.mode === "dark"
|
||||
@@ -92,10 +59,6 @@ export const StudyList: FC<{
|
||||
const action = actionCreator()
|
||||
const studies = useRecoilValue<StudySummary[]>(studySummariesState)
|
||||
|
||||
const newStudyNameAlreadyUsed = studies.some(
|
||||
(v) => v.study_name === newStudyName
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
action.updateStudySummaries()
|
||||
}, [])
|
||||
@@ -136,8 +99,7 @@ export const StudyList: FC<{
|
||||
size="small"
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
setDeleteStudyID(studies[i].study_id)
|
||||
setOpenDeleteStudyDialog(true)
|
||||
openDeleteStudyDialog(studies[i].study_id)
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
@@ -151,42 +113,6 @@ export const StudyList: FC<{
|
||||
{ field: "value", label: "Value", sortable: true },
|
||||
]
|
||||
|
||||
const handleCloseNewSingleObjectiveStudyDialog = () => {
|
||||
setNewStudyName("")
|
||||
setOpenNewSingleObjectiveStudyDialog(false)
|
||||
}
|
||||
|
||||
const handleCreateNewSingleObjectiveStudy = () => {
|
||||
const direction = maximize ? "maximize" : "minimize"
|
||||
action.createNewStudy(newStudyName, [direction])
|
||||
setOpenNewSingleObjectiveStudyDialog(false)
|
||||
setNewStudyName("")
|
||||
}
|
||||
|
||||
const handleCloseNewMultiObjectiveStudyDialog = () => {
|
||||
setOpenNewMultiObjectiveStudyDialog(false)
|
||||
setNewStudyName("")
|
||||
setDirections(["minimize"])
|
||||
}
|
||||
|
||||
const handleCreateNewMultiObjectiveStudy = () => {
|
||||
action.createNewStudy(newStudyName, directions)
|
||||
setOpenNewMultiObjectiveStudyDialog(false)
|
||||
setNewStudyName("")
|
||||
setDirections(["minimize"])
|
||||
}
|
||||
|
||||
const handleCloseDeleteStudyDialog = () => {
|
||||
setOpenDeleteStudyDialog(false)
|
||||
setDeleteStudyID(-1)
|
||||
}
|
||||
|
||||
const handleDeleteStudy = () => {
|
||||
action.deleteStudy(deleteStudyID)
|
||||
setOpenDeleteStudyDialog(false)
|
||||
setDeleteStudyID(-1)
|
||||
}
|
||||
|
||||
const collapseBody = (index: number) => {
|
||||
return (
|
||||
<Grid container direction="row">
|
||||
@@ -278,47 +204,13 @@ export const StudyList: FC<{
|
||||
aria-controls="menu-appbar"
|
||||
aria-haspopup="true"
|
||||
onClick={(e) => {
|
||||
setNewStudySelectionAnchorEl(e.currentTarget)
|
||||
openCreateStudyDialog()
|
||||
}}
|
||||
color="inherit"
|
||||
title="Create new study"
|
||||
>
|
||||
<AddBox />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="menu-appbar"
|
||||
anchorEl={newStudySelectionAnchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
open={openNewStudySelection}
|
||||
onClose={() => {
|
||||
setNewStudySelectionAnchorEl(null)
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
setNewStudySelectionAnchorEl(null)
|
||||
setOpenNewSingleObjectiveStudyDialog(true)
|
||||
}}
|
||||
>
|
||||
Single-objective
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
setNewStudySelectionAnchorEl(null)
|
||||
setOpenNewMultiObjectiveStudyDialog(true)
|
||||
}}
|
||||
>
|
||||
Multi-objective
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
</AppBar>
|
||||
@@ -383,183 +275,8 @@ export const StudyList: FC<{
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
<Dialog
|
||||
open={openNewSingleObjectiveStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseNewSingleObjectiveStudyDialog()
|
||||
}}
|
||||
aria-labelledby="create-single-objective-study-form-dialog-title"
|
||||
>
|
||||
<DialogTitle id="create-single-objective-study-form-dialog-title">
|
||||
New single-objective study
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
To create a new study, please enter the study name here.
|
||||
</DialogContentText>
|
||||
<DebouncedInputTextField
|
||||
onChange={(s) => {
|
||||
setNewStudyName(s)
|
||||
}}
|
||||
delay={500}
|
||||
textFieldProps={{
|
||||
type: "text",
|
||||
autoFocus: true,
|
||||
fullWidth: true,
|
||||
error: newStudyNameAlreadyUsed,
|
||||
helperText: newStudyNameAlreadyUsed
|
||||
? `"${newStudyName}" is already used`
|
||||
: "",
|
||||
label: "Study name",
|
||||
}}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={maximize}
|
||||
onChange={() => {
|
||||
setMaximize(!maximize)
|
||||
}}
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Set maximize direction (default: minimize)"
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={handleCloseNewSingleObjectiveStudyDialog}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCreateNewSingleObjectiveStudy}
|
||||
color="primary"
|
||||
disabled={newStudyName === "" || newStudyNameAlreadyUsed}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Dialog
|
||||
open={openNewMultiObjectiveStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseNewMultiObjectiveStudyDialog()
|
||||
}}
|
||||
aria-labelledby="create-multi-objective-study-form-dialog-title"
|
||||
>
|
||||
<DialogTitle id="create-multi-objective-study-form-dialog-title">
|
||||
New multi-objective study
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
To create a new study, please enter the study name and directions
|
||||
here.
|
||||
</DialogContentText>
|
||||
<DebouncedInputTextField
|
||||
onChange={(s) => {
|
||||
setNewStudyName(s)
|
||||
}}
|
||||
delay={500}
|
||||
textFieldProps={{
|
||||
autoFocus: true,
|
||||
fullWidth: true,
|
||||
error: newStudyNameAlreadyUsed,
|
||||
helperText: newStudyNameAlreadyUsed
|
||||
? `"${newStudyName}" is already used`
|
||||
: "",
|
||||
label: "Study name",
|
||||
type: "text",
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
{directions.map((d, i) => (
|
||||
<DialogContent key={i}>
|
||||
<FormControl component="fieldset" fullWidth={true}>
|
||||
<FormLabel component="legend">Objective {i}:</FormLabel>
|
||||
<Select
|
||||
value={directions[i]}
|
||||
onChange={(e) => {
|
||||
const newVal: StudyDirection[] = [...directions]
|
||||
newVal[i] = e.target.value as StudyDirection
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
<MenuItem value="minimize">Minimize</MenuItem>
|
||||
<MenuItem value="maximize">Maximize</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</DialogContent>
|
||||
))}
|
||||
<DialogContent>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
sx={{ marginRight: theme.spacing(1) }}
|
||||
onClick={() => {
|
||||
const newVal: StudyDirection[] = [...directions, "minimize"]
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Remove />}
|
||||
sx={{ marginRight: theme.spacing(1) }}
|
||||
disabled={directions.length <= 1}
|
||||
onClick={() => {
|
||||
const newVal: StudyDirection[] = [...directions]
|
||||
newVal.pop()
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={handleCloseNewMultiObjectiveStudyDialog}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCreateNewMultiObjectiveStudy}
|
||||
color="primary"
|
||||
disabled={
|
||||
newStudyName === "" ||
|
||||
newStudyNameAlreadyUsed ||
|
||||
directions.length === 0
|
||||
}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Dialog
|
||||
open={openDeleteStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseDeleteStudyDialog()
|
||||
}}
|
||||
aria-labelledby="delete-study-dialog-title"
|
||||
>
|
||||
<DialogTitle id="delete-study-dialog-title">Delete study</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Are you sure you want to delete a study (id={deleteStudyID})?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCloseDeleteStudyDialog} color="primary">
|
||||
No
|
||||
</Button>
|
||||
<Button onClick={handleDeleteStudy} color="primary">
|
||||
Yes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
{renderCreateStudyDialog()}
|
||||
{renderDeleteStudyDialog()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,58 +5,34 @@ import {
|
||||
Typography,
|
||||
Container,
|
||||
Card,
|
||||
Grid,
|
||||
Box,
|
||||
Button,
|
||||
IconButton,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogActions,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
MenuItem,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
useTheme,
|
||||
InputAdornment,
|
||||
SvgIcon,
|
||||
CardContent,
|
||||
TextField,
|
||||
Menu,
|
||||
} from "@mui/material"
|
||||
import { Add, Delete, Remove, Search } from "@mui/icons-material"
|
||||
import { Delete, Refresh, Search } from "@mui/icons-material"
|
||||
import SortIcon from "@mui/icons-material/Sort"
|
||||
import AddBoxIcon from "@mui/icons-material/AddBox"
|
||||
|
||||
import { actionCreator } from "../action"
|
||||
import { DataGrid, DataGridColumn } from "./DataGrid"
|
||||
import { DebouncedInputTextField } from "./Debounce"
|
||||
import { studySummariesState } from "../state"
|
||||
import { styled } from "@mui/system"
|
||||
import { AppDrawer } from "./AppDrawer"
|
||||
import { useCreateStudyDialog } from "./CreateStudyDialog"
|
||||
import { useDeleteStudyDialog } from "./DeleteStudyDialog"
|
||||
|
||||
export const StudyListBeta: FC<{
|
||||
toggleColorMode: () => void
|
||||
}> = ({ toggleColorMode }) => {
|
||||
const theme = useTheme()
|
||||
const action = actionCreator()
|
||||
|
||||
const [newStudySelectionAnchorEl, setNewStudySelectionAnchorEl] =
|
||||
React.useState<null | HTMLElement>(null)
|
||||
const openNewStudySelection = Boolean(newStudySelectionAnchorEl)
|
||||
const [
|
||||
openNewSingleObjectiveStudyDialog,
|
||||
setOpenNewSingleObjectiveStudyDialog,
|
||||
] = React.useState(false)
|
||||
const [
|
||||
openNewMultiObjectiveStudyDialog,
|
||||
setOpenNewMultiObjectiveStudyDialog,
|
||||
] = React.useState(false)
|
||||
const [openDeleteStudyDialog, setOpenDeleteStudyDialog] =
|
||||
React.useState(false)
|
||||
const [deleteStudyID, setDeleteStudyID] = React.useState(-1)
|
||||
const [newStudyName, setNewStudyName] = React.useState("")
|
||||
const [studyFilterText, setStudyFilterText] = React.useState<string>("")
|
||||
const studyFilter = (row: StudySummary) => {
|
||||
const keywords = studyFilterText.split(" ")
|
||||
@@ -67,11 +43,10 @@ export const StudyListBeta: FC<{
|
||||
return row.study_name.indexOf(k) >= 0
|
||||
})
|
||||
}
|
||||
|
||||
const [maximize, setMaximize] = React.useState(false)
|
||||
const [directions, setDirections] = React.useState<StudyDirection[]>([
|
||||
"minimize",
|
||||
])
|
||||
const [openCreateStudyDialog, renderCreateStudyDialog] =
|
||||
useCreateStudyDialog()
|
||||
const [openDeleteStudyDialog, renderDeleteStudyDialog] =
|
||||
useDeleteStudyDialog()
|
||||
const linkColor = useMemo(
|
||||
() =>
|
||||
theme.palette.mode === "dark"
|
||||
@@ -80,141 +55,13 @@ export const StudyListBeta: FC<{
|
||||
[theme.palette.mode]
|
||||
)
|
||||
|
||||
const action = actionCreator()
|
||||
const studies = useRecoilValue<StudySummary[]>(studySummariesState)
|
||||
|
||||
const newStudyNameAlreadyUsed = studies.some(
|
||||
(v) => v.study_name === newStudyName
|
||||
)
|
||||
const filteredStudy = studies.filter((s) => !studyFilter(s))
|
||||
|
||||
useEffect(() => {
|
||||
action.updateStudySummaries()
|
||||
}, [])
|
||||
|
||||
const columns: DataGridColumn<StudySummary>[] = [
|
||||
{
|
||||
field: "study_id",
|
||||
label: "Study ID",
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: "study_name",
|
||||
label: "Name",
|
||||
sortable: true,
|
||||
toCellValue: (i) => (
|
||||
<Link
|
||||
to={`${URL_PREFIX}/studies/${studies[i].study_id}/beta`}
|
||||
style={{ color: linkColor }}
|
||||
>
|
||||
{studies[i].study_name}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: "directions",
|
||||
label: "Direction",
|
||||
sortable: false,
|
||||
toCellValue: (i) => studies[i].directions.join(),
|
||||
},
|
||||
{
|
||||
field: "study_name",
|
||||
label: "",
|
||||
sortable: false,
|
||||
padding: "none",
|
||||
toCellValue: (i) => (
|
||||
<IconButton
|
||||
aria-label="delete study"
|
||||
size="small"
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
setDeleteStudyID(studies[i].study_id)
|
||||
setOpenDeleteStudyDialog(true)
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
const collapseAttrColumns: DataGridColumn<Attribute>[] = [
|
||||
{ field: "key", label: "Key", sortable: true },
|
||||
{ field: "value", label: "Value", sortable: true },
|
||||
]
|
||||
|
||||
const handleCloseNewSingleObjectiveStudyDialog = () => {
|
||||
setNewStudyName("")
|
||||
setOpenNewSingleObjectiveStudyDialog(false)
|
||||
}
|
||||
|
||||
const handleCreateNewSingleObjectiveStudy = () => {
|
||||
const direction = maximize ? "maximize" : "minimize"
|
||||
action.createNewStudy(newStudyName, [direction])
|
||||
setOpenNewSingleObjectiveStudyDialog(false)
|
||||
setNewStudyName("")
|
||||
}
|
||||
|
||||
const handleCloseNewMultiObjectiveStudyDialog = () => {
|
||||
setOpenNewMultiObjectiveStudyDialog(false)
|
||||
setNewStudyName("")
|
||||
setDirections(["minimize"])
|
||||
}
|
||||
|
||||
const handleCreateNewMultiObjectiveStudy = () => {
|
||||
action.createNewStudy(newStudyName, directions)
|
||||
setOpenNewMultiObjectiveStudyDialog(false)
|
||||
setNewStudyName("")
|
||||
setDirections(["minimize"])
|
||||
}
|
||||
|
||||
const handleCloseDeleteStudyDialog = () => {
|
||||
setOpenDeleteStudyDialog(false)
|
||||
setDeleteStudyID(-1)
|
||||
}
|
||||
|
||||
const handleDeleteStudy = () => {
|
||||
action.deleteStudy(deleteStudyID)
|
||||
setOpenDeleteStudyDialog(false)
|
||||
setDeleteStudyID(-1)
|
||||
}
|
||||
|
||||
const collapseBody = (index: number) => {
|
||||
return (
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={6}>
|
||||
<Box margin={1}>
|
||||
<Typography variant="h6" gutterBottom component="div">
|
||||
Study user attributes
|
||||
</Typography>
|
||||
<DataGrid<Attribute>
|
||||
columns={collapseAttrColumns}
|
||||
rows={studies[index].user_attrs}
|
||||
keyField={"key"}
|
||||
dense={true}
|
||||
initialRowsPerPage={5}
|
||||
rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Box margin={1}>
|
||||
<Typography variant="h6" gutterBottom component="div">
|
||||
Study system attributes
|
||||
</Typography>
|
||||
<DataGrid<Attribute>
|
||||
columns={collapseAttrColumns}
|
||||
rows={studies[index].system_attrs}
|
||||
keyField={"key"}
|
||||
dense={true}
|
||||
initialRowsPerPage={5}
|
||||
rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
const Wrapper = styled("div")(({ theme }) => ({
|
||||
position: "relative",
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
@@ -293,417 +140,59 @@ export const StudyListBeta: FC<{
|
||||
startIcon={<AddBoxIcon />}
|
||||
aria-haspopup="true"
|
||||
onClick={(e) => {
|
||||
setNewStudySelectionAnchorEl(e.currentTarget)
|
||||
openCreateStudyDialog()
|
||||
}}
|
||||
sx={{ marginRight: theme.spacing(2) }}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
<Menu
|
||||
id="menu-appbar"
|
||||
anchorEl={newStudySelectionAnchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
open={openNewStudySelection}
|
||||
onClose={() => {
|
||||
setNewStudySelectionAnchorEl(null)
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Refresh />}
|
||||
aria-haspopup="true"
|
||||
onClick={(e) => {
|
||||
action.updateStudySummaries("Success to reload")
|
||||
}}
|
||||
sx={{ marginRight: theme.spacing(2) }}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
setNewStudySelectionAnchorEl(null)
|
||||
setOpenNewSingleObjectiveStudyDialog(true)
|
||||
}}
|
||||
>
|
||||
Single-objective
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
setNewStudySelectionAnchorEl(null)
|
||||
setOpenNewMultiObjectiveStudyDialog(true)
|
||||
}}
|
||||
>
|
||||
Multi-objective
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
Refresh
|
||||
</Button>
|
||||
{sortBySelect}
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<DataGrid<StudySummary>
|
||||
columns={columns}
|
||||
rows={studies}
|
||||
keyField={"study_id"}
|
||||
collapseBody={collapseBody}
|
||||
initialRowsPerPage={10}
|
||||
rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
|
||||
defaultFilter={studyFilter}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
<Dialog
|
||||
open={openNewSingleObjectiveStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseNewSingleObjectiveStudyDialog()
|
||||
}}
|
||||
aria-labelledby="create-single-objective-study-form-dialog-title"
|
||||
>
|
||||
<DialogTitle id="create-single-objective-study-form-dialog-title">
|
||||
New single-objective study
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
To create a new study, please enter the study name here.
|
||||
</DialogContentText>
|
||||
<DebouncedInputTextField
|
||||
onChange={(s) => {
|
||||
setNewStudyName(s)
|
||||
}}
|
||||
delay={500}
|
||||
textFieldProps={{
|
||||
type: "text",
|
||||
autoFocus: true,
|
||||
fullWidth: true,
|
||||
error: newStudyNameAlreadyUsed,
|
||||
helperText: newStudyNameAlreadyUsed
|
||||
? `"${newStudyName}" is already used`
|
||||
: "",
|
||||
label: "Study name",
|
||||
}}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={maximize}
|
||||
onChange={() => {
|
||||
setMaximize(!maximize)
|
||||
}}
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Set maximize direction (default: minimize)"
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={handleCloseNewSingleObjectiveStudyDialog}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCreateNewSingleObjectiveStudy}
|
||||
color="primary"
|
||||
disabled={newStudyName === "" || newStudyNameAlreadyUsed}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Dialog
|
||||
open={openNewMultiObjectiveStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseNewMultiObjectiveStudyDialog()
|
||||
}}
|
||||
aria-labelledby="create-multi-objective-study-form-dialog-title"
|
||||
>
|
||||
<DialogTitle id="create-multi-objective-study-form-dialog-title">
|
||||
New multi-objective study
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
To create a new study, please enter the study name and directions
|
||||
here.
|
||||
</DialogContentText>
|
||||
<DebouncedInputTextField
|
||||
onChange={(s) => {
|
||||
setNewStudyName(s)
|
||||
}}
|
||||
delay={500}
|
||||
textFieldProps={{
|
||||
autoFocus: true,
|
||||
fullWidth: true,
|
||||
error: newStudyNameAlreadyUsed,
|
||||
helperText: newStudyNameAlreadyUsed
|
||||
? `"${newStudyName}" is already used`
|
||||
: "",
|
||||
label: "Study name",
|
||||
type: "text",
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
{directions.map((d, i) => (
|
||||
<DialogContent key={i}>
|
||||
<FormControl component="fieldset" fullWidth={true}>
|
||||
<FormLabel component="legend">Objective {i}:</FormLabel>
|
||||
<Select
|
||||
value={directions[i]}
|
||||
onChange={(e) => {
|
||||
const newVal: StudyDirection[] = [...directions]
|
||||
newVal[i] = e.target.value as StudyDirection
|
||||
setDirections(newVal)
|
||||
{filteredStudy.map((study) => (
|
||||
<Card key={study.study_id} sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent sx={{ margin: theme.spacing(2) }}>
|
||||
<Typography variant="h5">
|
||||
<Link
|
||||
to={`${URL_PREFIX}/studies/${study.study_id}/beta`}
|
||||
style={{ color: linkColor }}
|
||||
>
|
||||
{study.study_name}
|
||||
</Link>
|
||||
</Typography>
|
||||
<Typography>{study.study_id}</Typography>
|
||||
<Typography>
|
||||
{study.directions.map((d) => d.toString()).join(" ")}
|
||||
</Typography>
|
||||
<IconButton
|
||||
aria-label="delete study"
|
||||
size="small"
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
openDeleteStudyDialog(study.study_id)
|
||||
}}
|
||||
>
|
||||
<MenuItem value="minimize">Minimize</MenuItem>
|
||||
<MenuItem value="maximize">Maximize</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</DialogContent>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
<DialogContent>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
sx={{ marginRight: theme.spacing(1) }}
|
||||
onClick={() => {
|
||||
const newVal: StudyDirection[] = [...directions, "minimize"]
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Remove />}
|
||||
sx={{ marginRight: theme.spacing(1) }}
|
||||
disabled={directions.length <= 1}
|
||||
onClick={() => {
|
||||
const newVal: StudyDirection[] = [...directions]
|
||||
newVal.pop()
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={handleCloseNewMultiObjectiveStudyDialog}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCreateNewMultiObjectiveStudy}
|
||||
color="primary"
|
||||
disabled={
|
||||
newStudyName === "" ||
|
||||
newStudyNameAlreadyUsed ||
|
||||
directions.length === 0
|
||||
}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Dialog
|
||||
open={openDeleteStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseDeleteStudyDialog()
|
||||
}}
|
||||
aria-labelledby="delete-study-dialog-title"
|
||||
>
|
||||
<DialogTitle id="delete-study-dialog-title">Delete study</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Are you sure you want to delete a study (id={deleteStudyID})?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCloseDeleteStudyDialog} color="primary">
|
||||
No
|
||||
</Button>
|
||||
<Button onClick={handleDeleteStudy} color="primary">
|
||||
Yes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Container>
|
||||
</AppDrawer>
|
||||
<Dialog
|
||||
open={openNewSingleObjectiveStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseNewSingleObjectiveStudyDialog()
|
||||
}}
|
||||
aria-labelledby="create-single-objective-study-form-dialog-title"
|
||||
>
|
||||
<DialogTitle id="create-single-objective-study-form-dialog-title">
|
||||
New single-objective study
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
To create a new study, please enter the study name here.
|
||||
</DialogContentText>
|
||||
<DebouncedInputTextField
|
||||
onChange={(s) => {
|
||||
setNewStudyName(s)
|
||||
}}
|
||||
delay={500}
|
||||
textFieldProps={{
|
||||
type: "text",
|
||||
autoFocus: true,
|
||||
fullWidth: true,
|
||||
error: newStudyNameAlreadyUsed,
|
||||
helperText: newStudyNameAlreadyUsed
|
||||
? `"${newStudyName}" is already used`
|
||||
: "",
|
||||
label: "Study name",
|
||||
}}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={maximize}
|
||||
onChange={() => {
|
||||
setMaximize(!maximize)
|
||||
}}
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Set maximize direction (default: minimize)"
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={handleCloseNewSingleObjectiveStudyDialog}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCreateNewSingleObjectiveStudy}
|
||||
color="primary"
|
||||
disabled={newStudyName === "" || newStudyNameAlreadyUsed}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Dialog
|
||||
open={openNewMultiObjectiveStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseNewMultiObjectiveStudyDialog()
|
||||
}}
|
||||
aria-labelledby="create-multi-objective-study-form-dialog-title"
|
||||
>
|
||||
<DialogTitle id="create-multi-objective-study-form-dialog-title">
|
||||
New multi-objective study
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
To create a new study, please enter the study name and directions
|
||||
here.
|
||||
</DialogContentText>
|
||||
<DebouncedInputTextField
|
||||
onChange={(s) => {
|
||||
setNewStudyName(s)
|
||||
}}
|
||||
delay={500}
|
||||
textFieldProps={{
|
||||
autoFocus: true,
|
||||
fullWidth: true,
|
||||
error: newStudyNameAlreadyUsed,
|
||||
helperText: newStudyNameAlreadyUsed
|
||||
? `"${newStudyName}" is already used`
|
||||
: "",
|
||||
label: "Study name",
|
||||
type: "text",
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
{directions.map((d, i) => (
|
||||
<DialogContent key={i}>
|
||||
<FormControl component="fieldset" fullWidth={true}>
|
||||
<FormLabel component="legend">Objective {i}:</FormLabel>
|
||||
<Select
|
||||
value={directions[i]}
|
||||
onChange={(e) => {
|
||||
const newVal: StudyDirection[] = [...directions]
|
||||
newVal[i] = e.target.value as StudyDirection
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
<MenuItem value="minimize">Minimize</MenuItem>
|
||||
<MenuItem value="maximize">Maximize</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</DialogContent>
|
||||
))}
|
||||
<DialogContent>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
sx={{ marginRight: theme.spacing(1) }}
|
||||
onClick={() => {
|
||||
const newVal: StudyDirection[] = [...directions, "minimize"]
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Remove />}
|
||||
sx={{ marginRight: theme.spacing(1) }}
|
||||
disabled={directions.length <= 1}
|
||||
onClick={() => {
|
||||
const newVal: StudyDirection[] = [...directions]
|
||||
newVal.pop()
|
||||
setDirections(newVal)
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={handleCloseNewMultiObjectiveStudyDialog}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCreateNewMultiObjectiveStudy}
|
||||
color="primary"
|
||||
disabled={
|
||||
newStudyName === "" ||
|
||||
newStudyNameAlreadyUsed ||
|
||||
directions.length === 0
|
||||
}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Dialog
|
||||
open={openDeleteStudyDialog}
|
||||
onClose={() => {
|
||||
handleCloseDeleteStudyDialog()
|
||||
}}
|
||||
aria-labelledby="delete-study-dialog-title"
|
||||
>
|
||||
<DialogTitle id="delete-study-dialog-title">Delete study</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Are you sure you want to delete a study (id={deleteStudyID})?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCloseDeleteStudyDialog} color="primary">
|
||||
No
|
||||
</Button>
|
||||
<Button onClick={handleDeleteStudy} color="primary">
|
||||
Yes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
{renderCreateStudyDialog()}
|
||||
{renderDeleteStudyDialog()}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user