Removed the autoStartDate and replaced it with the current startDate

This commit is contained in:
Tiago Ribeiro
2024-11-10 00:13:09 +00:00
parent 042b07c267
commit 7ac15fc767
7 changed files with 149 additions and 225 deletions

View File

@@ -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<Module[]>(assignment.exams.map((e) => e.module));
const [assignees, setAssignees] = useState<string[]>(assignment.assignees);
const [teachers, setTeachers] = useState<string[]>(assignment.teachers || []);
@@ -90,12 +90,11 @@ export default function AssignmentsPage({assignment, user, users, entities, grou
const [released, setReleased] = useState<boolean>(assignment.released || false);
const [autoStart, setAutostart] = useState<boolean>(assignment.autoStart || false);
const [autoStartDate, setAutoStartDate] = useState<Date | null>(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
<Input type="text" name="name" onChange={(e) => setName(e)} defaultValue={name} label="Assignment Name" required />
<Select
label="Entity"
options={entities.map((e) => ({value: e.id, label: e.label}))}
options={entities.map((e) => ({ value: e.id, label: e.label }))}
onChange={(v) => setEntity(v ? v.value! : undefined)}
defaultValue={{value: entities[0]?.id, label: entities[0]?.label}}
defaultValue={{ value: entities[0]?.id, label: entities[0]?.label }}
/>
</div>
@@ -355,24 +353,6 @@ export default function AssignmentsPage({assignment, user, users, entities, grou
onChange={(date) => setEndDate(date)}
/>
</div>
{autoStart && (
<div className="flex flex-col gap-2">
<label className="font-normal text-base text-mti-gray-dim">Automatic Start Date *</label>
<ReactDatePicker
className={clsx(
"p-6 w-full min-h-[70px] flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer",
"hover:border-mti-purple tooltip z-10",
"transition duration-300 ease-in-out",
)}
popperClassName="!z-20"
filterTime={(date) => moment(date).isSameOrAfter(new Date())}
dateFormat="dd/MM/yyyy HH:mm"
selected={autoStartDate}
showTimeSelect
onChange={(date) => setAutoStartDate(date)}
/>
</div>
)}
</div>
{selectedModules.includes("speaking") && (
@@ -386,9 +366,9 @@ export default function AssignmentsPage({assignment, user, users, entities, grou
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" },
]}
/>
</div>
@@ -412,14 +392,14 @@ export default function AssignmentsPage({assignment, user, users, entities, grou
onChange={(value) =>
value
? setExamIDs((prev) => [
...prev.filter((x) => x.module !== module),
{id: value.value!, module},
])
...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 }))}
/>
</div>
))}
@@ -446,7 +426,7 @@ export default function AssignmentsPage({assignment, user, users, entities, grou
"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}
</button>
@@ -510,7 +490,7 @@ export default function AssignmentsPage({assignment, user, users, entities, grou
"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}
</button>

View File

@@ -6,37 +6,37 @@ 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, WithEntity} 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, WithEntity } 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 {getEntitiesWithRoles} from "@/utils/entities.be";
import {getGroups, getGroupsByEntities} from "@/utils/groups.be";
import {checkAccess, findAllowedEntities} from "@/utils/permissions";
import {calculateAverageLevel} from "@/utils/score";
import { getEntitiesWithRoles } from "@/utils/entities.be";
import { getGroups, getGroupsByEntities } from "@/utils/groups.be";
import { checkAccess, findAllowedEntities } from "@/utils/permissions";
import { calculateAverageLevel } from "@/utils/score";
import { isAdmin } from "@/utils/users";
import {getEntitiesUsers, getUsers} from "@/utils/users.be";
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}) => {
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res)
if (!user) return redirect("/login")
@@ -49,7 +49,7 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
const users = await (isAdmin(user) ? getUsers() : getEntitiesUsers(mapBy(allowedEntities, 'id')));
const groups = await (isAdmin(user) ? getGroups() : getGroupsByEntities(mapBy(allowedEntities, 'id')));
return {props: serialize({user, users, entities, groups})};
return { props: serialize({ user, users, entities, groups }) };
}, sessionOptions);
interface Props {
@@ -62,7 +62,7 @@ interface Props {
const SIZE = 9;
export default function AssignmentsPage({user, users, groups, entities}: Props) {
export default function AssignmentsPage({ user, users, groups, entities }: Props) {
const [selectedModules, setSelectedModules] = useState<Module[]>([]);
const [assignees, setAssignees] = useState<string[]>([]);
const [teachers, setTeachers] = useState<string[]>([...(user.type === "teacher" ? [user.id] : [])]);
@@ -88,12 +88,11 @@ export default function AssignmentsPage({user, users, groups, entities}: Props)
const [released, setReleased] = useState<boolean>(false);
const [autoStart, setAutostart] = useState<boolean>(false);
const [autoStartDate, setAutoStartDate] = useState<Date | null>(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 router = useRouter();
const classrooms = useMemo(() => groups.filter((e) => e.entity?.id === entity), [entity, groups]);
@@ -102,11 +101,11 @@ export default function AssignmentsPage({user, users, groups, entities}: Props)
const userStudents = useMemo(() => allowedUsers.filter((x) => x.type === "student"), [allowedUsers]);
const userTeachers = useMemo(() => allowedUsers.filter((x) => x.type === "teacher"), [allowedUsers]);
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({user, users, groups, entities}: Props)
instructorGender,
released,
autoStart,
autoStartDate,
})
.then((result) => {
toast.success(`The assignment "${name}" has been created successfully!`);
@@ -274,9 +272,9 @@ export default function AssignmentsPage({user, users, groups, entities}: Props)
<Input type="text" name="name" onChange={(e) => setName(e)} defaultValue={name} label="Assignment Name" required />
<Select
label="Entity"
options={entities.map((e) => ({value: e.id, label: e.label}))}
options={entities.map((e) => ({ value: e.id, label: e.label }))}
onChange={(v) => setEntity(v ? v.value! : undefined)}
defaultValue={{value: entities[0]?.id, label: entities[0]?.label}}
defaultValue={{ value: entities[0]?.id, label: entities[0]?.label }}
/>
</div>
@@ -313,24 +311,6 @@ export default function AssignmentsPage({user, users, groups, entities}: Props)
onChange={(date) => setEndDate(date)}
/>
</div>
{autoStart && (
<div className="flex flex-col gap-2">
<label className="font-normal text-base text-mti-gray-dim">Automatic Start Date *</label>
<ReactDatePicker
className={clsx(
"p-6 w-full min-h-[70px] flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer",
"hover:border-mti-purple tooltip z-10",
"transition duration-300 ease-in-out",
)}
popperClassName="!z-20"
filterTime={(date) => moment(date).isSameOrAfter(new Date())}
dateFormat="dd/MM/yyyy HH:mm"
selected={autoStartDate}
showTimeSelect
onChange={(date) => setAutoStartDate(date)}
/>
</div>
)}
</div>
{selectedModules.includes("speaking") && (
@@ -344,9 +324,9 @@ export default function AssignmentsPage({user, users, groups, entities}: Props)
onChange={(value) => (value ? setInstructorGender(value.value as InstructorGender) : null)}
disabled={!selectedModules.includes("speaking")}
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" },
]}
/>
</div>
@@ -370,14 +350,14 @@ export default function AssignmentsPage({user, users, groups, entities}: Props)
onChange={(value) =>
value
? setExamIDs((prev) => [
...prev.filter((x) => x.module !== module),
{id: value.value!, module},
])
...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 }))}
/>
</div>
))}
@@ -404,7 +384,7 @@ export default function AssignmentsPage({user, users, groups, entities}: Props)
"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}
</button>
@@ -468,7 +448,7 @@ export default function AssignmentsPage({user, users, groups, entities}: Props)
"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}
</button>