Skip to content
Merged
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
68 changes: 41 additions & 27 deletions packages/alea-frontend/components/CourseAccessControlDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ import {
isValid,
updateResourceAction,
} from '@alea/spec';
import { Action, CURRENT_TERM, ResourceActionPair } from '@alea/utils';
import { Action, ResourceActionPair } from '@alea/utils';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import AclDisplay from './AclDisplay';
import { useStudentCount } from '../hooks/useStudentCount';
import { useCurrentTermContext } from '../contexts/CurrentTermContext';
import {
createInstructorResourceActions,
createMetadataResourceActions,
Expand Down Expand Up @@ -75,56 +76,58 @@ const studentAccessResources: Record<ShortId, string> = {
'homework-take': 'Homework Take',
};

const getAclShortIdToResourceActionPair = (courseId: string) =>
const getAclShortIdToResourceActionPair = (courseId: string, currentTerm: string) =>
({
syllabus: {
resourceId: `/course/${courseId}/instance/${CURRENT_TERM}/syllabus`,
resourceId: `/course/${courseId}/instance/${currentTerm}/syllabus`,
actionId: Action.MUTATE,
},
quiz: {
resourceId: `/course/${courseId}/instance/${CURRENT_TERM}/quiz`,
resourceId: `/course/${courseId}/instance/${currentTerm}/quiz`,
actionId: Action.MUTATE,
},
'homework-crud': {
resourceId: `/course/${courseId}/instance/${CURRENT_TERM}/homework`,
resourceId: `/course/${courseId}/instance/${currentTerm}/homework`,
actionId: Action.MUTATE,
},
'homework-grading': {
resourceId: `/course/${courseId}/instance/${CURRENT_TERM}/homework`,
resourceId: `/course/${courseId}/instance/${currentTerm}/homework`,
actionId: Action.INSTRUCTOR_GRADING,
},
comments: {
resourceId: `/course/${courseId}/instance/${CURRENT_TERM}/comments`,
resourceId: `/course/${courseId}/instance/${currentTerm}/comments`,
actionId: Action.MODERATE,
},
'study-buddy': {
resourceId: `/course/${courseId}/instance/${CURRENT_TERM}/study-buddy`,
resourceId: `/course/${courseId}/instance/${currentTerm}/study-buddy`,
actionId: Action.MODERATE,
},
'quiz-preview': {
resourceId: `/course/${courseId}/instance/${CURRENT_TERM}/quiz`,
resourceId: `/course/${courseId}/instance/${currentTerm}/quiz`,
actionId: Action.PREVIEW,
},
'quiz-take': {
resourceId: `/course/${courseId}/instance/${CURRENT_TERM}/quiz`,
resourceId: `/course/${courseId}/instance/${currentTerm}/quiz`,
actionId: Action.TAKE,
},
'homework-take': {
resourceId: `/course/${courseId}/instance/${CURRENT_TERM}/homework`,
resourceId: `/course/${courseId}/instance/${currentTerm}/homework`,
actionId: Action.TAKE,
},
metadata: {
resourceId: `/course/${courseId}/instance/${CURRENT_TERM}/metadata`,
resourceId: `/course/${courseId}/instance/${currentTerm}/metadata`,
actionId: Action.MUTATE,
},
} as Record<ShortId, ResourceActionPair>);

const CourseAccessControlDashboard = ({ courseId }) => {
const CourseAccessControlDashboard = ({ courseId }: { courseId: string }) => {
const router = useRouter();
const { currentTermByCourseId, loadingTermByCourseId } = useCurrentTermContext();
const [semesterSetupLoading, setSemesterSetupLoading] = useState(false);
const [semesterSetupMessage, setSemesterSetupMessage] = useState('');
const [isAlreadySetup, setIsAlreadySetup] = useState(false);

const currentTerm = currentTermByCourseId[courseId];

async function checkIfAlreadySetup() {
const complete = await isCourseSemesterSetupComplete(courseId);
setIsAlreadySetup(!!complete);
Expand Down Expand Up @@ -177,7 +180,7 @@ const CourseAccessControlDashboard = ({ courseId }) => {
const [acls, setAcls] = useState<string[]>([]);
const [newAclId, setNewAclId] = useState('');
const [error, setError] = useState('');
const studentCount = useStudentCount(courseId, CURRENT_TERM);
const studentCount = useStudentCount(courseId, currentTerm);
const handleAclClick = (aclId: string) => {
router.push(`/acl/${aclId}`);
};
Expand All @@ -194,7 +197,8 @@ const CourseAccessControlDashboard = ({ courseId }) => {
};

const updateAclId = async (shortId: ShortId, aclId: string) => {
const aclShortIdToResourceActionPair = getAclShortIdToResourceActionPair(courseId);
if (!currentTerm) return;
const aclShortIdToResourceActionPair = getAclShortIdToResourceActionPair(courseId, currentTerm);
const resourceActionPair = aclShortIdToResourceActionPair[shortId];
const resourceId = resourceActionPair.resourceId;
const actionId = resourceActionPair.actionId;
Expand All @@ -215,7 +219,8 @@ const CourseAccessControlDashboard = ({ courseId }) => {

useEffect(() => {
async function getAclData() {
const aclShortIdToResourceActionPair = getAclShortIdToResourceActionPair(courseId);
if (!currentTerm) return;
const aclShortIdToResourceActionPair = getAclShortIdToResourceActionPair(courseId, currentTerm);
const resourceActionPairs = ALL_SHORT_IDS.map((sId) => aclShortIdToResourceActionPair[sId]);
const aclIds = await getSpecificAclIds(resourceActionPairs);

Expand All @@ -227,28 +232,29 @@ const CourseAccessControlDashboard = ({ courseId }) => {
setEditingValues({ ...aclData });
}
getAclData();
}, [courseId]);
}, [courseId, currentTerm]);

async function getAcls() {
const data = await getCourseAcls(courseId, CURRENT_TERM);
if (!currentTerm) return;
const data = await getCourseAcls(courseId, currentTerm);
setAcls(data);
}

useEffect(() => {
if (!courseId) return;
if (!courseId || !currentTerm) return;
getAcls();
}, [courseId]);
}, [courseId, currentTerm]);

useEffect(() => {
if (courseId) {
checkIfAlreadySetup();
}
}, [courseId]);
}, [courseId, currentTerm]);

async function handleCreateAclClick() {
if (!newAclId || !courseId) return;
const aclId = `${courseId}-${CURRENT_TERM}-${newAclId}`;
const updaterACLId = `${courseId}-${CURRENT_TERM}-instructors`;
if (!newAclId || !courseId || !currentTerm) return;
const aclId = `${courseId}-${currentTerm}-${newAclId}`;
const updaterACLId = `${courseId}-${currentTerm}-instructors`;
const res = await isValid(updaterACLId);
if (!res) {
setNewAclId('');
Expand All @@ -258,7 +264,7 @@ const CourseAccessControlDashboard = ({ courseId }) => {
try {
await createAcl({
id: aclId,
description: `${newAclId} for ${courseId} (${CURRENT_TERM})`,
description: `${newAclId} for ${courseId} (${currentTerm})`,
memberUserIds: [],
memberACLIds: [],
updaterACLId,
Expand All @@ -272,6 +278,14 @@ const CourseAccessControlDashboard = ({ courseId }) => {
getAcls();
}

if (loadingTermByCourseId) {
return (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="200px">
<Typography>Loading...</Typography>
</Box>
);
}

return (
<Box display="flex" flexDirection="column" maxWidth="900px" m="auto" p="20px" gap="20px">
<Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
Expand Down Expand Up @@ -377,7 +391,7 @@ const CourseAccessControlDashboard = ({ courseId }) => {
<Typography variant="h5">Create New ACL</Typography>
<Box sx={{ mb: 2, display: 'flex', alignItems: 'center' }}>
<Typography sx={{ ml: 1, fontSize: 14 }}>
{courseId ? `${courseId}-${CURRENT_TERM}-` : ''}
{courseId && currentTerm ? `${courseId}-${currentTerm}-` : ''}
</Typography>
<TextField
value={newAclId}
Expand Down
12 changes: 8 additions & 4 deletions packages/alea-frontend/components/CoverageTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import {
} from '@mui/material';
import { getAllQuizzes, QuizWithStatus } from '@alea/spec';
import { NoMaxWidthTooltip } from '@alea/stex-react-renderer';
import { CURRENT_TERM, LectureEntry } from '@alea/utils';
import { LectureEntry } from '@alea/utils';
import dayjs from 'dayjs';
import { useEffect, useState } from 'react';
import { useStudentCount } from '../hooks/useStudentCount';
import { useCurrentTermContext } from '../contexts/CurrentTermContext';
import { SecInfo } from '../types';
import { AutoDetectedTooltipContent } from './AutoDetectedComponent';
import { getSectionNameForUri } from './CoverageUpdater';
Expand Down Expand Up @@ -477,12 +478,15 @@ export function CoverageTable({
const missingTargetsCount = countMissingTargetsInFuture(entries);
const sortedEntries = [...entries].sort((a, b) => a.timestamp_ms - b.timestamp_ms);
const [quizMatchMap, setQuizMatchMap] = useState<QuizMatchMap>({});
const studentCount = useStudentCount(courseId, CURRENT_TERM);
const { currentTermByCourseId } = useCurrentTermContext();
const currentTerm = currentTermByCourseId[courseId];
const studentCount = useStudentCount(courseId, currentTerm);

useEffect(() => {
async function fetchQuizzes() {
if (!currentTerm) return;
try {
const allQuizzes = await getAllQuizzes(courseId, CURRENT_TERM);
const allQuizzes = await getAllQuizzes(courseId, currentTerm);
const map: QuizMatchMap = {};
entries.forEach((entry) => {
const match = allQuizzes.find(
Expand All @@ -496,7 +500,7 @@ export function CoverageTable({
}
}
fetchQuizzes();
}, [courseId]);
}, [courseId, currentTerm, entries]);

return (
<Box>
Expand Down
24 changes: 15 additions & 9 deletions packages/alea-frontend/components/ForumView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ import {
} from '@alea/spec';
import { MystEditor } from '@alea/myst';
import { DateView } from '@alea/react-utils';
import { CURRENT_TERM } from '@alea/utils';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useEffect, useReducer, useState } from 'react';
import { useCurrentTermContext } from '../contexts/CurrentTermContext';
import { getLocaleObject } from '../lang/utils';
function getDraftKey(courseId: string) {
return `question-draft-${courseId}`;
Expand Down Expand Up @@ -121,6 +121,9 @@ function ForumViewControls({
const { forum: t } = getLocaleObject(router);
const [openQuestionDialog, setOpenQuestionDialog] = useState(false);
const courseId = router.query?.courseId as string;
const { currentTermByCourseId } = useCurrentTermContext();
const currentTerm = currentTermByCourseId[courseId];

const [userInfo, setUserInfo] = useState<UserInfo | undefined>(undefined);
const [isUserAuthorized, setIsUserAuthorized] = useState<boolean>(false);

Expand All @@ -129,9 +132,9 @@ function ForumViewControls({
}, []);

useEffect(() => {
if (!courseId) return;
canModerateComment(courseId, CURRENT_TERM).then(setIsUserAuthorized);
}, [courseId]);
if (!courseId || !currentTerm) return;
canModerateComment(courseId, currentTerm).then(setIsUserAuthorized);
}, [courseId, currentTerm]);

return (
<Box
Expand Down Expand Up @@ -187,7 +190,7 @@ function ForumViewControls({
addComment({
commentId: -1,
courseId,
courseTerm: CURRENT_TERM,
courseTerm: currentTerm,
isPrivate: false,
isAnonymous,
commentType: CommentType.QUESTION,
Expand Down Expand Up @@ -236,17 +239,20 @@ export function QuestionStatusIcon({ comment }: { comment: Comment }) {
export function ForumView() {
const router = useRouter();
const courseId = router.query.courseId as string;
const { currentTermByCourseId, loadingTermByCourseId } = useCurrentTermContext();
const currentTerm = currentTermByCourseId[courseId];

const [threadComments, setThreadComments] = useState<Comment[]>([]);
const [showRemarks, setShowRemarks] = useState(false);
const [showUnanswered, setShowUnanswered] = useState(false);
const [updateCounter, markUpdate] = useReducer((x) => x + 1, 0);

useEffect(() => {
if (!router.isReady || !courseId) return;
getCourseInstanceThreads(courseId, CURRENT_TERM).then(setThreadComments);
}, [courseId, router.isReady, updateCounter]);
if (!router.isReady || !courseId || !currentTerm) return;
getCourseInstanceThreads(courseId, currentTerm).then(setThreadComments);
}, [courseId, router.isReady, updateCounter, currentTerm]);

if (!router.isReady || !courseId) return <CircularProgress />;
if (!router.isReady || !courseId || loadingTermByCourseId) return <CircularProgress />;
const toShow = showUnanswered
? threadComments.filter((c) => c.questionStatus === QuestionStatus.UNANSWERED)
: showRemarks
Expand Down
7 changes: 5 additions & 2 deletions packages/alea-frontend/components/HomeworkManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import {
updateHomework,
UpdateHomeworkRequest,
} from '@alea/spec';
import { CURRENT_TERM } from '@alea/utils';
import dayjs from 'dayjs';
import { useCurrentTermContext } from '../contexts/CurrentTermContext';
import HomeworkForm from './HomeworkForm';
import HomeworkList from './HomeworkList';
import HomeworkStats from './HomeworkState';
Expand All @@ -42,6 +42,9 @@ function timestampEOD() {
}

const HomeworkManager = ({ courseId }) => {
const { currentTermByCourseId } = useCurrentTermContext();
const currentTerm = currentTermByCourseId[courseId];

const [homeworks, setHomeworks] = useState<HomeworkStub[]>([]);
const [stats, setStats] = useState<HomeworkStatsInfo | null>(null);
const [id, setId] = useState<number | null>(null);
Expand Down Expand Up @@ -104,7 +107,7 @@ const HomeworkManager = ({ courseId }) => {
const createRequest: CreateHomeworkRequest = {
...body,
courseId,
courseInstance: CURRENT_TERM,
courseInstance: currentTerm,
};
response = await createHomework(createRequest);
}
Expand Down
12 changes: 8 additions & 4 deletions packages/alea-frontend/components/QuizDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import { SafeHtml } from '@alea/react-utils';
import {
Action,
CoverageTimeline,
CURRENT_TERM,
LectureEntry,
ResourceName,
roundToMinutes,
Expand All @@ -41,6 +40,7 @@ import { AxiosResponse } from 'axios';
import dayjs from 'dayjs';
import type { NextPage } from 'next';
import { useEffect, useState } from 'react';
import { useCurrentTermContext } from '../contexts/CurrentTermContext';
import { CheckboxWithTimestamp } from './CheckBoxWithTimestamp';
import { EndSemSumAccordion } from './EndSemSumAccordion';
import { ExcusedAccordion } from './ExcusedAccordion';
Expand Down Expand Up @@ -179,12 +179,14 @@ interface QuizDashboardProps {

const QuizDashboard: NextPage<QuizDashboardProps> = ({ courseId, quizId, onQuizIdChange }) => {
const selectedQuizId = quizId || NEW_QUIZ_ID;
const { currentTermByCourseId, loadingTermByCourseId } = useCurrentTermContext();
const currentTerm = currentTermByCourseId[courseId];

const [title, setTitle] = useState<string>('');
const [quizStartTs, setQuizStartTs] = useState<number>(roundToMinutes(Date.now()));
const [quizEndTs, setQuizEndTs] = useState<number>(roundToMinutes(Date.now()));
const [feedbackReleaseTs, setFeedbackReleaseTs] = useState<number>(roundToMinutes(Date.now()));
const [courseTerm, setCourseTerm] = useState<string>(CURRENT_TERM);
const [courseTerm, setCourseTerm] = useState<string>('');
const [sections, setSections] = useState<SecInfo[]>([]);
const [coverageTimeline, setCoverageTimeline] = useState<CoverageTimeline>({});
const [upcomingQuizSyllabus, setUpcomingQuizSyllabus] = useState<{
Expand All @@ -207,6 +209,7 @@ const QuizDashboard: NextPage<QuizDashboardProps> = ({ courseId, quizId, onQuizI
const [canAccess, setCanAccess] = useState(false);
const [syllabusLoading, setSyllabusLoading] = useState(false);
const isNew = isNewQuiz(selectedQuizId);

const router = useRouter();

const selectedQuiz = quizzes.find((quiz) => quiz.id === selectedQuizId);
Expand Down Expand Up @@ -258,7 +261,7 @@ const QuizDashboard: NextPage<QuizDashboardProps> = ({ courseId, quizId, onQuizI
setTitle('');
setProblems({});
setCss([]);
setCourseTerm(CURRENT_TERM);
setCourseTerm(currentTerm);
return;
}

Expand All @@ -283,7 +286,7 @@ const QuizDashboard: NextPage<QuizDashboardProps> = ({ courseId, quizId, onQuizI
async function checkHasAccessAndGetTypeOfAccess() {
const canMutate = await canAccessResource(ResourceName.COURSE_QUIZ, Action.MUTATE, {
courseId,
instanceId: CURRENT_TERM,
instanceId: currentTerm,
});
if (canMutate) {
setAccessType('MUTATE');
Expand Down Expand Up @@ -355,6 +358,7 @@ const QuizDashboard: NextPage<QuizDashboardProps> = ({ courseId, quizId, onQuizI
}

if (!canAccess) return <>Unauthorized</>;
if (loadingTermByCourseId) return <CircularProgress />;

return (
<Box m="auto" maxWidth="800px" p="10px">
Expand Down
Loading