Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,211 changes: 2,054 additions & 157 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import ProtectedRoute from "./router/ProtectedRoute";
import { ROLE } from "./utils/interfaces";
import AssignReviewer from "./pages/Assignments/AssignReviewer";
import StudentTasks from "./pages/StudentTasks/StudentTasks";
import AssignedReviews from "./pages/StudentTasks/AssignedReviews";
import StudentTeams from "./pages/Student Teams/StudentTeamView";
import StudentTeamView from "./pages/Student Teams/StudentTeamView";
import NewTeammateAdvertisement from './pages/Student Teams/NewTeammateAdvertisement';
Expand All @@ -72,6 +73,7 @@ function App() {
{
path: "edit-questionnaire",
element: <ProtectedRoute element={<Questionnaire />} />,
loader: loadQuestionnaire,
},

{
Expand Down Expand Up @@ -293,6 +295,10 @@ function App() {
path: "email_the_author",
element: <Email_the_author />,
},
{
path: "student_tasks/reviews",
element: <ProtectedRoute element={<AssignedReviews />} />,
},
{
path: "student_tasks",
element: <ProtectedRoute element={<StudentTasks />} />,
Expand All @@ -301,6 +307,10 @@ function App() {
path: "student_tasks/:assignmentId",
element: <ProtectedRoute element={<StudentTasks />} />,
},
{
path: "student_tasks/:assignmentId/reviews",
element: <ProtectedRoute element={<AssignedReviews />} />,
},
{
path: "assignments/:id/review",
element: <ReviewReportPage />,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Form/FormSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const FormSelect: React.FC<IFormPropsWithOption & { onChange?: (event: React.Cha
>
{options.map((option) => {
return (
<option key={option.value} value={option.value}>
<option key={option.value} value={option.value} disabled={option.value === ""}>
{option.label}
</option>
);
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useSignupSheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const useSignupSheet = (assignmentId: string) => {

// Fetch sign up topics for the assignment
const topicsResponse = await axios.get<SignUpTopic[]>(
`${API_BASE_URL}/sign_up_topics`,
`${API_BASE_URL}/project_topics`,
{
params: { assignment_id: assignmentId },
headers,
Expand Down
37 changes: 35 additions & 2 deletions src/pages/Assignments/AssignReviewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import React, { useMemo, useState } from "react";
import { Container, Row, Col, Form, Button } from "react-bootstrap";
import { useLocation, useParams } from "react-router-dom";
import axiosClient from "../../utils/axios_client";

type Id = number;
type ReviewStatus = "Not saved" | "Saved" | "Submitted";
Expand Down Expand Up @@ -395,13 +396,16 @@ const AssignReviewer: React.FC = () => {
setTimeout(() => setTick(v => v + 1), 0);
}

function onAddReviewer(teamId: number) {
async function onAddReviewer(teamId: number) {
if (!hasValidId) return;
const raw = window.prompt("Enter reviewer user_id to add for this team:");
if (!raw) return;
const reviewerUserId = Number(raw);
if (!Number.isFinite(reviewerUserId)) { window.alert("Invalid user_id."); return; }

// Track the locally-assigned map id so we can patch it after the backend responds
let localMapId: number | null = null;

mutate(p => {
let reviewerPart = p.participants.find(x => x.user_id === reviewerUserId && x.parent_id === assignmentId);
if (!reviewerPart) {
Expand All @@ -412,15 +416,40 @@ const AssignReviewer: React.FC = () => {
p.users.push({ id: reviewerUserId, name: `user_${reviewerUserId}`, full_name: `user_${reviewerUserId}` });
}
}
localMapId = p.nextMapId++;
p.response_maps.push({
id: p.nextMapId++,
id: localMapId,
reviewed_object_id: assignmentId,
reviewer_id: reviewerPart.id,
reviewer_user_id: reviewerUserId,
reviewee_id: teamId,
reviewee_team_id: teamId,
});
});

// Persist the response map to the backend and patch localStorage with the real DB id
try {
const res = await axiosClient.post('/response_maps', {
assignment_id: assignmentId,
reviewer_user_id: reviewerUserId,
reviewee_team_id: teamId,
});
const realMapId: number = res.data.id;
const realParticipantId: number = res.data.reviewer_id;

if (localMapId !== null) {
mutate(p => {
const map = p.response_maps.find(m => m.id === localMapId);
if (map) {
map.id = realMapId;
map.reviewer_id = realParticipantId;
}
p.responses.forEach(r => { if (r.map_id === localMapId!) r.map_id = realMapId; });
});
}
} catch (err) {
console.warn('Failed to persist response map to backend — local ID will be used:', err);
}
}

function onDeleteReviewer(_teamId: number, mappingId: number) {
Expand All @@ -429,6 +458,8 @@ const AssignReviewer: React.FC = () => {
p.response_maps = p.response_maps.filter(m => m.id !== mappingId);
p.responses = p.responses.filter(r => r.map_id !== mappingId);
});
// Also delete from backend DB
axiosClient.delete(`/response_maps/${mappingId}`).catch(() => {});
}

function onUnsubmit(_teamId: number, mappingId: number) {
Expand All @@ -448,6 +479,8 @@ const AssignReviewer: React.FC = () => {
);
p.response_maps = p.response_maps.filter(m => !ids.has(m.id));
p.responses = p.responses.filter(r => !ids.has(r.map_id));
// Also delete from backend DB
ids.forEach(id => axiosClient.delete(`/response_maps/${id}`).catch(() => {}));
});
}

Expand Down
Loading