Skip to content

Commit

Permalink
Ieee 259 add description box on teams homepage (#509)
Browse files Browse the repository at this point in the history
* incident form init

* implementing form components

* fixing form style

* fixing position of menu items

* implement back button

* adding helper text for radio

* fixing button

* cleaning up unused vars

* adding report broken lost button

* init test

* fixing link

* reset form on successful submission

* setting up testing

* fix test

* fixing tests

* removing unused code

* adding name to readme

* fix type error

* padding qty to new page & adding snackbar

* rewriting formik with map

* fixing responsive issues on form

* fixing checkedout tables style

* adding map to radio options

* using map in form

* fixing textfield width

* fixing validation onchange

* adding more unit tests to check for rendering of form components and submit button

* adding more padding between form elements and labels

* removing inline styles (sorry bad habitsgit status)

* removing unused code and console.log & changing url format a little bit to pass more info about hardware for incident form

* added yup validation to check if the input string is 0 for qty, is so don't submit the form

* IEEE-259 add input box on teams homepage

* Delete hackathon_site/dashboard/frontend/package-lock.json

* IEEE-259 remove unused ErrorMessage

* IEEE-259 refactor and clean project description component

* IEEE-259 Add title and project description to admin page

* IEEE-259 Clean code in CartSummary.tsx

* IEEE-259 Add project description character limit display and fix tests

* IEEE-259 refactor project description component and teamDetail slice

* IEEE-259 Fix dashboard and teamDetails test cases

---------

Co-authored-by: Natalie <natapoke2@gmail.com>
Co-authored-by: Mustafa <mus2003.abdul@gmail.com>
Co-authored-by: Karandeep Lubana <karandeep.lubana@utoronto.ca>
  • Loading branch information
4 people authored Feb 1, 2024
1 parent 1dc1d95 commit 4585976
Show file tree
Hide file tree
Showing 14 changed files with 359 additions and 3 deletions.
1 change: 1 addition & 0 deletions hackathon_site/dashboard/frontend/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export interface Team {
created_at: string;
updated_at: string;
profiles: ProfileWithUser[];
project_description: string;
}

export interface ProfileWithUser extends ProfileWithoutTeamNumber {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { maxTeamSize, minTeamSize } from "constants.js";
import AlertBox from "components/general/AlertBox/AlertBox";
import { Link } from "@material-ui/core";
import DateRestrictionAlert from "components/general/DateRestrictionAlert/DateRestrictionAlert";
import ProjectDescriptionAlert from "components/teamDetail/ProjectDescription/ProjectDescriptionAlert";

const CartErrorBox = () => {
const cartQuantity = useSelector(cartTotalSelector);
Expand All @@ -22,6 +23,7 @@ const CartErrorBox = () => {
<Grid xs={12} sm={12} md={2} item />
<Grid xs={12} sm={12} md={8} item>
<DateRestrictionAlert />
<ProjectDescriptionAlert />
{orderSubmissionError && cartQuantity > 0 && (
<AlertBox
error={orderSubmissionError}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,41 @@ import {
import CircularProgress from "@material-ui/core/CircularProgress";
import { teamSizeSelector } from "slices/event/teamSlice";
import { isTestUserSelector } from "slices/users/userSlice";
import { projectDescriptionSelector } from "slices/event/teamDetailSlice";
import { displaySnackbar } from "slices/ui/uiSlice";
import {
hardwareSignOutEndDate,
hardwareSignOutStartDate,
maxTeamSize,
minTeamSize,
minProjectDescriptionLength,
} from "constants.js";

const CartSummary = () => {
const isTestUser = useSelector(isTestUserSelector);
const cartQuantity = useSelector(cartTotalSelector);
const cartOrderLoading = useSelector(isLoadingSelector);
const teamSize = useSelector(teamSizeSelector);
const projectDescription = useSelector(projectDescriptionSelector);
const teamSizeValid = teamSize >= minTeamSize && teamSize <= maxTeamSize;
const dispatch = useDispatch();
const onSubmit = () => {
if (cartQuantity > 0) {
dispatch(submitOrder());
if (
projectDescription &&
projectDescription.length < minProjectDescriptionLength
) {
dispatch(
displaySnackbar({
message: "Please provide a more detailed project description.",
options: {
variant: "error",
},
})
);
} else {
dispatch(submitOrder());
}
}
};
const currentDateTime = new Date();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.formTextField {
margin: 10px 5px 0 0;
}

.actionBtn {
width: 120px;
}

.submitBtn {
width: 120px;
margin-right: 10px;
}

.projectDescriptionDetail {
padding: 10px;
}

.title {
margin-top: 30px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React, { useState } from "react";
import styles from "./ProjectDescription.module.scss";
import { Formik, Form, Field, FormikValues } from "formik";
import * as Yup from "yup";
import { TextField, Button, Box, Grid, Typography } from "@material-ui/core";
import { useDispatch, useSelector } from "react-redux";
import { updateProjectDescription } from "slices/event/teamDetailSlice";
import { minProjectDescriptionLength } from "constants.js";
import { teamSelector, isLoadingSelector } from "slices/event/teamSlice";

export interface ProjectDescriptionProps {
teamCode: string;
}

const ProjectDescription = ({ teamCode }: ProjectDescriptionProps) => {
const dispatch = useDispatch();
const isProjectDescriptionLoading: boolean = useSelector(isLoadingSelector);
const initialProjectDescription =
useSelector(teamSelector)?.project_description ||
"Project description is required";
const [isEditing, setIsEditing] = useState(false);
const projectDescriptionSchema = Yup.object().shape({
projectDescription: Yup.string()
.max(500)
.required("Project description is required"),
});

const handleSubmit = async (values: FormikValues, { setSubmitting }: any) => {
await dispatch(
updateProjectDescription({
teamCode: teamCode,
projectDescription: values.projectDescription,
})
);
setSubmitting(false);
setIsEditing(false);
};

return (
<>
{isProjectDescriptionLoading ? (
""
) : (
<div className={styles.title}>
<Typography variant="h2" gutterBottom>
Project Description
</Typography>
<Formik
initialValues={{
projectDescription: initialProjectDescription,
}}
validationSchema={projectDescriptionSchema}
onSubmit={handleSubmit}
>
{({ isSubmitting, isValid, values }) => (
<Form>
<Field
as={TextField}
name="projectDescription"
multiline
fullWidth
variant="outlined"
disabled={!isEditing}
rows={4}
className={styles.formTextField}
/>
{minProjectDescriptionLength -
values.projectDescription.length >
0 && (
<Typography
variant="caption"
display="block"
gutterBottom
>
Minimum{" "}
{minProjectDescriptionLength -
values.projectDescription.length}{" "}
characters required
</Typography>
)}
<Box mt={2}>
<Grid container justifyContent="flex-end">
{isEditing ? (
<>
<Button
type="submit"
variant="contained"
disabled={!isValid || isSubmitting}
className={styles.submitBtn}
>
SUBMIT
</Button>
<Button
type="button"
variant="contained"
color="secondary"
className={styles.actionBtn}
onClick={() => setIsEditing(false)}
>
CANCEL
</Button>
</>
) : null}
{!isEditing && (
<Button
type="button"
variant="contained"
color="primary"
className={styles.actionBtn}
onClick={() => setIsEditing(true)}
>
EDIT
</Button>
)}
</Grid>
</Box>
</Form>
)}
</Formik>
</div>
)}
</>
);
};

export default ProjectDescription;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";
import AlertBox from "components/general/AlertBox/AlertBox";
import { teamSelector } from "slices/event/teamSlice";
import { useSelector } from "react-redux";
import { minProjectDescriptionLength } from "constants.js";

const ProjectDescriptionAlert = () => {
const projectDescription = useSelector(teamSelector)?.project_description;

if (projectDescription && projectDescription.length < minProjectDescriptionLength) {
return (
<AlertBox
data-testid="project-description-alert"
title="Project Description Required"
error="Please provide a more detailed project description."
type="error"
/>
);
}

return null;
};

export default ProjectDescriptionAlert;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react";
import { useSelector } from "react-redux";
import {
projectDescriptionSelector,
isTeamInfoLoadingSelector,
} from "slices/event/teamDetailSlice";
import styles from "./ProjectDescription.module.scss";
import { LinearProgress, Paper, Typography } from "@material-ui/core";

const ProjectDescriptionDetail = () => {
const projectDescription = useSelector(projectDescriptionSelector);
const isTeamInfoLoading = useSelector(isTeamInfoLoadingSelector);

return (
<div>
{isTeamInfoLoading ? (
<LinearProgress data-testid="project-description-linear-progress" />
) : (
<>
<Typography variant="h2" gutterBottom>
Project Description
</Typography>
<Paper className={styles.projectDescriptionDetail}>
<Typography variant="body1">{projectDescription}</Typography>
</Paper>
</>
)}
</div>
);
};

export default ProjectDescriptionDetail;
3 changes: 2 additions & 1 deletion hackathon_site/dashboard/frontend/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export const adminGroup = "Hardware Site Admins";
export const minTeamSize = 2;
export const maxTeamSize = 4;
export const hardwareSignOutStartDate = new Date(2020, 9, 1, 23, 59);
export const hardwareSignOutEndDate = new Date(2023, 9, 30, 11, 59);
export const hardwareSignOutEndDate = new Date(2025, 9, 30, 11, 59);
export const hssTestUserGroup = "HSS Test Users";
export const minProjectDescriptionLength = 60;
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import AlertBox from "components/general/AlertBox/AlertBox";
import { openTeamModalItem } from "slices/ui/uiSlice";
import EditTeam from "components/dashboard/EditTeam/EditTeam";
import DateRestrictionAlert from "components/general/DateRestrictionAlert/DateRestrictionAlert";
import ProjectDescription from "components/teamDetail/ProjectDescription/ProjectDescription";

const Dashboard = () => {
const dispatch = useDispatch();
Expand Down Expand Up @@ -122,6 +123,7 @@ const Dashboard = () => {
{fetchOrderError && <AlertBox error={fetchOrderError} />}
{/* TODO: add back in when incident reports are completed on the frontend */}
{/* <BrokenTable items={itemsBroken} openReportAlert={openBrokenTable} /> */}
<ProjectDescription teamCode={team_code ?? "None"} />
<PendingTables />
<CheckedOutTables />
<ReturnedTable />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ describe("<TeamDetail />", () => {
render(<TeamDetail {...teamDetailProps} />);

expect(screen.getByTestId("team-info-linear-progress")).toBeInTheDocument();
expect(
screen.getByTestId("project-description-linear-progress")
).toBeInTheDocument();

await waitFor(() => {
expect(mockedGet).toHaveBeenNthCalledWith(
Expand Down Expand Up @@ -141,6 +144,9 @@ describe("<TeamDetail />", () => {
const { getByTestId, getByText } = render(<TeamDetail {...teamDetailProps} />);

expect(screen.getByTestId("team-info-linear-progress")).toBeInTheDocument();
expect(
screen.getByTestId("project-description-linear-progress")
).toBeInTheDocument();

await waitFor(() => {
expect(mockedGet).toHaveBeenNthCalledWith(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import TeamCheckedOutOrderTable from "components/teamDetail/TeamCheckedOutOrderT
import { getHardwareWithFilters, setFilters } from "slices/hardware/hardwareSlice";
import { getCategories } from "slices/hardware/categorySlice";
import ProductOverview from "components/inventory/ProductOverview/ProductOverview";
import ProjectDescriptionDetail from "components/teamDetail/ProjectDescription/ProjectDescriptionDetail";

export interface PageParams {
code: string;
Expand Down Expand Up @@ -91,6 +92,7 @@ const TeamDetail = ({ match }: RouteComponentProps<PageParams>) => {
<AlertBox error={orderError} />
) : (
<>
<ProjectDescriptionDetail />
<SimplePendingOrderFulfillmentTable />
<Divider className={styles.dividerMargin} />
<TeamCheckedOutOrderTable />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
teamInfoErrorSelector,
updateParticipantIdErrorSelector,
updateParticipantIdProvided,
updateProjectDescription,
} from "slices/event/teamDetailSlice";

import { get, patch } from "api/api";
Expand Down Expand Up @@ -216,3 +217,58 @@ describe("updateParticipantIdProvided thunk", () => {
);
});
});

describe("updateProjectDescription thunk", () => {
it("Updates the store on API success", async () => {
const store = mockStore(); // Initialize mock store

// Define the parameters for the thunk
const params = {
teamCode: "A48E5",
projectDescription: "Updated project description",
};

const mockApiResponse = { ...mockTeam };
mockApiResponse.project_description = "Updated project description";
let mockResponse = makeMockApiResponse(mockApiResponse);
mockedPatch.mockResolvedValueOnce(mockResponse);

await store.dispatch(updateProjectDescription(params));

await waitFor(() => {
expect(mockedPatch).toHaveBeenCalledWith(
`/api/event/teams/${mockTeam.team_code}`,
{
project_description: "Updated project description",
}
);
});
});
it("Dispatches snackbar on API failure", async () => {
const store = mockStore();

const params = {
teamCode: "A48E5",
projectDescription: "Updated project description",
};

mockedPatch.mockRejectedValueOnce(teamFailureResponse);
await store.dispatch(updateProjectDescription(params));

expect(mockedPatch).toHaveBeenCalledWith(
`/api/event/teams/${mockTeam.team_code}`,
{
project_description: "Updated project description",
}
);

const actions = store.getActions();

expect(actions).toContainEqual(
displaySnackbar({
message: "Could not update project description: Error 404",
options: { variant: "error" },
})
);
});
});
Loading

0 comments on commit 4585976

Please sign in to comment.