Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRDCDH-539 Initiate File Validations #217

Merged
merged 17 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/components/DataSubmissions/DataSubmissionUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const StyledMetadataText = styled(StyledUploadTypeText)(() => ({
const StyledUploadFilesButton = styled(LoadingButton)(() => ({
display: "flex",
flexDirection: "column",
padding: "12px 22px",
padding: "12px 20px",
justifyContent: "center",
alignItems: "center",
borderRadius: "8px",
Expand All @@ -46,7 +46,8 @@ const StyledUploadFilesButton = styled(LoadingButton)(() => ({
textTransform: "none",
"&.MuiButtonBase-root": {
marginLeft: "auto",
marginRight: "21.5px"
marginRight: "21.5px",
minWidth: "137px",
}
}));

Expand Down Expand Up @@ -336,7 +337,7 @@ const DataSubmissionUpload = ({ submitterID, readOnly, onUpload }: Props) => {
disableRipple
disableTouchRipple
>
Upload Files
Upload
</StyledUploadFilesButton>
</StyledUploadWrapper>
);
Expand Down
55 changes: 2 additions & 53 deletions src/components/DataSubmissions/RadioInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ import {
Grid,
FormControl,
FormControlLabel,
Radio,
RadioGroup,
RadioProps,
RadioGroupProps,
FormHelperText,
Stack,
styled,
GridProps,
} from "@mui/material";
import { updateInputValidity } from "../../utils";
import StyledRadioButton from '../Questionnaire/StyledRadioButton';

const GridStyled = styled(Grid, {
shouldForwardProp: (prop) => prop !== "containerWidth",
Expand Down Expand Up @@ -47,31 +46,6 @@ const GridStyled = styled(Grid, {
},
}));

const BpIcon = styled("span")(() => ({
borderRadius: "50%",
width: 24,
height: 24,
outline: "6px auto #1D91AB",
"input:hover ~ &": {
outlineOffset: "2px",
},
}));

const BpCheckedIcon = styled(BpIcon)({
backgroundImage:
"linear-gradient(180deg,hsla(0,0%,100%,.1),hsla(0,0%,100%,0))",
"&:before": {
borderRadius: "50%",
display: "block",
marginTop: "4px",
marginLeft: "4px",
width: 16,
height: 16,
backgroundImage: "radial-gradient(#1D91AB, #1D91AB)",
content: '""',
},
});

const StyledFormLabel = styled("label")(() => ({
fontWeight: 700,
fontSize: "16px",
Expand All @@ -85,15 +59,6 @@ const StyledAsterisk = styled("span")(() => ({
color: "#D54309",
}));

const StyledRadio = styled(Radio)((props) => ({
"& input": {
cursor: props.readOnly ? "not-allowed" : "pointer",
},
"& .radio-icon": {
backgroundColor: props.readOnly ? "#D2DFE9 !important" : "initial",
},
}));

const StyledFormControlLabel = styled(FormControlLabel)(() => ({
"&.MuiFormControlLabel-root": {
paddingRight: "10px",
Expand All @@ -109,22 +74,6 @@ const StyledFormControlLabel = styled(FormControlLabel)(() => ({
},
}));

// Inspired by blueprintjs
const BpRadio = (props: RadioProps) => (
<StyledRadio
disableRipple
color="default"
checkedIcon={<BpCheckedIcon className="radio-icon" />}
icon={<BpIcon className="radio-icon" />}
inputProps={
{
"data-type": "auto",
} as unknown
}
{...props}
/>
);

type Option = {
label: string;
value: string;
Expand Down Expand Up @@ -231,7 +180,7 @@ const RadioYesNoInput: FC<Props> = ({
label={option.label}
color="#1D91AB"
control={(
<BpRadio
<StyledRadioButton
id={id.concat(`-${option.label}-radio-button`)}
readOnly={readOnly || option.disabled}
disabled={option.disabled}
Expand Down
250 changes: 250 additions & 0 deletions src/components/DataSubmissions/ValidationControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
import { FC, useMemo, useState } from 'react';
import { useMutation } from '@apollo/client';
import { FormControlLabel, RadioGroup, styled } from '@mui/material';
import { LoadingButton } from '@mui/lab';
import { useAuthContext } from '../Contexts/AuthContext';
import StyledRadioButton from "../Questionnaire/StyledRadioButton";
import { VALIDATE_SUBMISSION, ValidateSubmissionResp } from '../../graphql';
import GenericAlert, { AlertState } from '../GenericAlert';

type Props = {
/**
* The data submission to display validation controls for.
*
* NOTE: Initially null during the loading state.
*/
dataSubmission?: Submission;
};

type ValidationType = "Metadata" | "Files" | "All";

type UploadType = "New" | "All";

const StyledValidateButton = styled(LoadingButton)({
alignSelf: "center",
display: "flex",
flexDirection: "column",
padding: "12px 20px",
justifyContent: "center",
alignItems: "center",
borderRadius: "8px",
background: "#1A7B90",
color: "#FFF",
textAlign: "center",
fontFamily: "'Nunito'",
fontSize: "16px",
fontStyle: "normal",
fontWeight: 700,
lineHeight: "16px",
letterSpacing: "0.32px",
textTransform: "none",
border: "1.5px solid #136071",
"&.MuiButtonBase-root": {
height: "fit-content",
marginLeft: "auto",
marginRight: "21.5px",
minWidth: "137px",
},
"&.MuiButtonBase-root:disabled": {
height: "fit-content",
marginLeft: "auto",
marginRight: "21.5px",
minWidth: "137px",
background: "#949494",
color: "#CBCBCB",
},
"&.MuiButtonBase-root:hover": {
background: "#496065",
height: "fit-content",
marginLeft: "auto",
marginRight: "21.5px",
minWidth: "137px",
}
});

const StyledFileValidationSection = styled("div")({
borderRadius: 0,
minHeight: "147px",
padding: "21px 40px 0",
borderTop: "solid 1.5px #6CACDA",
background: "#F0FBFD",
gridAutoFlow: "row",
gridTemplateColumns: "2.5fr 0.5fr",
display: "grid",
".headerText": {
fontFamily: "Nunito",
color: "#083A50",
fontSize: "16px",
fontWeight: "700",
lineHeight: "20px",
letterSpacing: "0em",
textAlign: "left",
minWidth: "270px"
},
".fileValidationLeftSide": {
display: "flex",
flexDirection: "column",
},
".fileValidationLeftSideTopRow": {
display: "grid",
gridTemplateColumns: "1fr 3fr",
height: "50px",
alignItems: "center",
borderBottom: "1px solid #0B7F99",
width: "800px",
},
".fileValidationLeftSideBottomRow": {
display: "grid",
gridTemplateColumns: "1fr 3fr",
height: "50px",
alignItems: "center",
width: "800px",
},
".fileValidationRadioButtonGroup": {
marginLeft: "20px",
},
});

const StyledRadioControl = styled(FormControlLabel)({
fontFamily: "Nunito",
fontSize: "16px",
fontWeight: "500",
lineHeight: "20px",
letterSpacing: "0em",
textAlign: "left",
color: "#083A50",
minWidth: "200px",
marginRight: "20px",
"&:last-child": {
marginRight: "0px",
minWidth: "unset",
},
});

const ValidateRoles: User["role"][] = ["Submitter", "Data Curator", "Organization Owner", "Admin"];
const ValidateStatuses: Submission["status"][] = ["In Progress", "Withdrawn", "Rejected"];

/**
* Provides the UI for validating a data submission's assets.
*
* @param {Props}
* @returns {React.FC<Props>}
*/
const ValidationControls: FC<Props> = ({ dataSubmission }: Props) => {
const { user } = useAuthContext();
const [validationType, setValidationType] = useState<ValidationType>("Metadata");
const [uploadType, setUploadType] = useState<UploadType>("New");
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isValidating, setIsValidating] = useState<boolean>(false);
const [validationAlert, setValidationAlert] = useState<AlertState>(null);

const canValidateData: boolean = useMemo(() => ValidateRoles.includes(user?.role), [user?.role]);
const validateButtonEnabled: boolean = useMemo(() => ValidateStatuses.includes(dataSubmission?.status), [dataSubmission?.status]);

const [validateSubmission] = useMutation<ValidateSubmissionResp>(VALIDATE_SUBMISSION, {
context: { clientName: 'backend' },
fetchPolicy: 'no-cache'
});

const handleValidateFiles = async () => {
setIsLoading(true);

const { data, errors } = await validateSubmission({
variables: {
_id: dataSubmission?._id,
types: getTypes(validationType),
scope: uploadType === "New" ? "New" : "All",
}
});

if (errors || !data?.validateSubmission?.success) {
setValidationAlert({ message: "Unable to initiate validation process.", severity: "error" });
setIsValidating(false);
} else {
setValidationAlert({ message: "Validation process is starting; this may take some time. Please wait before initiating another validation.", severity: "success" });
setIsValidating(true);
}

// Reset form to default values
setValidationType("Metadata");
setUploadType("New");
setIsLoading(false);
setTimeout(() => setValidationAlert(null), 10000);
};

const getTypes = (validationType: ValidationType): string[] => {
switch (validationType) {
case "Metadata":
return ["metadata"];
case "Files":
return ["file"];
default:
return ["metadata", "file"];
}
};

return (
<StyledFileValidationSection>
<GenericAlert open={!!validationAlert} severity={validationAlert?.severity} key="data-validation-alert">
{validationAlert?.message}
</GenericAlert>
<div className="fileValidationLeftSide">
<div className="fileValidationLeftSideTopRow">
<div className="headerText">Validation Type:</div>
<div className="fileValidationRadioButtonGroup">
<RadioGroup value={validationType} onChange={(event, val: ValidationType) => setValidationType(val)} row>
<StyledRadioControl
value="Metadata"
control={<StyledRadioButton readOnly={false} />}
label="Validate Metadata"
disabled={!canValidateData}
/>
<StyledRadioControl
value="Files"
control={<StyledRadioButton readOnly={false} />}
label="Validate Data Files"
disabled={!canValidateData}
/>
<StyledRadioControl
value="All"
control={<StyledRadioButton readOnly={false} />}
label="Both"
disabled={!canValidateData}
/>
</RadioGroup>
</div>
</div>
<div className="fileValidationLeftSideBottomRow">
<div className="headerText">Validation Target:</div>
<div className="fileValidationRadioButtonGroup">
<RadioGroup value={uploadType} onChange={(event, val: UploadType) => setUploadType(val)} row>
<StyledRadioControl
value="New"
control={<StyledRadioButton readOnly={false} />}
label="New Uploaded Data"
disabled={!canValidateData}
/>
<StyledRadioControl
value="All"
control={<StyledRadioButton readOnly={false} />}
label="All Uploaded Data"
disabled={!canValidateData}
/>
</RadioGroup>
</div>
</div>
</div>
<StyledValidateButton
variant="contained"
disableElevation
disabled={!canValidateData || !validateButtonEnabled || isValidating}
loading={isLoading}
onClick={handleValidateFiles}
>
{isValidating ? "Validating..." : "Validate"}
</StyledValidateButton>
</StyledFileValidationSection>
);
};

export default ValidationControls;
Loading