Skip to content

Commit

Permalink
Merge pull request #4 from hack4impact-upenn/joseph-and-caroline/stud…
Browse files Browse the repository at this point in the history
…ent-routes

Teacher Homepage
  • Loading branch information
annabay04 authored Oct 1, 2023
2 parents ce2a064 + 42e8d2d commit 47703e5
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 4 deletions.
2 changes: 2 additions & 0 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import ResetPasswordPage from './Authentication/ResetPasswordPage';
import AlertPopup from './components/AlertPopup';
import InviteRegisterPage from './Authentication/InviteRegisterPage';
import Header from './components/PageHeader';
import TeacherDashboard from './TeacherDashboard';

function App() {
return (
Expand All @@ -38,6 +39,7 @@ function App() {
{/* Routes accessed only if user is not authenticated */}
<Route element={<UnauthenticatedRoutesWrapper />}>
<Route path="/login" element={<LoginPage />} />
<Route path="/teacher" element={<TeacherDashboard />} />
<Route path="/register" element={<RegisterPage />} />
<Route
path="/verify-account/:token"
Expand Down
89 changes: 89 additions & 0 deletions client/src/TeacherDashboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from 'react';
import Box from '@mui/material/Box';
import Paper from '@mui/material/Paper';
import { styled } from '@mui/system';
// eslint-disable-next-line
import { useData } from './util/api';
import StudentCard from './components/buttons/StudentCard';
import PageHeader from './components/PageHeader';

const ScrollableBox = styled(Box)({
overflowY: 'auto',
maxHeight: '100%',
});

// // eslint-disable-next-line
// function createStudentCard(student: any) {
// const id = student.user_id;
// console.log(id)
// const user = useData(`users/${id}`);
// console.log(user)
// if (user) {
// const info = user.data;
// const name = `${info.firstName} ${info.lastName}`;
// const lesson = student.lesson_level;
// return <StudentCard studentName={name} lesson={lesson} />;
// }
// }

// eslint-disable-next-line
function createData(data: any) {
return data.map((student: any) => {
return <StudentCard studentID={student.user_id} lesson="Lesson 1" />;
});
}

function SplitGrid() {
const id = '111';
const students = useData(`student/teacher/${id}`);
const studentData = students?.data ?? [];

// const student_users = []
// for (let i = 0; i < studentData.length; i++) {
// const user_id = studentData[i].user_id;
// const user = useData(`users/${id}`);
// student_users.push(user);
// }

console.log(studentData);

return (
<Box display="flex" flexDirection="column" width="100%" height="100vh">
<PageHeader />

<Box display="flex" flexGrow={5}>
<Paper
sx={{
width: '30%',
overflowY: 'auto',
maxHeight: 'calc(100vh - 64px)', // Subtract the Toolbar height (default is 64px)
bgcolor: 'white',
p: 2,
}}
elevation={0}
square
>
<h2>Students</h2>
{createData(studentData)}
</Paper>

<Paper
sx={{
width: '70%',
overflowY: 'auto',
maxHeight: 'calc(100vh - 64px)', // Subtract the Toolbar height (default is 64px)
bgcolor: '#EDEDED',
p: 2,
paddingX: 4,
}}
elevation={0}
square
>
<h2>Class Progress</h2>
</Paper>
</Box>
</Box>
);
}

export default SplitGrid;
52 changes: 52 additions & 0 deletions client/src/components/buttons/StudentCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* eslint-disable react/jsx-no-bind */
import { CardActionArea, CardContent, Typography, Card } from '@mui/material';
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useData } from '../../util/api';

type StudentCardProps = {
studentID: string;
lesson: string;
};

function StudentCard({ studentID, lesson }: StudentCardProps) {
const navigate = useNavigate();
console.log(studentID);
const user = useData(`user/${studentID}`);
console.log(user);
let label = 'Name';
if (user) {
const info = user.data;
const name = `${info.firstName} ${info.lastName}`;
label = name;
}

function handleClick() {
// const s = `/city-dashboard/${cityName}`;
// navigate(s);
}

return (
<Card sx={{ p: 2, bgcolor: '#EDEDED', mb: 2, borderRadius: '8px' }}>
<CardActionArea onClick={handleClick}>
<CardContent>
<Typography variant="h5">{label}</Typography>
<Typography
sx={{
color: '#0175C0',
display: 'flex',
alignItems: 'center',
}}
variant="subtitle1"
>
<b>
<i>{lesson}</i>
</b>
</Typography>
</CardContent>
</CardActionArea>
</Card>
);
}

export default StudentCard;
39 changes: 39 additions & 0 deletions server/src/controllers/student.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* All the controller functions containing the logic for routes relating to
* admin users such as getting all users, deleting users and upgrading users.
*/
import express from 'express';
import ApiError from '../util/apiError';
import StatusCode from '../util/statusCode';
import { getAllStudentsFromDB } from '../services/student.service';

/**
* Get students by teacher_id
*/
const getStudentsFromTeacherId = async (
req: express.Request,
res: express.Response,
next: express.NextFunction,
) => {
const { id } = req.params;

if (!id) {
next(ApiError.internal('Request must include a valid teacher_id param'));
}

return (
getAllStudentsFromDB()
.then((studentList) => {
return studentList.filter((student) => student.teacher_id === id);
})
.then((filteredList) => {
res.status(StatusCode.OK).send(filteredList);
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.catch((e) => {
next(ApiError.internal('Unable to retrieve all users'));
})
);
};

export { getStudentsFromTeacherId };
33 changes: 33 additions & 0 deletions server/src/controllers/user.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* All the controller functions containing the logic for routes relating to
* admin users such as getting all users, deleting users and upgrading users.
*/
import express from 'express';
import ApiError from '../util/apiError';
import StatusCode from '../util/statusCode';
import { getUserById } from '../services/user.service';

// get a specific student
const getUser = async (
req: express.Request,
res: express.Response,
next: express.NextFunction,
) => {
const { id } = req.params;
if (!id) {
next(ApiError.internal('Request must include a valid userID param'));
}

return (
getUserById(id)
.then((user) => {
res.status(StatusCode.OK).send(user);
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.catch((e) => {
next(ApiError.internal('Unable to retrieve specified student'));
})
);
};

export { getUser };
33 changes: 33 additions & 0 deletions server/src/models/student.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@ const StudentSchema = new mongoose.Schema({
type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'School' }],
required: true,
},
teacher_id: {
type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
required: true,
},
coach_id: {
type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
required: true,
},
block_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Block',
required: true,
},
lesson_level: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Lesson',
Expand Down Expand Up @@ -65,12 +74,36 @@ const StudentSchema = new mongoose.Schema({
type: String,
required: false,
},
parent_name: {
type: String,
required: true,
},
parent_commmunication_days: {
type: String,
enum: ['weekends', 'weekdays', 'any'],
required: false,
},
parent_communication_times: {
type: String,
enum: ['morning', 'afternoon', 'evening'],
required: false,
},
media_waiver: {
type: Boolean,
required: true,
default: false,
},
work_habits: {
type: String,
required: false,
},
});

interface IStudent extends mongoose.Document {
_id: string;
user_id: string;
school_id: [string];
teacher_id: [string];
coach_id: [string];
block_id: string;
lesson_level: string;
Expand Down
11 changes: 8 additions & 3 deletions server/src/routes/routers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ The prefix should be of the form '/api/ROUTERNAME'
import { Router } from 'express';
import adminRouter from './admin.route';
import authRouter from './auth.route';
import thumbnailRouter from './thumbnail.route';
import studentRouter from './student.route';
import userRouter from './user.route';

const prefixToRouterMap: { prefix: string; router: Router }[] = [
{
Expand All @@ -21,8 +22,12 @@ const prefixToRouterMap: { prefix: string; router: Router }[] = [
router: adminRouter,
},
{
prefix: '/api/thumbnail',
router: thumbnailRouter,
prefix: '/api/student',
router: studentRouter,
},
{
prefix: '/api/user',
router: userRouter,
},
];

Expand Down
19 changes: 19 additions & 0 deletions server/src/routes/student.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Specifies the middleware and controller functions to call for each route
* relating to admin users.
*/
import express from 'express';
import { isAdmin } from '../controllers/admin.middleware';
import { getStudentsFromTeacherId } from '../controllers/student.controller';
import { isAuthenticated } from '../controllers/auth.middleware';
import 'dotenv/config';

const router = express.Router();

/**
* A GET route to get all users. Checks first if the requestor is a
* authenticated and is an admin.
*/
router.get('/teacher/:id', isAuthenticated, isAdmin, getStudentsFromTeacherId);

export default router;
2 changes: 1 addition & 1 deletion server/src/routes/thumbnail.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const getPageThumbnailUrl = async (url: string) => {
};

/**
* HTTP handler for retrieving a thumbnail from a url
* HTTP handler for retrieving a thumbnail from a url
*/
const retrieveThumbnail: RequestHandler = async (req, res) => {
const { url } = req.body;
Expand Down
18 changes: 18 additions & 0 deletions server/src/routes/user.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Specifies the middleware and controller functions to call for each route
* relating to admin users.
*/
import express from 'express';
import { isAdmin } from '../controllers/admin.middleware';
import { getUser } from '../controllers/user.controller';
import { isAuthenticated } from '../controllers/auth.middleware';
import 'dotenv/config';

const router = express.Router();

/**
* A GET route to get a user by their ID
*/
router.get('/:id', isAuthenticated, isAdmin, getUser);

export default router;
14 changes: 14 additions & 0 deletions server/src/services/student.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* All the functions for interacting with user data in the MongoDB database
*/
import { Student } from '../models/student.model';

/**
* @returns All the {@link Student}s in the database.
*/
const getAllStudentsFromDB = async () => {
const studentList = await Student.find({}).exec();
return studentList;
};

export { getAllStudentsFromDB };

0 comments on commit 47703e5

Please sign in to comment.