Skip to content

Commit

Permalink
Adding Confirm Modal and Cancel Job Utility (#347)
Browse files Browse the repository at this point in the history
* Added confirm modal

* Added cancel job utility

* Fixed bug that led to web crash from bad URL job UUID req

* Changed error message

* Ran prettier and linting

* Switched confirm button to be on the left of cancel button in the ConfirmModal
  • Loading branch information
jthet authored Feb 1, 2024
1 parent 8c87c9d commit 54a5bd9
Show file tree
Hide file tree
Showing 16 changed files with 276 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"react-query": "^3.19.2",
"react-resize-detector": "^6.1.0",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-scripts": "^4.0.3",
"react-step-wizard": "^5.3.9",
"react-table": "^7.0.5",
"react-transition-group": "^4.4.1",
Expand Down
18 changes: 18 additions & 0 deletions src/tapis-api/jobs/cancel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Jobs } from '@tapis/tapis-typescript';
import { apiGenerator, errorDecoder } from 'tapis-api/utils';

export const cancel = (
jobCancelReq: Jobs.CancelJobRequest,
basePath: string,
jwt: string
) => {
const api: Jobs.JobsApi = apiGenerator<Jobs.JobsApi>(
Jobs,
Jobs.JobsApi,
basePath,
jwt
);
return errorDecoder<Jobs.RespCancelJob>(() => api.cancelJob(jobCancelReq));
};

export default cancel;
1 change: 1 addition & 0 deletions src/tapis-api/jobs/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as list } from './list';
export { default as details } from './details';
export { default as submit } from './submit';
export { default as cancel } from './cancel';
23 changes: 22 additions & 1 deletion src/tapis-app/Jobs/JobDetail/_Layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
import React from 'react';
import { JobDetail } from 'tapis-ui/components/jobs';
import { PageLayout, LayoutHeader } from 'tapis-ui/_common';
import { JobsToolbar } from 'tapis-app/Jobs/_components';
import { useDetails } from 'tapis-hooks/jobs';

interface JobDetailProps {
jobUuid: string;
}

const Layout: React.FC<JobDetailProps> = ({ jobUuid }) => {
const header = <LayoutHeader type={'sub-header'}>Job Details</LayoutHeader>;
// eslint-disable-next-line
const { data, isLoading, error } = useDetails(jobUuid);

// Don't need the below because error message is already shown, as an error not as plain text like below would do
// if (error) {
// return <div>Error: {error.message}</div>;
// }

// Use this block below to hide error message and only show in console
// if (error) {
// console.error(error); // Log the error for debugging
// return <div>Something went wrong. Please try again later.</div>;
// }

const header = (
<LayoutHeader type={'sub-header'}>
Job Details
{data && <JobsToolbar jobUuid={jobUuid} />}
</LayoutHeader>
);

const body = (
<div style={{ flex: 1 }}>
Expand Down
20 changes: 20 additions & 0 deletions src/tapis-app/Jobs/_components/JobsToolbar/JobsToolbar.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.toolbar-wrapper {
padding: 0;
margin: 0;
margin-top: 0.5em;
display: flex !important;
}

.toolbar-btn {
height: 2rem;
align-items: center;
margin-left: 0.5em;
font-size: 0.7em !important;
border-radius: 0 !important;
background-color: #f4f4f4 !important;
color: #333333 !important;
}

.toolbar-btn:disabled {
color: #999999 !important;
}
83 changes: 83 additions & 0 deletions src/tapis-app/Jobs/_components/JobsToolbar/JobsToolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useState } from 'react';
import { Button } from 'reactstrap';
import { Icon } from 'tapis-ui/_common';
import styles from './JobsToolbar.module.scss';
import { useLocation } from 'react-router-dom';
import ConfirmModal from 'tapis-ui/_common/ConfirmModal';
import { useCancel } from 'tapis-hooks/jobs';

type ToolbarButtonProps = {
text: string;
icon: string;
onClick: () => void;
disabled: boolean;
};

export type ToolbarModalProps = {
toggle: () => void;
};

export const ToolbarButton: React.FC<ToolbarButtonProps> = ({
text,
icon,
onClick,
disabled = true,
...rest
}) => {
return (
<div>
<Button
disabled={disabled}
onClick={onClick}
className={styles['toolbar-btn']}
{...rest}
>
<Icon name={icon}></Icon>
<span> {text}</span>
</Button>
</div>
);
};

const JobsToolbar: React.FC<{ jobUuid: string }> = ({ jobUuid }) => {
const [modal, setModal] = useState<string | undefined>(undefined);
const { pathname } = useLocation();
const { isLoading, isError, isSuccess, error, cancel } = useCancel();

const toggle = () => {
setModal(undefined);
};

return (
<div id="file-operation-toolbar">
{pathname && (
<div className={styles['toolbar-wrapper']}>
<ToolbarButton
text="Cancel Job"
icon="trash"
disabled={false}
onClick={() => setModal('ConfirmModal')}
aria-label="createSystem"
/>

{modal === 'ConfirmModal' && (
<ConfirmModal
toggle={toggle}
onConfirm={() => {
cancel({ jobUuid });
}}
isLoading={isLoading}
isError={isError}
isSuccess={isSuccess}
error={error}
/>
)}
</div>
)}
</div>
);
};

export default JobsToolbar;

// JobUuid = pathname.split("/")[2]
3 changes: 3 additions & 0 deletions src/tapis-app/Jobs/_components/JobsToolbar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import JobsToolbar from './JobsToolbar';

export default JobsToolbar;
1 change: 1 addition & 0 deletions src/tapis-app/Jobs/_components/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as JobsNav } from './JobsNav';
export { default as JobsToolbar } from './JobsToolbar';
1 change: 1 addition & 0 deletions src/tapis-hooks/jobs/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as useList } from './useList';
export { default as useDetails } from './useDetails';
export { default as useSubmit } from './useSubmit';
export { default as useCancel } from './useCancel';
1 change: 1 addition & 0 deletions src/tapis-hooks/jobs/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const QueryKeys = {
list: 'jobs/list',
details: 'jobs/details',
submit: 'jobs/submit',
cancel: 'jobs/cancel',
};

export default QueryKeys;
52 changes: 52 additions & 0 deletions src/tapis-hooks/jobs/useCancel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useMutation, MutateOptions } from 'react-query';
import { Jobs } from '@tapis/tapis-typescript';
import { cancel } from 'tapis-api/jobs';
import { useTapisConfig } from 'tapis-hooks';
import QueryKeys from './queryKeys';

const useCancel = () => {
const { basePath, accessToken } = useTapisConfig();
const jwt = accessToken?.access_token || '';

// The useMutation react-query hook is used to call operations that make server-side changes
// (Other hooks would be used for data retrieval)
//
// In this case, move helper is called to perform the operation
const {
mutate,
mutateAsync,
isLoading,
isError,
isSuccess,
data,
error,
reset,
} = useMutation<Jobs.RespCancelJob, Error, Jobs.CancelJobRequest>(
[QueryKeys.cancel, basePath, jwt],
(jobCancelReq) => cancel(jobCancelReq, basePath, jwt)
);

// Return hook object with loading states and login function
return {
isLoading,
isError,
isSuccess,
data,
error,
reset,
cancel: (
jobCancelReq: Jobs.CancelJobRequest,
// react-query options to allow callbacks such as onSuccess
options?: MutateOptions<Jobs.RespCancelJob, Error, Jobs.CancelJobRequest>
) => {
// Call mutate to trigger a single post-like API operation
return mutate(jobCancelReq, options);
},
cancelAsync: (
jobCancelReq: Jobs.CancelJobRequest,
options?: MutateOptions<Jobs.RespCancelJob, Error, Jobs.CancelJobRequest>
) => mutateAsync(jobCancelReq, options),
};
};

export default useCancel;
62 changes: 62 additions & 0 deletions src/tapis-ui/_common/ConfirmModal/ConfirmModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import { Button } from 'reactstrap';
import { GenericModal } from 'tapis-ui/_common';
import { SubmitWrapper } from 'tapis-ui/_wrappers';

type ConfirmModalProps = {
toggle: () => void;
message?: string;
onConfirm: () => void;
isLoading: boolean;
isSuccess: boolean;
isError: boolean;
error: Error | null;
};

const ConfirmModal: React.FC<ConfirmModalProps> = ({
toggle,
message,
onConfirm,
isLoading,
isSuccess,
isError,
error,
}) => {
return (
<GenericModal
toggle={toggle}
title="Confirm"
body={message || 'Are you sure you want to continue?'}
footer={
<SubmitWrapper
className={''}
isLoading={isLoading}
error={error}
success={isSuccess ? `Success` : ''}
reverse={true}
>
<Button
form="newsystem-form"
color="primary"
aria-label="Submit"
type="submit"
onClick={toggle}
>
Cancel
</Button>
<Button
form="newsystem-form"
color="primary"
aria-label="Submit"
type="submit"
onClick={onConfirm}
>
Confirm
</Button>
</SubmitWrapper>
}
/>
);
};

export default ConfirmModal;
3 changes: 3 additions & 0 deletions src/tapis-ui/_common/ConfirmModal/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ConfirmModal from './ConfirmModal';

export default ConfirmModal;
1 change: 1 addition & 0 deletions src/tapis-ui/_common/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { default as Breadcrumbs } from './Breadcrumbs';
export { default as GenericModal } from './GenericModal';
export { default as ConfirmModal } from './ConfirmModal';
export { default as LoadingSpinner } from './LoadingSpinner';
export { default as Section } from './Section';
export { default as SectionHeader } from './SectionHeader';
Expand Down
6 changes: 6 additions & 0 deletions src/tapis-ui/components/jobs/JobDetail/JobDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ import { useDetails } from 'tapis-hooks/jobs';
import { Jobs } from '@tapis/tapis-typescript';
import { DescriptionList } from 'tapis-ui/_common';
import { QueryWrapper } from 'tapis-ui/_wrappers';
import { Link } from 'react-router-dom';

const JobDetail: React.FC<{ jobUuid: string }> = ({ jobUuid }) => {
const { data, isLoading, error } = useDetails(jobUuid);
const job: Jobs.Job | undefined = data?.result;

// console.log(job?.execSystemOutputDir);

return (
<QueryWrapper isLoading={isLoading} error={error}>
<h3>{job?.name}</h3>
<h5>{job?.uuid}</h5>
<Link to={`/files/${job?.execSystemId}${job?.execSystemOutputDir}`}>
See Files
</Link>
{job && <DescriptionList data={job} />}
</QueryWrapper>
);
Expand Down

0 comments on commit 54a5bd9

Please sign in to comment.