Skip to content

Commit

Permalink
Merge branch 'main' into fix-add-staff
Browse files Browse the repository at this point in the history
  • Loading branch information
KCCPMG committed Apr 9, 2024
2 parents 4281a32 + 3ac6b58 commit a69c9bb
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 38 deletions.
7 changes: 4 additions & 3 deletions src/backend/routers/case_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,14 @@ export const case_manager = router({
const { userId } = req.ctx.auth; // case manager id

// Check if the student exists and if the case manager is assigned to the student
const existingStudent = req.ctx.db
const existingStudent = await req.ctx.db
.selectFrom("student")
.selectAll()
.where("student_id", "=", student_id)
.where("assigned_case_manager_id", "=", userId);
.where("assigned_case_manager_id", "=", userId)
.execute();

if (!existingStudent) {
if (!existingStudent[0]) {
throw new Error("Student not found");
}

Expand Down
53 changes: 53 additions & 0 deletions src/backend/routers/student.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import test from "ava";
import { getTestServer } from "@/backend/tests";
import { parseISO } from "date-fns";

test("getStudentById", async (t) => {
const { trpc, db, seed } = await getTestServer(t, {
Expand Down Expand Up @@ -81,6 +82,58 @@ test("addIep and getIep", async (t) => {
t.deepEqual(got[0].end_date, added.end_date);
});

test("editIep", async (t) => {
const { trpc, seed } = await getTestServer(t, {
authenticateAs: "case_manager",
});

// * must add student this way to populate the assigned_case_manager_id
await trpc.case_manager.addStudent.mutate({
first_name: seed.student.first_name,
last_name: seed.student.last_name,
email: seed.student.email,
grade: seed.student.grade,
});

const myStudents = await trpc.case_manager.getMyStudents.query();
const student_id = myStudents[0].student_id;

const iep = await trpc.student.addIep.mutate({
student_id: student_id,
start_date: new Date("2023-01-01"),
end_date: new Date("2023-12-31"),
});

let got = await trpc.student.getIeps.query({
student_id: student_id,
});

t.is(got.length, 1);
t.is(got[0].student_id, iep.student_id);
t.deepEqual(got[0].start_date, iep.start_date);
t.deepEqual(got[0].end_date, iep.end_date);

const updated_start_date = new Date(parseISO("2023-03-02"));
const updated_end_date = new Date(parseISO("2024-03-01"));

await trpc.student.editIep.mutate({
student_id: student_id,
start_date: updated_start_date,
end_date: updated_end_date,
});

got = await trpc.student.getIeps.query({
student_id: student_id,
});

t.is(got.length, 1);
t.is(got[0].student_id, student_id);
t.notDeepEqual(got[0].start_date, iep.start_date);
t.notDeepEqual(got[0].end_date, iep.end_date);
t.deepEqual(got[0].start_date, updated_start_date);
t.deepEqual(got[0].end_date, updated_end_date);
});

test("getActiveStudentIep - return only one iep object", async (t) => {
const { trpc, seed } = await getTestServer(t, {
authenticateAs: "case_manager",
Expand Down
37 changes: 37 additions & 0 deletions src/backend/routers/student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,43 @@ export const student = router({
return result;
}),

/**
* Adds a new IEP for the given student.
*/
editIep: authenticatedProcedure
.input(
z.object({
student_id: z.string(),
start_date: z.date(),
end_date: z.date(),
})
)
.mutation(async (req) => {
const { student_id, start_date, end_date } = req.input;
const { userId } = req.ctx.auth; // case manager id

// Check if the student exists and if the case manager is assigned to the student
const existingStudent = await req.ctx.db
.selectFrom("student")
.selectAll()
.where("student_id", "=", student_id)
.where("assigned_case_manager_id", "=", userId)
.execute();

if (!existingStudent[0]) {
throw new Error("Student not found under this Case Manager");
}

await req.ctx.db
.updateTable("iep")
.set({
start_date: start_date,
end_date: end_date,
})
.where("student_id", "=", student_id)
.execute();
}),

/**
* Returns all the IEPs associated with the given student.
*/
Expand Down
21 changes: 21 additions & 0 deletions src/components/design_system/button/Button.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,27 @@
background-color: var(--primary-container);
}

.tertiary {
display: block;
color: var(--primary-50);
padding: 10px 12px;
border-radius: 8px;
cursor: pointer;
text-decoration: underline;
text-align: center;
font-size: var(--base-size);
background-color: inherit;
font-family: var(--quicksand);
text-transform: none;
line-height: normal;
letter-spacing: 0;
min-width: 0;
}

.tertiary:hover {
background-color: var(--primary-container);
}

.about {
border: none;
background-color: transparent;
Expand Down
125 changes: 90 additions & 35 deletions src/pages/students/[student_id].tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import { trpc } from "@/client/lib/trpc";
import $button from "@/components/design_system/button/Button.module.css";
import $home from "@/styles/Home.module.css";
import $input from "@/styles/Input.module.css";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Container from "@mui/material/Container";
import Modal from "@mui/material/Modal";
import Stack from "@mui/material/Stack";
import { Box, Button, Container, Modal, Stack } from "@mui/material";
import { addYears, format, parseISO, subDays } from "date-fns";
import Image from "next/image";
import { useRouter } from "next/router";
import { useState } from "react";
import Iep from "../../components/iep/Iep";
import noGoals from "../../public/img/no-goals-icon.png";
import $Form from "../../styles/Form.module.css";
import Image from "next/image";
import $Image from "../../styles/Image.module.css";
import $button from "@/components/design_system/button/Button.module.css";
import $Form from "../../styles/Form.module.css";
import $input from "@/styles/Input.module.css";
import $Modal from "../../styles/Modal.module.css";
import $StudentPage from "../../styles/StudentPage.module.css";

Expand All @@ -33,6 +28,11 @@ const ViewStudentPage = () => {

const handleEditState = () => {
setViewState(VIEW_STATES.EDIT);
if (activeIep) {
// * Populates the Edit form with iep startDate and endDate
setStartDate(activeIep.start_date.toISOString().slice(0, 10));
setEndDate(activeIep.end_date.toISOString().slice(0, 10));
}
};

const handleMainState = () => {
Expand Down Expand Up @@ -61,20 +61,32 @@ const ViewStudentPage = () => {
onSuccess: () => utils.student.getStudentById.invalidate(),
});

const editIepMutation = trpc.student.editIep.useMutation({
onSuccess: () => utils.student.getActiveStudentIep.invalidate(),
});

const handleEditStudent = (e: React.ChangeEvent<HTMLFormElement>) => {
e.preventDefault();
const data = new FormData(e.currentTarget);

if (!student) {
return; // TODO: improve error handling
}

editMutation.mutate({
student_id: student.student_id,
first_name: data.get("firstName") as string,
last_name: data.get("lastName") as string,
email: data.get("email") as string,
grade: Number(data.get("grade")) || 0,
});

editIepMutation.mutate({
student_id: student.student_id,
start_date: new Date(parseISO(data.get("start_date") as string)),
end_date: new Date(parseISO(data.get("end_date") as string)),
});

handleMainState();
};

Expand Down Expand Up @@ -109,6 +121,7 @@ const ViewStudentPage = () => {
if (!student) {
return; // TODO: improve error handling
}

iepMutation.mutate({
student_id: student.student_id,
start_date: new Date(parseISO(data.get("start_date") as string)),
Expand Down Expand Up @@ -145,32 +158,23 @@ const ViewStudentPage = () => {

{/* Edit button only to be shown when view state is set to MAIN */}
{viewState === VIEW_STATES.MAIN && (
<Button
className={`${$button.secondary}`}
onClick={handleEditState}
>
Edit
</Button>
)}

{/* Save and Cancel buttons only to be shown when view state is set to EDIT */}
{viewState === VIEW_STATES.EDIT && (
<Box className={$StudentPage.displayBoxGap}>
<Button
onClick={handleMainState}
className={`${$button.secondary}`}
onClick={() => setArchivePrompt(true)}
className={`${$button.tertiary}`}
>
Cancel
Archive
</Button>
<Button
className={`${$button.default}`}
type="submit"
form="edit"
className={`${$button.secondary}`}
onClick={handleEditState}
>
Save
Edit
</Button>
</Box>
)}

{/* Save and Cancel buttons only to be shown when view state is set to EDIT */}
</Box>

{/* if view state is "EDIT" then show the edit version of the student page */}
Expand All @@ -184,11 +188,21 @@ const ViewStudentPage = () => {
<p className={$StudentPage.centerText}>{student?.grade}</p>
</div>
<div className={$StudentPage.singleInfoArea}>
<p>Next IEP</p>
<p>IEP Start Date</p>
<p className={$StudentPage.centerText}>
{activeIep?.start_date.toLocaleDateString() ?? "None"}
</p>
</div>
<div className={$StudentPage.singleInfoArea}>
<p>IEP End Date</p>
<p className={$StudentPage.centerText}>
{activeIep?.end_date.toLocaleDateString() ?? "None"}
</p>
</div>
<div className={$StudentPage.singleInfoArea}>
<p>Email ID</p>
<p className={$StudentPage.centerText}>{student?.email}</p>
</div>
</Box>
</Box>
)}
Expand Down Expand Up @@ -234,6 +248,22 @@ const ViewStudentPage = () => {
required
/>
</Container>
<Container
className={$StudentPage.studentEditContainer}
sx={{
display: "grid",
gridTemplateColumns: "200px 30px 300px",
}}
>
<label>Email</label>
<p>:</p>
<input
type="text"
name="email"
defaultValue={student?.email || ""}
required
/>
</Container>
<Container
className={$StudentPage.studentEditContainer}
sx={{
Expand All @@ -257,25 +287,50 @@ const ViewStudentPage = () => {
gridTemplateColumns: "200px 30px 300px",
}}
>
<label>Email</label>
<label>IEP Start Date</label>
<p>:</p>
<input
type="text"
name="email"
defaultValue={student?.email || ""}
type="date"
name="start_date"
defaultValue={startDate}
onChange={(e) => setStartDate(e.target.value)}
required
/>
</Container>
<Container
className={$StudentPage.studentEditContainer}
sx={{
display: "grid",
gridTemplateColumns: "200px 30px 300px",
}}
>
<label>IEP End Date</label>
<p>:</p>
<input
type="date"
name="end_date"
defaultValue={endDate}
min={startDate}
required
/>
</Container>
</Stack>
</form>

<Container sx={{ marginTop: "2rem" }}>
<Box textAlign="center">
<Box className={$StudentPage.displayBoxButtons}>
<Button
onClick={handleMainState}
className={`${$button.secondary}`}
>
Cancel
</Button>
<Button
onClick={() => setArchivePrompt(true)}
className={`${$button.default}`}
type="submit"
form="edit"
>
Archive {student?.first_name} {student?.last_name}
Save
</Button>
</Box>
</Container>
Expand Down
7 changes: 7 additions & 0 deletions src/styles/StudentPage.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
gap: 1rem;
}

.displayBoxButtons {
display: flex;
justify-content: flex-end;
padding: 10px 0;
gap: 1rem;
}

.studentName {
font-size: xx-large;
}
Expand Down

0 comments on commit a69c9bb

Please sign in to comment.