diff --git a/package-lock.json b/package-lock.json index 8f4850965..ee5e567b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "keeptrack", + "name": "CRDC DataHub", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "keeptrack", + "name": "CRDC DataHub", "version": "0.1.0", "dependencies": { "@apollo/client": "^3.7.15", @@ -16,6 +16,7 @@ "@mui/lab": "^5.0.0-alpha.130", "@mui/material": "^5.13.1", "@mui/styles": "^5.13.1", + "@mui/x-date-pickers": "^6.8.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -23,6 +24,7 @@ "@types/node": "^16.18.31", "@types/react": "^18.2.6", "@types/react-dom": "^18.2.4", + "dayjs": "^1.11.8", "graphql": "^16.6.0", "lodash": "^4.17.21", "nprogress": "^0.2.0", @@ -3516,6 +3518,71 @@ "react": "^17.0.0 || ^18.0.0" } }, + "node_modules/@mui/x-date-pickers": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.8.0.tgz", + "integrity": "sha512-QqJ8heAA07pCNYL1e1WIriJE1MTpZieEVvOu4FMkqByq9WB+Dyn+J3OdchS3d3Lw0BxuoZyJ9dsa0VgPIm5MhA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@mui/utils": "^5.13.1", + "@types/react-transition-group": "^4.4.6", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/base": "^5.0.0-alpha.87", + "@mui/material": "^5.8.6", + "@mui/system": "^5.8.0", + "date-fns": "^2.25.0", + "date-fns-jalali": "^2.13.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -6620,6 +6687,11 @@ "node": ">=10" } }, + "node_modules/dayjs": { + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz", + "integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -19082,6 +19154,19 @@ "react-is": "^18.2.0" } }, + "@mui/x-date-pickers": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.8.0.tgz", + "integrity": "sha512-QqJ8heAA07pCNYL1e1WIriJE1MTpZieEVvOu4FMkqByq9WB+Dyn+J3OdchS3d3Lw0BxuoZyJ9dsa0VgPIm5MhA==", + "requires": { + "@babel/runtime": "^7.21.0", + "@mui/utils": "^5.13.1", + "@types/react-transition-group": "^4.4.6", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + } + }, "@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -21348,6 +21433,11 @@ "whatwg-url": "^8.0.0" } }, + "dayjs": { + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz", + "integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/package.json b/package.json index 44e014496..ea6f2018a 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@mui/lab": "^5.0.0-alpha.130", "@mui/material": "^5.13.1", "@mui/styles": "^5.13.1", + "@mui/x-date-pickers": "^6.8.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -18,6 +19,7 @@ "@types/node": "^16.18.31", "@types/react": "^18.2.6", "@types/react-dom": "^18.2.4", + "dayjs": "^1.11.8", "graphql": "^16.6.0", "lodash": "^4.17.21", "nprogress": "^0.2.0", diff --git a/src/App.tsx b/src/App.tsx index e5fe02d74..2a6aa49e8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,8 @@ -import { RouterProvider, createBrowserRouter } from 'react-router-dom'; -import { ThemeProvider, CssBaseline, createTheme } from '@mui/material'; -import routeConfig from './router'; +import { RouterProvider, createBrowserRouter } from "react-router-dom"; +import { ThemeProvider, CssBaseline, createTheme } from "@mui/material"; +import { LocalizationProvider } from "@mui/x-date-pickers"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import routeConfig from "./router"; const theme = createTheme({ typography: { @@ -8,15 +10,15 @@ const theme = createTheme({ }, }); -const router = createBrowserRouter( - routeConfig, -); +const router = createBrowserRouter(routeConfig); function App() { return ( - - + + + + ); } diff --git a/src/assets/icons/calendar.svg b/src/assets/icons/calendar.svg new file mode 100644 index 000000000..a9f06d1f0 --- /dev/null +++ b/src/assets/icons/calendar.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/icons/dropdown_arrows.svg b/src/assets/icons/dropdown_arrows.svg new file mode 100644 index 000000000..04f540e8a --- /dev/null +++ b/src/assets/icons/dropdown_arrows.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/info_circle.svg b/src/assets/icons/info_circle.svg new file mode 100644 index 000000000..e018195b1 --- /dev/null +++ b/src/assets/icons/info_circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Questionnaire/AddRemoveButton.tsx b/src/components/Questionnaire/AddRemoveButton.tsx new file mode 100644 index 000000000..23ddfb980 --- /dev/null +++ b/src/components/Questionnaire/AddRemoveButton.tsx @@ -0,0 +1,51 @@ +import { Button, ButtonProps, Stack, StackProps, styled } from "@mui/material"; +import { FC } from "react"; + +const ActionButton = styled(Button, { + shouldForwardProp: (prop) => prop !== "textColor" && prop !== "iconColor" +})` + font-weight: 700; + font-size: 15px; + font-family: 'Nunito', 'Rubik', sans-serif; + line-height: 16px; + color: ${(props) => props.textColor ?? "#000000"}; + padding: 0; + justify-content: end; + min-width: 143px; + border: none !important; + background: transparent; + text-transform: none; + & .MuiButton-startIcon { + color: ${(props) => props.iconColor ?? "#6EC882"}; + margin-right: 4px; + & svg { + font-size: 23px; + } + } +`; + +type Props = ButtonProps & { + label?: string; + placement?: StackProps["justifyContent"]; + iconColor?: string; + textColor?: string; +}; + +const AddRemoveButton: FC = ({ + label, + placement = "end", + ...rest +}) => ( + + + {label} + + +); + +export default AddRemoveButton; diff --git a/src/components/Questionnaire/AutocompleteInput.tsx b/src/components/Questionnaire/AutocompleteInput.tsx index 9592b8eed..67c6465a0 100644 --- a/src/components/Questionnaire/AutocompleteInput.tsx +++ b/src/components/Questionnaire/AutocompleteInput.tsx @@ -5,8 +5,18 @@ import { FormHelperText, Grid, TextField, + styled, } from "@mui/material"; import { WithStyles, withStyles } from "@mui/styles"; +import dropdownArrowsIcon from "../../assets/icons/dropdown_arrows.svg"; + +const DropdownArrowsIcon = styled("div")(() => ({ + backgroundImage: `url(${dropdownArrowsIcon})`, + backgroundSize: "contain", + backgroundRepeat: "no-repeat", + width: "9.17px", + height: "18px", +})); type Props = { classes: WithStyles["classes"]; @@ -70,6 +80,25 @@ const AutocompleteInput: FC = ({ value={val} classes={{ root: classes.input }} onChange={onChangeWrapper} + popupIcon={} + slotProps={{ + paper: { + className: classes.paper + }, + popper: { + disablePortal: true, + modifiers: [ + { + // disables popper from flipping above the input when out of screen room + name: "flip", + enabled: false, + options: { + fallbackPlacements: [], + }, + }, + ], + }, + }} renderInput={(p) => ( ({ color: "#D54309 !important", }, "& .MuiOutlinedInput-notchedOutline": { - borderRadius: "0", - borderColor: "#346798", + borderRadius: "8px", + borderColor: "#6B7294", + }, + "& .Mui-focused .MuiOutlinedInput-notchedOutline": { + border: "1px solid #209D7D", + boxShadow: "2px 2px 4px 0px rgba(38, 184, 147, 0.10), -1px -1px 6px 0px rgba(38, 184, 147, 0.20)", }, "&.Mui-error fieldset": { borderColor: "#D54309 !important", }, + "& .MuiInputBase-input::placeholder": { + color: "#929296", + fontWeight: 400, + opacity: 1 + }, + "& .MuiAutocomplete-input": { + color: "#083A50", + }, + "& .MuiAutocomplete-popupIndicator": { + right: "12px" + }, + "& .MuiAutocomplete-popupIndicatorOpen": { + transform: "none" + } }, label: { - fontWeight: 500, + fontWeight: 700, fontSize: "16px", - color: "#346798", - marginBottom: "7px", + color: "#083A50", + marginBottom: "4px", }, asterisk: { color: "#D54309", - marginLeft: "4px", + marginLeft: "6px", + }, + paper: { + borderRadius: "8px", + border: "1px solid #6B7294", + marginTop: "2px", + "& .MuiAutocomplete-listbox": { + padding: 0 + }, + "& .MuiAutocomplete-option[aria-selected='true']": { + color: "#083A50", + background: "#FFFFFF" + }, + "& .MuiAutocomplete-option": { + padding: "0 10px", + height: "35px", + color: "#083A50", + background: "#FFFFFF" + }, + "& .MuiAutocomplete-option:hover": { + backgroundColor: "#5E6787", + color: "#FFFFFF" + }, + "& .MuiAutocomplete-option.Mui-focused": { + backgroundColor: "#5E6787 !important", + color: "#FFFFFF" + }, }, input: { backgroundColor: "#fff", - color: "#4E4E4E !important", + "& .MuiAutocomplete-inputRoot.MuiInputBase-root": { + padding: 0, + }, + "& .MuiInputBase-input": { + fontWeight: 400, + fontSize: "16px", + fontFamily: "'Nunito', 'Rubik', sans-serif", + padding: "12px !important", + height: "20px", + }, }, }); diff --git a/src/components/Questionnaire/DatePickerInput.tsx b/src/components/Questionnaire/DatePickerInput.tsx new file mode 100644 index 000000000..5ee937178 --- /dev/null +++ b/src/components/Questionnaire/DatePickerInput.tsx @@ -0,0 +1,214 @@ +import React, { FC, useEffect, useId, useState } from "react"; +import { + FormControl, + FormHelperText, + Grid, + TextFieldProps, + styled, +} from "@mui/material"; +import { DatePicker, DatePickerProps, DateValidationError } from "@mui/x-date-pickers"; +import dayjs, { Dayjs } from "dayjs"; +import Tooltip from "./Tooltip"; +import calendarIcon from "../../assets/icons/calendar.svg"; + +const CalendarIcon = styled("div")(() => ({ + backgroundImage: `url(${calendarIcon})`, + backgroundSize: "contain", + backgroundRepeat: "no-repeat", + width: "22.77px", + height: "22.06px", +})); + +const GridItem = styled(Grid)(({ theme }) => ({ + "& .MuiFormHelperText-root": { + color: "#083A50", + marginLeft: "0", + [theme.breakpoints.up("lg")]: { + whiteSpace: "nowrap", + }, + }, + "& .MuiFormHelperText-root.Mui-error": { + color: "#D54309 !important", + }, +})); + +const StyledFormControl = styled(FormControl)(() => ({ + height: "100%", + justifyContent: "end", +})); + +const StyledAsterisk = styled("span")(() => ({ + color: "#D54309", + marginLeft: "6px", +})); + +const StyledFormLabel = styled("label")(({ theme }) => ({ + fontWeight: 700, + fontSize: "16px", + lineHeight: "19.6px", + minHeight: "20px", + color: "#083A50", + marginBottom: "4px", + [theme.breakpoints.up("lg")]: { + whiteSpace: "nowrap", + }, +})); + +const StyledFormHelperText = styled(FormHelperText)(() => ({ + marginTop: "4px", + minHeight: "20px", +})); + +const StyledDatePicker = styled(DatePicker)(() => ({ + "& .MuiInputBase-root": { + borderRadius: "8px", + backgroundColor: "#FFFFFF", + color: "#083A50", + }, + "& .MuiInputBase-input": { + fontWeight: 400, + fontSize: "16px", + fontFamily: "'Nunito', 'Rubik', sans-serif", + lineHeight: "19.6px", + padding: "12px", + height: "20px", + }, + "& .Mui-focused .MuiOutlinedInput-notchedOutline": { + border: "1px solid #209D7D !important", + boxShadow: "2px 2px 4px 0px rgba(38, 184, 147, 0.10), -1px -1px 6px 0px rgba(38, 184, 147, 0.20)", + }, + "& .MuiOutlinedInput-notchedOutline": { + borderColor: "#6B7294", + }, + "& .MuiInputBase-input::placeholder": { + color: "#929296", + fontWeight: 400, + opacity: 1 + }, + // Override the input error border color + "&.Mui-error fieldset": { + borderColor: "#D54309 !important", + }, + // Target readOnly inputs + "& .MuiOutlinedInput-input:read-only": { + backgroundColor: "#D9DEE4", + cursor: "not-allowed", + }, +})); + +type Props = { + initialValue?: string | Date; + label: string; + infoText?: string; + errorText?: string; + tooltipText?: string; + gridWidth?: 2 | 4 | 6 | 8 | 10 | 12; + maxLength?: number; + name?: string; + required?: boolean; + inputProps?: TextFieldProps; + validate?: (input: string) => boolean; + filter?: (input: string) => string; +} & DatePickerProps; + +/** + * Generates a generic date picker input with a label, help text, and tooltip text + * + * @param {Props} props + * @returns {JSX.Element} + */ +const DatePickerInput: FC = ({ + initialValue, + label, + name, + required = false, + inputProps, + gridWidth, + maxLength, + infoText, + tooltipText, + errorText, + validate, + filter, + onChange, + ...rest +}) => { + const id = useId(); + const [val, setVal] = useState(dayjs(initialValue)); + const [error, setError] = useState(false); + const errorMsg = errorText || (required ? "This field is required" : null); + + const handleOnError = (error: DateValidationError) => { + if (!error && val) { + setError(false); + return; + } + setError(true); + }; + + const onChangeWrapper = (newVal) => { + if (typeof onChange === "function") { + onChange(newVal, null); + } + + if (!newVal) { + setError(true); + } + + setVal(newVal); + }; + + useEffect(() => { + if (initialValue) { + onChangeWrapper(initialValue.toString().trim()); + } + }, [initialValue]); + + return ( + + + + {label} + {required ? (*) : ""} + {tooltipText && } + + onChangeWrapper(value)} + onError={handleOnError} + slots={{ openPickerIcon: CalendarIcon }} + slotProps={{ + textField: { + id, + name, + required, + error, + size: "small", + ...inputProps, + }, + popper: { + placement: "bottom-end", + disablePortal: true, + modifiers: [ + { + // disables popper from flipping above the input when out of screen room + name: "flip", + enabled: false, + options: { + fallbackPlacements: [], + }, + }, + ], + }, + }} + {...rest} + /> + + {(error ? errorMsg : infoText) || " "} + + + + ); +}; + +export default DatePickerInput; diff --git a/src/components/Questionnaire/FormContainer.tsx b/src/components/Questionnaire/FormContainer.tsx index 3b6b08ce5..8569aad38 100644 --- a/src/components/Questionnaire/FormContainer.tsx +++ b/src/components/Questionnaire/FormContainer.tsx @@ -25,14 +25,11 @@ const FormContainer: FC = ({ return (
- - {title} - - + {description}
-
e.preventDefault()}> + e.preventDefault()}> {children}
@@ -41,32 +38,28 @@ const FormContainer: FC = ({ const styles = () => ({ formContainer: { - border: "2px solid #ACC7E5", background: "transparent", borderRadius: "8px", paddingBottom: "25px", }, + form: { + fontWeight: 400, + fontSize: '16px', + fontFamily: "'Nunito', 'Rubik', sans-serif", + }, titleGroup: { - background: "#E9F2FA", - color: "#2F486C", - padding: "33px 44px", - marginBottom: "10px", + background: "transparent", + color: "#327E8F", + paddingBottom: "40px", borderRadius: "8px 8px 0 0", display: "flex", alignItems: "center", }, sectionTitle: { - fontWeight: 600, - fontFamily: "'Public Sans', sans-serif", - marginRight: "19px", - fontSize: "30px", - lineHeight: "27px", - }, - sectionDesc: { - fontWeight: 300, - fontSize: "25px", - fontFamily: "'Rubik', sans-serif", - lineHeight: "27px", + fontWeight: 700, + fontSize: "24px", + fontFamily: "'Nunito', 'Rubik', sans-serif", + lineHeight: "32.74px", }, }); diff --git a/src/components/Questionnaire/PlannedPublication.tsx b/src/components/Questionnaire/PlannedPublication.tsx new file mode 100644 index 000000000..1d92894a4 --- /dev/null +++ b/src/components/Questionnaire/PlannedPublication.tsx @@ -0,0 +1,75 @@ +import React, { FC } from "react"; +import { Grid, styled } from "@mui/material"; +import RemoveCircleIcon from "@mui/icons-material/RemoveCircle"; +import TextInput from "./TextInput"; +import { Status as FormStatus, useFormContext } from "../Contexts/FormContext"; +import AddRemoveButton from "./AddRemoveButton"; +import DatePickerInput from "./DatePickerInput"; + +const GridContainer = styled(Grid)(() => ({ + border: "0.5px solid #DCDCDC !important", + borderRadius: "10px", + padding: "18px 15px", + marginLeft: "12px", +})); + +type Props = { + index: number; + plannedPublication: PlannedPublication | null; + onDelete: () => void; +}; + +/** + * Additional Contact Form Group + * + * @param {Props} props + * @returns {JSX.Element} + */ +const PlannedPublication: FC = ({ + index, + plannedPublication, + onDelete, +}: Props) => { + const { status } = useFormContext(); + + const { title, expectedDate } = plannedPublication; + + return ( + + + + + + + } + iconColor="#F18E8E" + disabled={status === FormStatus.SAVING} + /> + + + ); +}; + +export default PlannedPublication; diff --git a/src/components/Questionnaire/Publication.tsx b/src/components/Questionnaire/Publication.tsx index 95c326f0d..157cfccaa 100644 --- a/src/components/Questionnaire/Publication.tsx +++ b/src/components/Questionnaire/Publication.tsx @@ -1,13 +1,19 @@ import React, { FC } from "react"; -import { Button, Grid, Stack } from "@mui/material"; -import BookmarkRemoveIcon from '@mui/icons-material/BookmarkRemove'; -import { WithStyles, withStyles } from "@mui/styles"; +import { Grid, styled } from "@mui/material"; +import RemoveCircleIcon from "@mui/icons-material/RemoveCircle"; import TextInput from "./TextInput"; import { Status as FormStatus, useFormContext } from "../Contexts/FormContext"; +import AddRemoveButton from "./AddRemoveButton"; + +const GridContainer = styled(Grid)(() => ({ + border: "0.5px solid #DCDCDC !important", + borderRadius: "10px", + padding: "18px 15px", + marginLeft: "12px", +})); type Props = { index: number; - classes: WithStyles["classes"]; publication: Publication | null; onDelete: () => void; }; @@ -19,19 +25,22 @@ type Props = { * @returns {JSX.Element} */ const Publication: FC = ({ - index, classes, publication, onDelete, + index, + publication, + onDelete, }: Props) => { const { status } = useFormContext(); const { title, pubmedID, DOI } = publication; return ( - - + + = ({ label="PubMedID" name={`publications[${index}][pubmedID]`} value={pubmedID} + placeholder="Enter ID" maxLength={20} + gridWidth={6} /> - - - - + + } + iconColor="#F18E8E" + disabled={status === FormStatus.SAVING} + /> - + ); }; -const styles = () => ({ - root: { - border: "0.5px solid #346798", - borderRadius: "8px", - padding: "20px 30px", - marginTop: "20px", - marginLeft: "37px", - marginRight: "-27px", - }, - button: { - color: "#346798", - marginLeft: "auto", - marginTop: "28px", - marginRight: "-4px", - padding: "6px 20px", - minWidth: "115px", - borderRadius: "25px", - border: "2px solid #AFC2D8 !important", - background: "transparent", - "text-transform": "none", - "& .MuiButton-startIcon": { - marginRight: "14px", - }, - }, - lastInput: { - maxWidth: "250px", - marginLeft: "auto", - }, -}); - -export default withStyles(styles)(Publication); +export default Publication; diff --git a/src/components/Questionnaire/Repository.tsx b/src/components/Questionnaire/Repository.tsx index a687fd3d0..a03ff8930 100644 --- a/src/components/Questionnaire/Repository.tsx +++ b/src/components/Questionnaire/Repository.tsx @@ -1,13 +1,19 @@ import React, { FC } from "react"; -import { Button, Grid, Stack } from "@mui/material"; -import LabelOffIcon from '@mui/icons-material/LabelOff'; -import { WithStyles, withStyles } from "@mui/styles"; +import { Grid, styled } from "@mui/material"; +import RemoveCircleIcon from "@mui/icons-material/RemoveCircle"; import TextInput from "./TextInput"; import { Status as FormStatus, useFormContext } from "../Contexts/FormContext"; +import AddRemoveButton from "./AddRemoveButton"; + +const GridContainer = styled(Grid)(() => ({ + border: "0.5px solid #DCDCDC !important", + borderRadius: "10px", + padding: "18px 15px", + marginLeft: "12px", +})); type Props = { index: number; - classes: WithStyles["classes"]; repository: Repository | null; onDelete: () => void; }; @@ -19,75 +25,57 @@ type Props = { * @returns {JSX.Element} */ const Repository: FC = ({ - index, classes, repository, onDelete, + index, + repository, + onDelete, }: Props) => { const { status } = useFormContext(); const { name, studyID } = repository; return ( - - + + + - - - - + + } + iconColor="#F18E8E" + disabled={status === FormStatus.SAVING} + /> - + ); }; -const styles = () => ({ - root: { - border: "0.5px solid #346798", - borderRadius: "8px", - padding: "20px 30px", - marginTop: "20px", - marginLeft: "37px", - marginRight: "-27px", - }, - button: { - color: "#346798", - marginLeft: "auto", - marginTop: "28px", - marginRight: "-4px", - padding: "6px 20px", - minWidth: "115px", - borderRadius: "25px", - border: "2px solid #AFC2D8 !important", - background: "transparent", - "text-transform": "none", - "& .MuiButton-startIcon": { - marginRight: "14px", - }, - }, -}); - -export default withStyles(styles)(Repository); +export default Repository; diff --git a/src/components/Questionnaire/SectionGroup.tsx b/src/components/Questionnaire/SectionGroup.tsx index 29a69086b..dd36391b9 100644 --- a/src/components/Questionnaire/SectionGroup.tsx +++ b/src/components/Questionnaire/SectionGroup.tsx @@ -1,10 +1,12 @@ -import React, { FC } from 'react'; -import { Divider, Grid, Typography } from '@mui/material'; -import { WithStyles, withStyles } from '@mui/styles'; +import React, { FC } from "react"; +import { Box, Divider, Grid, Stack, Typography } from "@mui/material"; +import { WithStyles, withStyles } from "@mui/styles"; type Props = { - classes: WithStyles['classes']; + classes: WithStyles["classes"]; title?: string | JSX.Element; + description?: string | JSX.Element; + endButton?: JSX.Element; divider?: boolean; children: React.ReactNode; }; @@ -16,18 +18,38 @@ type Props = { * @returns {JSX.Element} */ const SectionGroup: FC = ({ - title, classes, children, - divider = true, + title, + description, + classes, + children, + endButton, + divider = false, }) => ( <> {divider && } - - - {title && ( - - {title} - - )} + + + + {title && ( + + {title} + {description && ( + + {' '} + {description} + + )} + + )} + {endButton && ( + {endButton} + )} + {children} @@ -36,16 +58,29 @@ const SectionGroup: FC = ({ const styles = () => ({ group: { - marginTop: "25px", - padding: "0 82px", + marginTop: "70px", + "&:first-of-type": { + marginTop: 0, + }, + "& > .MuiGrid-container": { + marginTop: "24px", + }, + "& > .MuiGrid-item + .MuiGrid-container": { + marginTop: 0, + }, + }, + groupHeader: { + marginBottom: "24px", }, groupTitle: { - fontWeight: 500, - fontFamily: "'Rubik', sans-serif", - color: "#00A37D", - fontSize: "18px", - margin: "0 -25px", - marginBottom: "18px", + fontWeight: 700, + fontFamily: "'Nunito', 'Rubik', sans-serif", + lineHeight: "19.6px", + color: "#34A286", + fontSize: "16px", + }, + groupDescription: { + fontWeight: 400, }, divider: { borderColor: "#ACC7E5", @@ -54,6 +89,9 @@ const styles = () => ({ marginTop: "25px", marginBottom: "0", }, + endButtonWrapper: { + marginLeft: "auto", + }, }); export default withStyles(styles, { withTheme: true })(SectionGroup); diff --git a/src/components/Questionnaire/SelectInput.tsx b/src/components/Questionnaire/SelectInput.tsx index 2e429ea93..47bfd600b 100644 --- a/src/components/Questionnaire/SelectInput.tsx +++ b/src/components/Questionnaire/SelectInput.tsx @@ -5,8 +5,19 @@ import { Grid, MenuItem, Select, + SelectProps, + styled, } from "@mui/material"; import { WithStyles, withStyles } from "@mui/styles"; +import dropdownArrowsIcon from "../../assets/icons/dropdown_arrows.svg"; + +const DropdownArrowsIcon = styled("div")(() => ({ + backgroundImage: `url(${dropdownArrowsIcon})`, + backgroundSize: "contain", + backgroundRepeat: "no-repeat", + width: "9.17px", + height: "18px", +})); type Props = { classes: WithStyles["classes"]; @@ -18,7 +29,7 @@ type Props = { helpText?: string; gridWidth?: 2 | 4 | 6 | 8 | 10 | 12; onChange?: (value: string) => void; -}; +} & SelectProps; /** * Generates a generic select box with a label and help text @@ -36,6 +47,7 @@ const SelectInput: FC = ({ helpText, gridWidth, onChange, + ...rest }) => { const id = useId(); @@ -64,12 +76,15 @@ const SelectInput: FC = ({