Skip to content

Commit

Permalink
Merge pull request #20 from DakshChan/feature/IBS-11
Browse files Browse the repository at this point in the history
[IBS-11] - Group List Page
  • Loading branch information
Kianoosh76 committed Jun 28, 2023
2 parents 21835a1 + b1564ff commit 33d6790
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 12 deletions.
8 changes: 8 additions & 0 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import ThemeSettings from './layouts/full-layout/customizer/ThemeSettings';
import { useSelector } from 'react-redux';
import RTL from './layouts/full-layout/customizer/RTL';
import StudentListPage from './components/Page/Student/StudentListPage';
import TaskGroupListPage from './components/General/TaskGroupList/TaskGroupListPage';
import SubmitMarks from './components/Page/Instructor/SubmitMarks';
import TaskGroupPage from './components/Page/Instructor/TaskGroupPage';

Expand Down Expand Up @@ -74,7 +75,14 @@ function App() {
<Route
path="/instructor/course/:course_id/task"
element={<InstructorTaskPage />}
>
</Route>

<Route
path="/instructor/course/:courseId/task/:taskId/groups"
element={<TaskGroupListPage />}
/>

<Route
path="/instructor/course/:course_id/impersonate"
element={<InstructorImpersonate />}
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/Constants/roles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const ROLE = "role";
export const STUDENT = "student"
export const TA = "ta"
export const INSTRUCTOR = "instructor"
export const ADMIN = "admin"
72 changes: 60 additions & 12 deletions frontend/src/api/instructor_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,19 +127,67 @@ let submitMark = async (courseId, task, criteria, username, mark) => {
// }
// };

let allTasks = async (course_id) => {
let token = sessionStorage.getItem("token");

let config = {
headers: { Authorization: `Bearer ${token}` }
};

try {
return await axios.get(process.env.REACT_APP_API_URL + "/instructor/course/" + course_id + "/task/all", config);
} catch (err) {
return err.response;
}
};

let allGroups = async (course_id, task) => {
let token = sessionStorage.getItem("token")

let config = {
headers: { Authorization: `Bearer ${token}` }
};

try {
return await axios.get(process.env.REACT_APP_API_URL + "/instructor/course/" + course_id + "/group/all?task=" + task, config);
} catch (err) {
return err.response;
}
}

let taskGroups = async (course_id, task) => {
/**
* Gets all groups for a particular task within a course.
*/
let token = sessionStorage.getItem("token");

let config = {
headers: { Authorization: `Bearer ${token}` }
};

try {
return await axios.get(process.env.REACT_APP_API_URL + "/instructor/course/" + course_id + "/group/all?task=" + task, config);
} catch (err) {
return err.response;
}
}


let InstructorApi = {
// Task related
all_tasks,
impersonate,
submitMark
//
// // Group related
// check_group,
//
// // Interview related
// all_interviews,
// schedule_interview,
// delete_interview,
//Course related
allTasks,
// Task related
impersonate,
taskGroups,
//
// // Group related
// check_group,
allGroups,
//
// // Interview related
// all_interviews,
// schedule_interview,
// delete_interview,
};

export default InstructorApi;
15 changes: 15 additions & 0 deletions frontend/src/api/ta_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ let check_group = async (course_id, group_id) => {
}
};

let allGroups = async (course_id, task) => {
let token = sessionStorage.getItem("token")

let config = {
headers: { Authorization: `Bearer ${token}` }
};

try {
return await axios.get(process.env.REACT_APP_API_URL + "/ta/course/" + course_id + "/group/all?task=" + task, config);
} catch (err) {
return err.response;
}
}

let all_interviews = async (course_id, curr_task) => {
let token = sessionStorage.getItem("token");

Expand Down Expand Up @@ -83,6 +97,7 @@ let TaApi = {

// Group related
check_group,
allGroups,

// Interview related
all_interviews,
Expand Down
43 changes: 43 additions & 0 deletions frontend/src/components/General/TaskGroupList/TaskGroupCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import { Card, CardActionArea, CardContent, CardMedia, Typography } from '@mui/material';
import { Link } from 'react-router-dom';
import HomeCardLink from './HomeCardLink';

const Homecard = ({ data }) => {
const role = data.role === undefined || data.role === 'student' ? '' : data.role;
const staffRoles = ['admin', 'ta', 'instructor'];

const coursePageLink = (role ? '/' + role : '') + '/course/' + data.course_id + '/task';
const courseStudentListPageLink = `/course/${data.course_id}/student-list`;

return (
<Card>
<CardActionArea component={Link} to={coursePageLink}>
<CardMedia
component="img"
height="200"
src={require('../../../images/general.png')}
alt="course"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{data.course_code}
</Typography>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Typography variant="body2" color="text.secondary">
{data.course_session.replaceAll('_', ' ')}
</Typography>
<Typography variant="body2" color="text.secondary">
{data?.role?.charAt(0)?.toUpperCase() + data?.role?.slice(1)}
</Typography>
</div>
</CardContent>
</CardActionArea>
{staffRoles.includes(data.role) && (
<HomeCardLink to={courseStudentListPageLink} name="Enrolled Students" />
)}
</Card>
);
};

export default Homecard;
38 changes: 38 additions & 0 deletions frontend/src/components/General/TaskGroupList/TaskGroupList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { Children } from 'react';
import BaseCard from '../../FlexyMainComponents/base-card/BaseCard';
import InstructorApi from '../../../api/instructor_api';
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router';
import { Grid } from '@mui/material';

const TaskGroupList = ({ courseId, task }) => {
let groups = [];
InstructorApi.allGroups(courseId, task)
.then((res) => {
res.groups.forEach((group) => {
groups.push({ id: group.id, members: group.users });
});
})
.catch((err) => {
console.log(err);
});

return (
<Grid
item
container
spacing={2}
direction="row"
justifyContent="center"
alignContent="center"
justify="center"
flex="1 1 auto"
>
{groups.map((group) => (
<BaseCard title={group.id} children={group.members} />
))}
</Grid>
);
};

export default TaskGroupList;
106 changes: 106 additions & 0 deletions frontend/src/components/General/TaskGroupList/TaskGroupListPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React, { useState, useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { toast } from 'react-toastify';
import InstructorApi from '../../../api/instructor_api';
import { Grid } from '@mui/material';
import { TextField } from '@mui/material';
import Autocomplete from '@mui/material/Autocomplete';
import { getCourses } from '../../../../utilities/courses';
import NavBar from '../../Module/Navigation/NavBar';
import { makeStyles } from '@mui/styles';
import TaskGroupList from './TaskGroupList';
import { getTasks } from '../../../../utilities/tasks';

const useStyles = makeStyles({
container: {
display: 'flex',
flexWrap: 'wrap',
alignItems: 'flex-start',
justifyContent: 'flex-start',
margin: '16px',
padding: '16px'
},

dropdown: {
display: 'inline-block'
}
});

const TaskGroupListPage = () => {
const classes = useStyles();
const courseOptions = getCourses();

const [course, setCourse] = useState('');
const [courseId, setCourseId] = useState(null);
const [task, setTask] = useState('');
const [taskOptions, setTaskOptions] = useState([]);

const handleCourseChange = (option) => {
setCourse(option.label);
setCourseId(option.course_id);
};

useEffect(() => {
if (course) {
const tasks = getTasks();
setTaskOptions(tasks);
}
}, [course]);

return (
<Grid container direction="column" height="100%" wrap="nowrap">
<NavBar item page="Home" />

<div className={classes.dropdown}>
<div>
<Autocomplete
disablePortal
id="course-selection-dropdown"
renderInput={(params) => (
<TextField
{...params}
size="small"
placeholder="Select Course"
aria-label="Select Course"
/>
)}
options={courseOptions}
onChange={(event, value) => handleCourseChange(value)}
/>
</div>
{task && (
<div>
<Autocomplete
disablePortal
id="task-selection-dropdown"
options={taskOptions}
renderInput={(params) => (
<TextField
{...params}
size="small"
placeholder="Select Task"
aria-label="Select Task"
/>
)}
onChange={(event, value) => setTask(value)}
/>
</div>
)}
</div>
<Grid
item
container
spacing={2}
direction="row"
justifyContent="center"
alignContent="center"
justify="center"
flex="1 1 auto"
>
<TaskGroupList courseId={courseId} task={task} />
</Grid>
</Grid>
);
};

export default TaskGroupListPage;
42 changes: 42 additions & 0 deletions frontend/utilities/courses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export const getCourses = (roles = []) => {
/** Gets all courses that a user belongs to with respect to the roles that they are in.
*
* @param roles An optional array of roles to validate whether a user belongs to a course
* with a given role.
*
* @returns An array of courses that the user belongs to: [{courseLabel: CSC108, courseId: 1}]
*/
const lowerCaseRoles = roles.map((role) => role.toLowerCase());
const courses = JSON.parse(sessionStorage.getItem('roles'));
let courseOptions = [];

courses.forEach((course) => {
// Filter for role specific courses
if (lowerCaseRoles && lowerCaseRoles.includes(course.role.toLowerCase())) {
courseOptions.push({ label: course.course_code, course_id: course.course_id });
} else {
courseOptions.push({ label: course.course_code, course_id: course.course_id });
}
});
return courseOptions;
};

export const getCourseIdFromName = (courseName) => {
/**
* Gets the course id associated with courseName
*
* @param courseName: The name of the course whos id to find
*
* @returns An int representing the course id or null if none exist.
*/

const courses = getCourses();
console.log(courseName);

courses.forEach((course) => {
if (courseName.toLowerCase() === course.label.toLowerCase()) {
return course.course_id;
}
});
return null;
};
22 changes: 22 additions & 0 deletions frontend/utilities/tasks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import InstructorApi from '../src/api/instructor_api';
export const getTasks = (courseId) => {
/** Gets all tasks in a course
*
* @param courseId The id of a course
*
* @return An array of objects containing the course code and id: [{courseCode: 'CSC108', courseId: '1'}]
*/

let tasks = [];
InstructorApi.allTasks(courseId)
.then((res) => {
res.task.forEach((task) => {
tasks.push({ label: task.task });
});
})
.catch((err) => {
console.log(err);
});

return tasks;
};
Loading

0 comments on commit 33d6790

Please sign in to comment.