Skip to content

Commit

Permalink
feat(ui-study): add button to display display 'digest' file on succes…
Browse files Browse the repository at this point in the history
…sful tasks in task list (#2101)
  • Loading branch information
skamril authored Jul 23, 2024
2 parents 6ef9d36 + 2f0ae26 commit 2aaa00c
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Skeleton } from "@mui/material";
import OkDialog, {
OkDialogProps,
} from "../../../../../common/dialogs/OkDialog";
import EditableMatrix from "../../../../../common/EditableMatrix";
import UsePromiseCond from "../../../../../common/utils/UsePromiseCond";
import type { LaunchJob } from "../../../../../../common/types";
import { getStudyData } from "../../../../../../services/api/study";
import usePromise from "../../../../../../hooks/usePromise";
import { useTranslation } from "react-i18next";

interface Props extends Pick<OkDialogProps, "open" | "onOk" | "onClose"> {
studyId: LaunchJob["studyId"];
outputId: LaunchJob["outputId"];
}

function DigestDialog({ studyId, outputId, ...dialogProps }: Props) {
const { t } = useTranslation();

const synthesisRes = usePromise(
() =>
getStudyData(studyId, `output/${outputId}/economy/mc-all/grid/digest`),
{
deps: [studyId, outputId],
},
);

return (
<OkDialog
{...dialogProps}
title="Digest"
okButtonText={t("global.close")}
fullScreen
sx={{ m: 5 }}
>
<UsePromiseCond
response={synthesisRes}
ifPending={() => <Skeleton sx={{ height: 1, transform: "none" }} />}
ifResolved={(matrix) =>
matrix && (
<EditableMatrix
matrix={matrix}
columnsNames={matrix.columns}
matrixTime={false}
readOnly
/>
)
}
/>
</OkDialog>
);
}

export default DigestDialog;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import StepLabel from "@mui/material/StepLabel";
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
import BlockIcon from "@mui/icons-material/Block";
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
import EqualizerIcon from "@mui/icons-material/Equalizer";
import { StepIconProps, Tooltip, Typography } from "@mui/material";
import moment from "moment";
import { useState } from "react";
Expand All @@ -29,6 +30,8 @@ import {
} from "./style";
import ConfirmationDialog from "../../../../../common/dialogs/ConfirmationDialog";
import LinearProgressWithLabel from "../../../../../common/LinearProgressWithLabel";
import DigestDialog from "./DigestDialog";
import type { EmptyObject } from "../../../../../../utils/tsUtils";

export const ColorStatus = {

Check warning on line 36 in webapp/src/components/App/Singlestudy/HomeView/InformationView/LauncherHistory/JobStepper.tsx

View workflow job for this annotation

GitHub Actions / npm-test (ubuntu-20.04)

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
running: "warning.main",
Expand All @@ -37,6 +40,15 @@ export const ColorStatus = {
failed: "error.main",
};

const iconStyle = {
m: 0.5,
height: 22,
cursor: "pointer",
"&:hover": {
color: "action.hover",
},
};

function QontoStepIcon(props: {
className: string | undefined;
status: JobStatus;
Expand All @@ -55,6 +67,13 @@ function QontoStepIcon(props: {
);
}

type DialogState =
| {
type: "killJob" | "digest";
job: LaunchJob;
}
| EmptyObject;

interface Props {
jobs: LaunchJob[];
jobsProgress: LaunchJobsProgress;
Expand All @@ -65,29 +84,32 @@ export default function VerticalLinearStepper(props: Props) {
const [t] = useTranslation();
const { enqueueSnackbar } = useSnackbar();
const enqueueErrorSnackbar = useEnqueueErrorSnackbar();
const [openConfirmationDialog, setOpenConfirmationDialog] =
useState<boolean>(false);
const [jobIdKill, setJobIdKill] = useState<string>();
const [dialogState, setDialogState] = useState<DialogState>({});

const openConfirmModal = (jobId: string) => {
setOpenConfirmationDialog(true);
setJobIdKill(jobId);
};
////////////////////////////////////////////////////////////////
// Utils
////////////////////////////////////////////////////////////////

const killTask = (jobId: string) => {
(async () => {
try {
await killStudy(jobId);
} catch (e) {
enqueueErrorSnackbar(t("study.failtokilltask"), e as AxiosError);
}
setOpenConfirmationDialog(false);
})();
const closeDialog = () => setDialogState({});

////////////////////////////////////////////////////////////////
// Actions
////////////////////////////////////////////////////////////////

const killTask = async (jobId: LaunchJob["id"]) => {
closeDialog();

try {
await killStudy(jobId);
} catch (e) {
enqueueErrorSnackbar(t("study.failtokilltask"), e as AxiosError);
}
};

const copyId = (jobId: string): void => {
const copyId = (jobId: LaunchJob["id"]) => {
try {
navigator.clipboard.writeText(jobId);

enqueueSnackbar(t("study.success.jobIdCopy"), {
variant: "success",
});
Expand All @@ -96,6 +118,10 @@ export default function VerticalLinearStepper(props: Props) {
}
};

////////////////////////////////////////////////////////////////
// JSX
////////////////////////////////////////////////////////////////

return (
<JobRoot jobLength={jobs.length}>
<Stepper
Expand Down Expand Up @@ -140,17 +166,18 @@ export default function VerticalLinearStepper(props: Props) {
<Tooltip title={t("study.copyJobId") as string}>
<ContentCopyIcon
onClick={() => copyId(job.id)}
sx={{
m: 0.5,
height: "22px",
cursor: "pointer",
"&:hover": {
color: "action.hover",
},
}}
sx={iconStyle}
/>
</Tooltip>
<LaunchJobLogView job={job} logButton logErrorButton />
{job.status === "success" && (
<Tooltip title="Digest">
<EqualizerIcon
onClick={() => setDialogState({ type: "digest", job })}
sx={iconStyle}
/>
</Tooltip>
)}
{job.status === "running" && (
<CancelContainer>
<LinearProgressWithLabel
Expand All @@ -160,11 +187,11 @@ export default function VerticalLinearStepper(props: Props) {
/>
<Tooltip title={t("study.killStudy") as string}>
<BlockIcon
onClick={() => openConfirmModal(job.id)}
onClick={() =>
setDialogState({ type: "killJob", job })
}
sx={{
m: 0.5,
height: "22px",
cursor: "pointer",
...iconStyle,
color: "error.light",
"&:hover": { color: "error.dark" },
}}
Expand All @@ -178,16 +205,24 @@ export default function VerticalLinearStepper(props: Props) {
</Step>
))}
</Stepper>
{openConfirmationDialog && (
{dialogState.type === "killJob" && (
<ConfirmationDialog
onCancel={() => setOpenConfirmationDialog(false)}
onConfirm={() => killTask(jobIdKill as string)}
alert="warning"
open
alert="warning"
onConfirm={() => killTask(dialogState.job.id)}
onCancel={closeDialog}
>
{t("study.question.killJob")}
</ConfirmationDialog>
)}
{dialogState.type === "digest" && (
<DigestDialog
open
studyId={dialogState.job.studyId}
outputId={dialogState.job.outputId}
onOk={closeDialog}
/>
)}
</JobRoot>
);
}
6 changes: 6 additions & 0 deletions webapp/src/utils/tsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import { O } from "ts-toolbelt";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type PromiseAny = Promise<any>;

/**
* Allow to define an empty object.
* Don't use `{}` as a type. `{}` actually means "any non-nullish value".
*/
export type EmptyObject = Record<string, never>;

/**
* Make all properties in T optional, except for those specified by K.
*/
Expand Down

0 comments on commit 2aaa00c

Please sign in to comment.