From fece9a3c16b8de727ec419f2ffa08e9f80304db5 Mon Sep 17 00:00:00 2001 From: c-bata Date: Sat, 31 Dec 2022 20:20:22 +0900 Subject: [PATCH] Separate dialog components --- .../ts/components/CreateStudyDialog.tsx | 148 +++++ .../ts/components/DeleteStudyDialog.tsx | 63 ++ optuna_dashboard/ts/components/StudyList.tsx | 305 +-------- .../ts/components/StudyListBeta.tsx | 605 ++---------------- 4 files changed, 269 insertions(+), 852 deletions(-) create mode 100644 optuna_dashboard/ts/components/CreateStudyDialog.tsx create mode 100644 optuna_dashboard/ts/components/DeleteStudyDialog.tsx diff --git a/optuna_dashboard/ts/components/CreateStudyDialog.tsx b/optuna_dashboard/ts/components/CreateStudyDialog.tsx new file mode 100644 index 00000000..ef1e9ec3 --- /dev/null +++ b/optuna_dashboard/ts/components/CreateStudyDialog.tsx @@ -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(["minimize"]) + const studies = useRecoilValue(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 ( + { + handleCloseNewStudyDialog() + }} + aria-labelledby="create-study-dialog-title" + > + New Study + + + Please enter the study name and directions here. + + { + setNewStudyName(s) + }} + delay={500} + textFieldProps={{ + autoFocus: true, + fullWidth: true, + error: newStudyNameAlreadyUsed, + helperText: newStudyNameAlreadyUsed + ? `"${newStudyName}" is already used` + : "", + label: "Study name", + type: "text", + }} + /> + + {directions.map((d, i) => ( + + + Objective {i}: + + + + ))} + + + + + + + + + + ) + } + return [openDialog, renderCreateNewStudyDialog] +} diff --git a/optuna_dashboard/ts/components/DeleteStudyDialog.tsx b/optuna_dashboard/ts/components/DeleteStudyDialog.tsx new file mode 100644 index 00000000..1d8d1705 --- /dev/null +++ b/optuna_dashboard/ts/components/DeleteStudyDialog.tsx @@ -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 ( + { + handleCloseDeleteStudyDialog() + }} + aria-labelledby="delete-study-dialog-title" + > + Delete study + + + Are you sure you want to delete a study (id={deleteStudyID})? + + + + + + + + ) + } + return [openDialog, renderCreateNewStudyDialog] +} diff --git a/optuna_dashboard/ts/components/StudyList.tsx b/optuna_dashboard/ts/components/StudyList.tsx index f752a09e..11c2696e 100644 --- a/optuna_dashboard/ts/components/StudyList.tsx +++ b/optuna_dashboard/ts/components/StudyList.tsx @@ -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) - 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("") 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([ - "minimize", - ]) const linkColor = useMemo( () => theme.palette.mode === "dark" @@ -92,10 +59,6 @@ export const StudyList: FC<{ const action = actionCreator() const studies = useRecoilValue(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) }} > @@ -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 ( @@ -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" > - { - setNewStudySelectionAnchorEl(null) - }} - > - { - setNewStudySelectionAnchorEl(null) - setOpenNewSingleObjectiveStudyDialog(true) - }} - > - Single-objective - - { - setNewStudySelectionAnchorEl(null) - setOpenNewMultiObjectiveStudyDialog(true) - }} - > - Multi-objective - - @@ -383,183 +275,8 @@ export const StudyList: FC<{ /> - { - handleCloseNewSingleObjectiveStudyDialog() - }} - aria-labelledby="create-single-objective-study-form-dialog-title" - > - - New single-objective study - - - - To create a new study, please enter the study name here. - - { - setNewStudyName(s) - }} - delay={500} - textFieldProps={{ - type: "text", - autoFocus: true, - fullWidth: true, - error: newStudyNameAlreadyUsed, - helperText: newStudyNameAlreadyUsed - ? `"${newStudyName}" is already used` - : "", - label: "Study name", - }} - /> - { - setMaximize(!maximize) - }} - color="primary" - /> - } - label="Set maximize direction (default: minimize)" - /> - - - - - - - { - handleCloseNewMultiObjectiveStudyDialog() - }} - aria-labelledby="create-multi-objective-study-form-dialog-title" - > - - New multi-objective study - - - - To create a new study, please enter the study name and directions - here. - - { - setNewStudyName(s) - }} - delay={500} - textFieldProps={{ - autoFocus: true, - fullWidth: true, - error: newStudyNameAlreadyUsed, - helperText: newStudyNameAlreadyUsed - ? `"${newStudyName}" is already used` - : "", - label: "Study name", - type: "text", - }} - /> - - {directions.map((d, i) => ( - - - Objective {i}: - - - - ))} - - - - - - - - - - { - handleCloseDeleteStudyDialog() - }} - aria-labelledby="delete-study-dialog-title" - > - Delete study - - - Are you sure you want to delete a study (id={deleteStudyID})? - - - - - - - + {renderCreateStudyDialog()} + {renderDeleteStudyDialog()} ) } diff --git a/optuna_dashboard/ts/components/StudyListBeta.tsx b/optuna_dashboard/ts/components/StudyListBeta.tsx index 129d5f36..de462ed1 100644 --- a/optuna_dashboard/ts/components/StudyListBeta.tsx +++ b/optuna_dashboard/ts/components/StudyListBeta.tsx @@ -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) - 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("") 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([ - "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(studySummariesState) - - const newStudyNameAlreadyUsed = studies.some( - (v) => v.study_name === newStudyName - ) + const filteredStudy = studies.filter((s) => !studyFilter(s)) useEffect(() => { action.updateStudySummaries() }, []) - const columns: DataGridColumn[] = [ - { - field: "study_id", - label: "Study ID", - sortable: true, - }, - { - field: "study_name", - label: "Name", - sortable: true, - toCellValue: (i) => ( - - {studies[i].study_name} - - ), - }, - { - field: "directions", - label: "Direction", - sortable: false, - toCellValue: (i) => studies[i].directions.join(), - }, - { - field: "study_name", - label: "", - sortable: false, - padding: "none", - toCellValue: (i) => ( - { - setDeleteStudyID(studies[i].study_id) - setOpenDeleteStudyDialog(true) - }} - > - - - ), - }, - ] - - const collapseAttrColumns: DataGridColumn[] = [ - { 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 ( - - - - - Study user attributes - - - columns={collapseAttrColumns} - rows={studies[index].user_attrs} - keyField={"key"} - dense={true} - initialRowsPerPage={5} - rowsPerPageOption={[5, 10, { label: "All", value: -1 }]} - /> - - - - - - Study system attributes - - - columns={collapseAttrColumns} - rows={studies[index].system_attrs} - keyField={"key"} - dense={true} - initialRowsPerPage={5} - rowsPerPageOption={[5, 10, { label: "All", value: -1 }]} - /> - - - - ) - } - const Wrapper = styled("div")(({ theme }) => ({ position: "relative", borderRadius: theme.shape.borderRadius, @@ -293,417 +140,59 @@ export const StudyListBeta: FC<{ startIcon={} aria-haspopup="true" onClick={(e) => { - setNewStudySelectionAnchorEl(e.currentTarget) + openCreateStudyDialog() }} sx={{ marginRight: theme.spacing(2) }} > Create - { - setNewStudySelectionAnchorEl(null) + + Refresh + {sortBySelect} - - - columns={columns} - rows={studies} - keyField={"study_id"} - collapseBody={collapseBody} - initialRowsPerPage={10} - rowsPerPageOption={[5, 10, { label: "All", value: -1 }]} - defaultFilter={studyFilter} - /> - - - { - handleCloseNewSingleObjectiveStudyDialog() - }} - aria-labelledby="create-single-objective-study-form-dialog-title" - > - - New single-objective study - - - - To create a new study, please enter the study name here. - - { - setNewStudyName(s) - }} - delay={500} - textFieldProps={{ - type: "text", - autoFocus: true, - fullWidth: true, - error: newStudyNameAlreadyUsed, - helperText: newStudyNameAlreadyUsed - ? `"${newStudyName}" is already used` - : "", - label: "Study name", - }} - /> - { - setMaximize(!maximize) - }} - color="primary" - /> - } - label="Set maximize direction (default: minimize)" - /> - - - - - - - { - handleCloseNewMultiObjectiveStudyDialog() - }} - aria-labelledby="create-multi-objective-study-form-dialog-title" - > - - New multi-objective study - - - - To create a new study, please enter the study name and directions - here. - - { - setNewStudyName(s) - }} - delay={500} - textFieldProps={{ - autoFocus: true, - fullWidth: true, - error: newStudyNameAlreadyUsed, - helperText: newStudyNameAlreadyUsed - ? `"${newStudyName}" is already used` - : "", - label: "Study name", - type: "text", - }} - /> - - {directions.map((d, i) => ( - - - Objective {i}: - - - + + + + ))} - - - - - - - - - - { - handleCloseDeleteStudyDialog() - }} - aria-labelledby="delete-study-dialog-title" - > - Delete study - - - Are you sure you want to delete a study (id={deleteStudyID})? - - - - - - - + - { - handleCloseNewSingleObjectiveStudyDialog() - }} - aria-labelledby="create-single-objective-study-form-dialog-title" - > - - New single-objective study - - - - To create a new study, please enter the study name here. - - { - setNewStudyName(s) - }} - delay={500} - textFieldProps={{ - type: "text", - autoFocus: true, - fullWidth: true, - error: newStudyNameAlreadyUsed, - helperText: newStudyNameAlreadyUsed - ? `"${newStudyName}" is already used` - : "", - label: "Study name", - }} - /> - { - setMaximize(!maximize) - }} - color="primary" - /> - } - label="Set maximize direction (default: minimize)" - /> - - - - - - - { - handleCloseNewMultiObjectiveStudyDialog() - }} - aria-labelledby="create-multi-objective-study-form-dialog-title" - > - - New multi-objective study - - - - To create a new study, please enter the study name and directions - here. - - { - setNewStudyName(s) - }} - delay={500} - textFieldProps={{ - autoFocus: true, - fullWidth: true, - error: newStudyNameAlreadyUsed, - helperText: newStudyNameAlreadyUsed - ? `"${newStudyName}" is already used` - : "", - label: "Study name", - type: "text", - }} - /> - - {directions.map((d, i) => ( - - - Objective {i}: - - - - ))} - - - - - - - - - - { - handleCloseDeleteStudyDialog() - }} - aria-labelledby="delete-study-dialog-title" - > - Delete study - - - Are you sure you want to delete a study (id={deleteStudyID})? - - - - - - - + {renderCreateStudyDialog()} + {renderDeleteStudyDialog()} ) }