diff --git a/src/components/High/AssignmentCard.tsx b/src/components/High/AssignmentCard.tsx index d7427e5f..49812254 100644 --- a/src/components/High/AssignmentCard.tsx +++ b/src/components/High/AssignmentCard.tsx @@ -1,6 +1,7 @@ import { Session } from "@/hooks/useSessions"; import { Assignment } from "@/interfaces/results"; import { User } from "@/interfaces/user"; +import { activeAssignmentFilter, futureAssignmentFilter } from "@/utils/assignments"; import { sortByModuleName } from "@/utils/moduleUtils"; import clsx from "clsx"; import moment from "moment"; @@ -44,7 +45,16 @@ export default function AssignmentCard({ user, assignment, session, startAssignm ))} - {!assignment.results.map((r) => r.user).includes(user.id) && ( + {futureAssignmentFilter(assignment) && ( + + )} + {activeAssignmentFilter(assignment) && !assignment.results.map((r) => r.user).includes(user.id) && ( <>
Submitted diff --git a/src/dashboards/AssignmentCreator.tsx b/src/dashboards/AssignmentCreator.tsx index c3bdd792..89c58598 100644 --- a/src/dashboards/AssignmentCreator.tsx +++ b/src/dashboards/AssignmentCreator.tsx @@ -1,27 +1,27 @@ import Input from "@/components/Low/Input"; import Modal from "@/components/Modal"; -import {Module} from "@/interfaces"; +import { Module } from "@/interfaces"; import clsx from "clsx"; -import {useEffect, useMemo, useState} from "react"; -import {BsBook, BsCheckCircle, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle} from "react-icons/bs"; -import {generate} from "random-words"; -import {capitalize} from "lodash"; +import { useEffect, useMemo, useState } from "react"; +import { BsBook, BsCheckCircle, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle } from "react-icons/bs"; +import { generate } from "random-words"; +import { capitalize } from "lodash"; import useUsers from "@/hooks/useUsers"; -import {Group, User} from "@/interfaces/user"; +import { Group, User } from "@/interfaces/user"; import ProgressBar from "@/components/Low/ProgressBar"; -import {calculateAverageLevel} from "@/utils/score"; +import { calculateAverageLevel } from "@/utils/score"; import Button from "@/components/Low/Button"; import ReactDatePicker from "react-datepicker"; import moment from "moment"; import axios from "axios"; -import {getExam} from "@/utils/exams"; -import {toast} from "react-toastify"; -import {Assignment} from "@/interfaces/results"; +import { getExam } from "@/utils/exams"; +import { toast } from "react-toastify"; +import { Assignment } from "@/interfaces/results"; import Checkbox from "@/components/Low/Checkbox"; -import {InstructorGender, Variant} from "@/interfaces/exam"; +import { InstructorGender, Variant } from "@/interfaces/exam"; import Select from "@/components/Low/Select"; import useExams from "@/hooks/useExams"; -import {useListSearch} from "@/hooks/useListSearch"; +import { useListSearch } from "@/hooks/useListSearch"; interface Props { isCreating: boolean; @@ -34,7 +34,7 @@ interface Props { const SIZE = 12; -export default function AssignmentCreator({isCreating, assignment, user, groups, users, cancelCreation}: Props) { +export default function AssignmentCreator({ isCreating, assignment, user, groups, users, cancelCreation }: Props) { const [studentsPage, setStudentsPage] = useState(0); const [teachersPage, setTeachersPage] = useState(0); @@ -43,14 +43,14 @@ export default function AssignmentCreator({isCreating, assignment, user, groups, const [teachers, setTeachers] = useState(!!assignment ? assignment.teachers || [] : [...(user.type === "teacher" ? [user.id] : [])]); const [name, setName] = useState( assignment?.name || - generate({ - minLength: 6, - maxLength: 8, - min: 2, - max: 3, - join: " ", - formatter: capitalize, - }), + generate({ + minLength: 6, + maxLength: 8, + min: 2, + max: 3, + join: " ", + formatter: capitalize, + }), ); const [isLoading, setIsLoading] = useState(false); const [startDate, setStartDate] = useState(assignment ? moment(assignment.startDate).toDate() : moment().add(1, "hour").toDate()); @@ -65,18 +65,17 @@ export default function AssignmentCreator({isCreating, assignment, user, groups, const [released, setReleased] = useState(assignment?.released || false); const [autoStart, setAutostart] = useState(assignment?.autoStart || false); - const [autoStartDate, setAutoStartDate] = useState(assignment ? moment(assignment.autoStartDate).toDate() : new Date()); const [useRandomExams, setUseRandomExams] = useState(true); - const [examIDs, setExamIDs] = useState<{id: string; module: Module}[]>([]); + const [examIDs, setExamIDs] = useState<{ id: string; module: Module }[]>([]); - const {exams} = useExams(); + const { exams } = useExams(); const userStudents = useMemo(() => users.filter((x) => x.type === "student"), [users]); const userTeachers = useMemo(() => users.filter((x) => x.type === "teacher"), [users]); - const {rows: filteredStudentsRows, renderSearch: renderStudentSearch, text: studentText} = useListSearch([["name"], ["email"]], userStudents); - const {rows: filteredTeachersRows, renderSearch: renderTeacherSearch, text: teacherText} = useListSearch([["name"], ["email"]], userTeachers); + const { rows: filteredStudentsRows, renderSearch: renderStudentSearch, text: studentText } = useListSearch([["name"], ["email"]], userStudents); + const { rows: filteredTeachersRows, renderSearch: renderTeacherSearch, text: teacherText } = useListSearch([["name"], ["email"]], userTeachers); useEffect(() => setStudentsPage(0), [studentText]); const studentRows = useMemo( @@ -131,7 +130,6 @@ export default function AssignmentCreator({isCreating, assignment, user, groups, instructorGender, released, autoStart, - autoStartDate, }) .then(() => { toast.success(`The assignment "${name}" has been ${assignment ? "updated" : "created"} successfully!`); @@ -306,24 +304,6 @@ export default function AssignmentCreator({isCreating, assignment, user, groups, onChange={(date) => setEndDate(date)} />
- {autoStart && ( -
- - moment(date).isSameOrAfter(new Date())} - dateFormat="dd/MM/yyyy HH:mm" - selected={autoStartDate} - showTimeSelect - onChange={(date) => setAutoStartDate(date)} - /> -
- )} {selectedModules.includes("speaking") && ( @@ -337,9 +317,9 @@ export default function AssignmentCreator({isCreating, assignment, user, groups, onChange={(value) => (value ? setInstructorGender(value.value as InstructorGender) : null)} disabled={!selectedModules.includes("speaking") || !!assignment} options={[ - {value: "male", label: "Male"}, - {value: "female", label: "Female"}, - {value: "varied", label: "Varied"}, + { value: "male", label: "Male" }, + { value: "female", label: "Female" }, + { value: "varied", label: "Varied" }, ]} /> @@ -362,12 +342,12 @@ export default function AssignmentCreator({isCreating, assignment, user, groups, }} onChange={(value) => value - ? setExamIDs((prev) => [...prev.filter((x) => x.module !== module), {id: value.value!, module}]) + ? setExamIDs((prev) => [...prev.filter((x) => x.module !== module), { id: value.value!, module }]) : setExamIDs((prev) => prev.filter((x) => x.module !== module)) } options={exams .filter((x) => !x.isDiagnostic && x.module === module) - .map((x) => ({value: x.id, label: x.id}))} + .map((x) => ({ value: x.id, label: x.id }))} /> ))} @@ -394,7 +374,7 @@ export default function AssignmentCreator({isCreating, assignment, user, groups, "bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light", "transition duration-300 ease-in-out", users.filter((u) => g.participants.includes(u.id)).every((u) => assignees.includes(u.id)) && - "!bg-mti-purple-light !text-white", + "!bg-mti-purple-light !text-white", )}> {g.name} @@ -475,7 +455,7 @@ export default function AssignmentCreator({isCreating, assignment, user, groups, "bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light", "transition duration-300 ease-in-out", users.filter((u) => g.participants.includes(u.id)).every((u) => teachers.includes(u.id)) && - "!bg-mti-purple-light !text-white", + "!bg-mti-purple-light !text-white", )}> {g.name} diff --git a/src/interfaces/results.ts b/src/interfaces/results.ts index cd9ff373..58832a4f 100644 --- a/src/interfaces/results.ts +++ b/src/interfaces/results.ts @@ -1,8 +1,8 @@ -import {Module} from "@/interfaces"; -import {InstructorGender} from "./exam"; -import {Stat} from "./user"; +import { Module } from "@/interfaces"; +import { InstructorGender } from "./exam"; +import { Stat } from "./user"; -export type UserResults = {[key in Module]: ModuleResult}; +export type UserResults = { [key in Module]: ModuleResult }; interface ModuleResult { exams: string[]; @@ -22,7 +22,7 @@ export interface Assignment { assigner: string; assignees: string[]; results: AssignmentResult[]; - exams: {id: string; module: Module; assignee: string}[]; + exams: { id: string; module: Module; assignee: string }[]; instructorGender?: InstructorGender; startDate: Date; endDate: Date; @@ -32,9 +32,8 @@ export interface Assignment { // unless start is active, the assignment is not visible to the assignees // start date now works as a limit time to start the exam start?: boolean; - autoStartDate?: Date; autoStart?: boolean; entity?: string; } -export type AssignmentWithCorporateId = Assignment & {corporateId: string}; +export type AssignmentWithCorporateId = Assignment & { corporateId: string }; diff --git a/src/pages/assignments/creator/[id].tsx b/src/pages/assignments/creator/[id].tsx index 4f3e8bca..0f8dcdf1 100644 --- a/src/pages/assignments/creator/[id].tsx +++ b/src/pages/assignments/creator/[id].tsx @@ -6,43 +6,43 @@ import ProgressBar from "@/components/Low/ProgressBar"; import Select from "@/components/Low/Select"; import Separator from "@/components/Low/Separator"; import useExams from "@/hooks/useExams"; -import {useListSearch} from "@/hooks/useListSearch"; +import { useListSearch } from "@/hooks/useListSearch"; import usePagination from "@/hooks/usePagination"; -import {Module} from "@/interfaces"; -import {EntityWithRoles} from "@/interfaces/entity"; -import {InstructorGender, Variant} from "@/interfaces/exam"; -import {Assignment} from "@/interfaces/results"; -import {Group, User} from "@/interfaces/user"; -import {sessionOptions} from "@/lib/session"; -import {mapBy, redirect, serialize} from "@/utils"; +import { Module } from "@/interfaces"; +import { EntityWithRoles } from "@/interfaces/entity"; +import { InstructorGender, Variant } from "@/interfaces/exam"; +import { Assignment } from "@/interfaces/results"; +import { Group, User } from "@/interfaces/user"; +import { sessionOptions } from "@/lib/session"; +import { mapBy, redirect, serialize } from "@/utils"; import { requestUser } from "@/utils/api"; -import {getAssignment} from "@/utils/assignments.be"; -import {getEntitiesWithRoles} from "@/utils/entities.be"; -import {getGroups, getGroupsByEntities} from "@/utils/groups.be"; -import {checkAccess, doesEntityAllow, findAllowedEntities} from "@/utils/permissions"; -import {calculateAverageLevel} from "@/utils/score"; -import {getEntitiesUsers, getUsers} from "@/utils/users.be"; +import { getAssignment } from "@/utils/assignments.be"; +import { getEntitiesWithRoles } from "@/utils/entities.be"; +import { getGroups, getGroupsByEntities } from "@/utils/groups.be"; +import { checkAccess, doesEntityAllow, findAllowedEntities } from "@/utils/permissions"; +import { calculateAverageLevel } from "@/utils/score"; +import { getEntitiesUsers, getUsers } from "@/utils/users.be"; import axios from "axios"; import clsx from "clsx"; -import {withIronSessionSsr} from "iron-session/next"; -import {capitalize} from "lodash"; +import { withIronSessionSsr } from "iron-session/next"; +import { capitalize } from "lodash"; import moment from "moment"; import Head from "next/head"; import Link from "next/link"; -import {useRouter} from "next/router"; -import {generate} from "random-words"; -import {useEffect, useMemo, useState} from "react"; +import { useRouter } from "next/router"; +import { generate } from "random-words"; +import { useEffect, useMemo, useState } from "react"; import ReactDatePicker from "react-datepicker"; -import {BsBook, BsCheckCircle, BsChevronLeft, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle} from "react-icons/bs"; -import {toast} from "react-toastify"; +import { BsBook, BsCheckCircle, BsChevronLeft, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle } from "react-icons/bs"; +import { toast } from "react-toastify"; -export const getServerSideProps = withIronSessionSsr(async ({req, res, params}) => { +export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => { const user = await requestUser(req, res) if (!user) return redirect("/login") res.setHeader("Cache-Control", "public, s-maxage=10, stale-while-revalidate=59"); - const {id} = params as {id: string}; + const { id } = params as { id: string }; const entityIDS = mapBy(user.entities, "id") || []; const assignment = await getAssignment(id); @@ -59,7 +59,7 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res, params}) const users = await (checkAccess(user, ["developer", "admin"]) ? getUsers() : getEntitiesUsers(mapBy(allowedEntities, 'id'))); const groups = await (checkAccess(user, ["developer", "admin"]) ? getGroups() : getGroupsByEntities(mapBy(allowedEntities, 'id'))); - return {props: serialize({user, users, entities: allowedEntities, assignment, groups})}; + return { props: serialize({ user, users, entities: allowedEntities, assignment, groups }) }; }, sessionOptions); interface Props { @@ -72,7 +72,7 @@ interface Props { const SIZE = 9; -export default function AssignmentsPage({assignment, user, users, entities, groups}: Props) { +export default function AssignmentsPage({ assignment, user, users, entities, groups }: Props) { const [selectedModules, setSelectedModules] = useState(assignment.exams.map((e) => e.module)); const [assignees, setAssignees] = useState(assignment.assignees); const [teachers, setTeachers] = useState(assignment.teachers || []); @@ -90,12 +90,11 @@ export default function AssignmentsPage({assignment, user, users, entities, grou const [released, setReleased] = useState(assignment.released || false); const [autoStart, setAutostart] = useState(assignment.autoStart || false); - const [autoStartDate, setAutoStartDate] = useState(moment(assignment.autoStartDate).toDate()); const [useRandomExams, setUseRandomExams] = useState(true); - const [examIDs, setExamIDs] = useState<{id: string; module: Module}[]>([]); + const [examIDs, setExamIDs] = useState<{ id: string; module: Module }[]>([]); - const {exams} = useExams(); + const { exams } = useExams(); const router = useRouter(); const classrooms = useMemo(() => groups.filter((e) => e.entity === entity), [entity, groups]); @@ -103,11 +102,11 @@ export default function AssignmentsPage({assignment, user, users, entities, grou const userStudents = useMemo(() => users.filter((x) => x.type === "student"), [users]); const userTeachers = useMemo(() => users.filter((x) => x.type === "teacher"), [users]); - const {rows: filteredStudentsRows, renderSearch: renderStudentSearch} = useListSearch([["name"], ["email"]], userStudents); - const {rows: filteredTeachersRows, renderSearch: renderTeacherSearch} = useListSearch([["name"], ["email"]], userTeachers); + const { rows: filteredStudentsRows, renderSearch: renderStudentSearch } = useListSearch([["name"], ["email"]], userStudents); + const { rows: filteredTeachersRows, renderSearch: renderTeacherSearch } = useListSearch([["name"], ["email"]], userTeachers); - const {items: studentRows, renderMinimal: renderStudentPagination} = usePagination(filteredStudentsRows, SIZE); - const {items: teacherRows, renderMinimal: renderTeacherPagination} = usePagination(filteredTeachersRows, SIZE); + const { items: studentRows, renderMinimal: renderStudentPagination } = usePagination(filteredStudentsRows, SIZE); + const { items: teacherRows, renderMinimal: renderTeacherPagination } = usePagination(filteredTeachersRows, SIZE); useEffect(() => { setExamIDs((prev) => prev.filter((x) => selectedModules.includes(x.module))); @@ -148,7 +147,6 @@ export default function AssignmentsPage({assignment, user, users, entities, grou instructorGender, released, autoStart, - autoStartDate, }) .then(() => { toast.success(`The assignment "${name}" has been updated successfully!`); @@ -316,9 +314,9 @@ export default function AssignmentsPage({assignment, user, users, entities, grou setName(e)} defaultValue={name} label="Assignment Name" required /> setName(e)} defaultValue={name} label="Assignment Name" required />