Skip to content

Commit

Permalink
Parent Profile + Linking Students (#123)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexzhang1618 authored Jan 16, 2023
1 parent 6f53bb5 commit 201899a
Show file tree
Hide file tree
Showing 49 changed files with 1,403 additions and 522 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ yarn-error.log*
/public/swagger.json

/models/*.ts

90 changes: 90 additions & 0 deletions __tests__/parents.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import parentStudentHandler from "../pages/api/parents/[id]/student";
import { client } from "../lib/db";
import { makeHTTPRequest } from "./__testutils__/testutils.test";
import { CreateParentStudentLink, ParentStudentLink } from "../models";
import { StatusCodes } from "http-status-codes";

const STUDENT_NOT_FOUND_ERROR = "Student not found";
beforeAll(async () => {
await client.query("DELETE from users");
await client.query("DELETE from event_information");
await client.query("DELETE from commitments");
await client.query("DELETE from classes");
await client.query("DELETE from images");
await client.query("INSERT INTO images(id) VALUES('1')");
await client.query(
"INSERT INTO users(id, first_name, last_name, email, role, address, phone_number, date_created, picture_id) VALUES('1', 'John', 'Doe', '[email protected]', 'Student', '123 Main Street', '1234567890', '5/23/2022, 4:45:03 AM', '1')"
);
await client.query(
"INSERT INTO users(id, first_name, last_name, email, role, address, phone_number, date_created, picture_id) VALUES('2', 'Jane', 'Doe', '[email protected]', 'Parent', '123 Main Street', '1234567890', '5/23/2022, 4:45:03 AM', '1')"
);
await client.query(
"INSERT INTO users(id, first_name, last_name, email, role, approved, address, phone_number, date_created, picture_id) VALUES('3', 'Teacher', 'Doe', '[email protected]', 'Teacher', false, '123 Main Street', '1234567890', '5/23/2022, 4:45:03 AM', '1')"
);
await client.query(
"INSERT INTO users(id, first_name, last_name, email, role, approved, address, phone_number, date_created, picture_id) VALUES('4', 'Admin', 'Doe', '[email protected]', 'Admin', false, '123 Main Street', '1234567890', '5/23/2022, 4:45:03 AM', '1')"
);
});

afterAll(async () => {
await client.query("DELETE from users");
await client.end();
});

describe("[POST] /api/parents/[id]/student", () => {
test("Create a valid parent-student link", async () => {
const body: CreateParentStudentLink = {
email: "[email protected]",
};
const query = {
id: "2",
};
const expected: ParentStudentLink = {
parentId: "2",
studentId: "1",
};
await makeHTTPRequest(
parentStudentHandler,
"/api/parents/2/student",
query,
"POST",
body,
StatusCodes.OK,
expected
);
});
test("Create an invalid link between a parent and a non-student", async () => {
const body: CreateParentStudentLink = {
email: "[email protected]",
};
const query = {
id: "2",
};
await makeHTTPRequest(
parentStudentHandler,
"/api/parents/2/student",
query,
"POST",
body,
StatusCodes.NOT_FOUND,
STUDENT_NOT_FOUND_ERROR
);
});
test("Create an invalid link between a parent and a nonexistent user", async () => {
const body: CreateParentStudentLink = {
email: "[email protected]",
};
const query = {
id: "2",
};
await makeHTTPRequest(
parentStudentHandler,
"/api/parents/2/student",
query,
"POST",
body,
StatusCodes.NOT_FOUND,
STUDENT_NOT_FOUND_ERROR
);
});
});
6 changes: 3 additions & 3 deletions __tests__/users.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe("[GET] /api/users/?filter", () => {
lastName: "Doe",
email: "[email protected]",
role: "Student",
approved: true,
approved: false,
dateCreated: "5/23/2022, 4:45:03 AM",
address: "123 Main Street",
phoneNumber: "1234567890",
Expand All @@ -63,7 +63,7 @@ describe("[GET] /api/users/?filter", () => {
lastName: "Doe",
email: "[email protected]",
role: "Student",
approved: true,
approved: false,
dateCreated: "5/23/2022, 4:45:03 AM",
address: "123 Main Street",
phoneNumber: "1234567890",
Expand Down Expand Up @@ -407,7 +407,7 @@ describe("[POST] /api/users", () => {
lastName: "Doe",
email: "[email protected]",
role: "Student",
approved: true,
approved: false,
pictureId: "",
dateCreated: "",
address: null,
Expand Down
15 changes: 14 additions & 1 deletion components/Login/LoginPositionInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const LoginPositionInput: React.FC<LoginPositionInputProps> = ({
<br></br>
<input
type="radio"
id="teacher"
id="student"
name="select-position"
value="Student"
onChange={(_) => onContentChange("Student")}
Expand All @@ -73,6 +73,19 @@ const LoginPositionInput: React.FC<LoginPositionInputProps> = ({
<label htmlFor="volunteer" className={styles.positionText}>
Volunteer
</label>
<br></br>
<input
type="radio"
id="parent"
name="select-position"
value="Parent"
onChange={(_) => onContentChange("Parent")}
className={styles.radioBox}
checked={currPosition == "Parent"}
/>
<label htmlFor="parent" className={styles.positionText}>
Parent
</label>
</form>
</div>
</div>
Expand Down
8 changes: 1 addition & 7 deletions components/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ import { AuthContext } from "../../context/AuthContext";

export const Navbar: React.FC = ({ children }) => {
const router = useRouter();
// let authUser = null;
// try {
const { user } = useContext(AuthContext);
// authUser = user;
// } catch {
// return null;
// }

if (user == null) return null;
return (
Expand Down Expand Up @@ -41,7 +35,7 @@ export const Navbar: React.FC = ({ children }) => {
</a>
</li>

{user.role != "Student" ? (
{user.role != "Student" && user.role != "Parent" ? (
<li className={styles.navitem}>
<a
className={router.pathname == "/league" ? styles.clicked : styles.navlink}
Expand Down
51 changes: 51 additions & 0 deletions components/Profile/ParentProfile/AddStudentModal.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.modalContainer {
width: 509.31px;
height: 261px;
z-index: 3;
border: 3px solid;
opacity: 100;
border-color: var(--orange);
position: absolute;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
border-radius: 11px;
background-color: white;
}

.modalContainerInside {
margin: 40.8px 64px 31px;
}

.textStyle {
font-size: 21.83px;
line-height: 28px;
vertical-align: top;
font-family: "Inter";

padding-bottom: 25px;
}

.input {
width: 100%;
}

.textButtonSpacing {
height: 39.21px;
}

.modalButtonContainer {
display: flex;
justify-content: space-evenly;
align-items: center;
}

.button {
width: 143px;
}

.errorMsg {
padding-top: 15px;
color: red;
}
54 changes: 54 additions & 0 deletions components/Profile/ParentProfile/AddStudentModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { useState } from "react";
import styles from "./AddStudentModal.module.css";
import TextField from "@mui/material/TextField";
import { Button } from "@mui/material";

type AddStudentModalProps = {
setShowModalState: (newState: boolean) => void;
linkParentAndStudent: (studentEmail: string) => Promise<void>;
errorMsg: string;
};
// modal to add students
export const AddStudentModal: React.FC<AddStudentModalProps> = ({
setShowModalState,
linkParentAndStudent,
errorMsg,
}) => {
const [studentEmail, setStudentEmail] = useState("");

return (
<div className={styles.modalContainer}>
<div className={styles.modalContainerInside}>
{/* eslint-disable-next-line react/no-unescaped-entities */}
<div className={styles.textStyle}>Enter Your Student's Email:</div>

<TextField
onChange={(e) => setStudentEmail(e.target.value)}
className={styles.input}
id="standard-basic"
variant="standard"
/>
<div className={styles.textButtonSpacing}></div>
<div className={styles.modalButtonContainer}>
<Button
className={styles.button}
onClick={() => setShowModalState(false)}
variant="outlined"
>
Cancel
</Button>
<Button
className={styles.button}
onClick={async () => {
await linkParentAndStudent(studentEmail);
}}
variant="contained"
>
Submit
</Button>
</div>
<div className={styles.errorMsg}>{errorMsg != "" ? "Error: " + errorMsg : ""}</div>
</div>
</div>
);
};
26 changes: 26 additions & 0 deletions components/Profile/ParentProfile/ConnectStudentProfile.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.createStudentContainer {
margin-bottom: 59px;
}

.connectStudentContainer {
display: flex;
justify-content: space-between;
align-items: center;
margin: 24px 32px 32px;
}

.connectStudentText {
font-family: "Inter";
font-weight: bold;
font-size: 24px;
text-align: center;
vertical-align: Top;
color: #959595;
}

.buttonContainer {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
}
56 changes: 56 additions & 0 deletions components/Profile/ParentProfile/ConnectStudentProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from "react";
import styles from "./ConnectStudentProfile.module.css";
import IconButton from "@mui/material/IconButton";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
import AddIcon from "@mui/icons-material/Add";

type ConnectStudentProfileProps = {
setShowModalState: (newState: boolean) => void;
incrementStudent: () => void;
decrementStudent: () => void;
};

// card to connect a student to the current parent. Main function is to open the modal
export const ConnectStudentProfile: React.FC<ConnectStudentProfileProps> = ({
setShowModalState,
incrementStudent,
decrementStudent,
}) => {
return (
<div className={styles.createStudentContainer}>
<div className={styles.connectStudentContainer}>
<div className={styles.connectStudentText}> Connect Your Student</div>
<div>
<IconButton onClick={() => decrementStudent()}>
<ArrowBackIcon></ArrowBackIcon>
</IconButton>
<IconButton
onClick={() => {
incrementStudent();
}}
>
<ArrowForwardIcon></ArrowForwardIcon>
</IconButton>
</div>
</div>
<div className={styles.buttonContainer}>
<div>
<IconButton
sx={{
"& .MuiSvgIcon-fontSizeMedium": {
width: 80,
height: 80,
},
}}
size="large"
color="primary"
onClick={() => setShowModalState(true)}
>
<AddIcon />
</IconButton>
</div>
</div>
</div>
);
};
Loading

0 comments on commit 201899a

Please sign in to comment.