Merge remote-tracking branch 'origin/develop' into feature/level-file-upload

This commit is contained in:
Carlos Mesquita
2024-09-06 09:41:01 +01:00
13 changed files with 282 additions and 284 deletions

View File

@@ -40,61 +40,71 @@ export default function SessionCard({
};
return (
<div className="border-mti-gray-anti-flash flex w-64 flex-col gap-3 rounded-xl border p-4 text-black">
<span className="flex gap-1">
<b>ID:</b>
{session.sessionId}
</span>
<span className="flex gap-1">
<b>Date:</b>
{moment(session.date).format("DD/MM/YYYY - HH:mm")}
</span>
<div className="flex w-full items-center justify-between">
<div className="-md:mt-2 grid w-full grid-cols-4 place-items-center justify-center gap-2">
{session.selectedModules.sort(sortByModuleName).map((module) => (
<div
key={module}
data-tip={capitalize(module)}
className={clsx(
"-md:px-4 tooltip flex w-fit items-center gap-2 rounded-xl py-2 text-white md:px-2 xl:px-4",
module === "reading" && "bg-ielts-reading",
module === "listening" && "bg-ielts-listening",
module === "writing" && "bg-ielts-writing",
module === "speaking" && "bg-ielts-speaking",
module === "level" && "bg-ielts-level",
)}>
{module === "reading" && <BsBook className="h-4 w-4" />}
{module === "listening" && <BsHeadphones className="h-4 w-4" />}
{module === "writing" && <BsPen className="h-4 w-4" />}
{module === "speaking" && <BsMegaphone className="h-4 w-4" />}
{module === "level" && <BsClipboard className="h-4 w-4" />}
</div>
))}
</div>
<div className="border-mti-gray-anti-flash flex w-64 flex-col justify-between gap-3 rounded-xl border p-4 text-black">
<div className="flex flex-col gap-3">
<span className="flex gap-1">
<b>ID:</b>
{session.sessionId}
</span>
<span className="flex gap-1">
<b>Date:</b>
{moment(session.date).format("DD/MM/YYYY - HH:mm")}
</span>
{session.assignment && (
<span className="flex flex-col gap-0">
<b>Assignment:</b>
{session.assignment.name}
</span>
)}
</div>
<div className="flex items-center gap-2 w-full">
<button
onClick={async () => await loadSession(session)}
disabled={isLoading}
className="bg-mti-green-ultralight w-full hover:bg-mti-green-light rounded-lg p-2 px-4 transition duration-300 ease-in-out hover:text-white disabled:cursor-not-allowed">
{!isLoading && "Resume"}
{isLoading && (
<div className="flex items-center justify-center">
<BsArrowRepeat className="animate-spin text-white" size={25} />
</div>
)}
</button>
<button
onClick={deleteSession}
disabled={isLoading}
className="bg-mti-red-ultralight w-full hover:bg-mti-red-light rounded-lg p-2 px-4 transition duration-300 ease-in-out hover:text-white disabled:cursor-not-allowed">
{!isLoading && "Delete"}
{isLoading && (
<div className="flex items-center justify-center">
<BsArrowRepeat className="animate-spin text-white" size={25} />
</div>
)}
</button>
<div className="flex flex-col gap-3">
<div className="flex w-full items-center justify-between">
<div className="-md:mt-2 grid w-full grid-cols-4 place-items-center justify-center gap-2">
{session.selectedModules.sort(sortByModuleName).map((module) => (
<div
key={module}
data-tip={capitalize(module)}
className={clsx(
"-md:px-4 tooltip flex w-fit items-center gap-2 rounded-xl py-2 text-white md:px-2 xl:px-4",
module === "reading" && "bg-ielts-reading",
module === "listening" && "bg-ielts-listening",
module === "writing" && "bg-ielts-writing",
module === "speaking" && "bg-ielts-speaking",
module === "level" && "bg-ielts-level",
)}>
{module === "reading" && <BsBook className="h-4 w-4" />}
{module === "listening" && <BsHeadphones className="h-4 w-4" />}
{module === "writing" && <BsPen className="h-4 w-4" />}
{module === "speaking" && <BsMegaphone className="h-4 w-4" />}
{module === "level" && <BsClipboard className="h-4 w-4" />}
</div>
))}
</div>
</div>
<div className="flex items-center gap-2 w-full">
<button
onClick={async () => await loadSession(session)}
disabled={isLoading}
className="bg-mti-green-ultralight w-full hover:bg-mti-green-light rounded-lg p-2 px-4 transition duration-300 ease-in-out hover:text-white disabled:cursor-not-allowed">
{!isLoading && "Resume"}
{isLoading && (
<div className="flex items-center justify-center">
<BsArrowRepeat className="animate-spin text-white" size={25} />
</div>
)}
</button>
<button
onClick={deleteSession}
disabled={isLoading}
className="bg-mti-red-ultralight w-full hover:bg-mti-red-light rounded-lg p-2 px-4 transition duration-300 ease-in-out hover:text-white disabled:cursor-not-allowed">
{!isLoading && "Delete"}
{isLoading && (
<div className="flex items-center justify-center">
<BsArrowRepeat className="animate-spin text-white" size={25} />
</div>
)}
</button>
</div>
</div>
</div>
);

View File

@@ -277,7 +277,11 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
selectedTrainingExams.some((exam) => exam.includes(timestamp)) &&
"border-2 border-slate-600",
)}
onClick={examNumber === undefined ? selectExam : undefined}
onClick={() => {
if (!!assignment && !assignment.released) return;
if (examNumber === undefined) return selectExam();
return;
}}
style={{
...(width !== undefined && {width}),
...(height !== undefined && {height}),

View File

@@ -207,64 +207,6 @@ export default function CorporateDashboard({user, linkedCorporate}: Props) {
</div>
);
const StudentsList = () => {
const filter = (x: User) =>
x.type === "student" &&
(!!selectedUser
? groups
.filter((g) => g.admin === selectedUser.id)
.flatMap((g) => g.participants)
.includes(x.id) || false
: groups.flatMap((g) => g.participants).includes(x.id));
return (
<UserList
user={user}
filters={[filter]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Students ({total})</h2>
</div>
)}
/>
);
};
const TeachersList = () => {
const filter = (x: User) =>
x.type === "teacher" &&
(!!selectedUser
? groups
.filter((g) => g.admin === selectedUser.id)
.flatMap((g) => g.participants)
.includes(x.id) || false
: groups.flatMap((g) => g.participants).includes(x.id));
return (
<UserList
user={user}
filters={[filter]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Teachers ({total})</h2>
</div>
)}
/>
);
};
const GroupsList = () => {
const filter = (x: Group) => x.admin === user.id || x.participants.includes(user.id);
@@ -518,8 +460,40 @@ export default function CorporateDashboard({user, linkedCorporate}: Props) {
)}
</>
</Modal>
{router.asPath === "/#students" && <StudentsList />}
{router.asPath === "/#teachers" && <TeachersList />}
{router.asPath === "/#students" && (
<UserList
user={user}
filters={[(x) => x.type === "student"]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Students ({total})</h2>
</div>
)}
/>
)}
{router.asPath === "/#teachers" && (
<UserList
user={user}
filters={[(x) => x.type === "teacher"]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Teachers ({total})</h2>
</div>
)}
/>
)}
{router.asPath === "/#groups" && <GroupsList />}
{router.asPath === "/#assignments" && (
<AssignmentsPage

View File

@@ -349,8 +349,8 @@ export default function MasterCorporateDashboard({user}: Props) {
);
}, [assignments, groups, users]);
const studentFilter = (user: User) => user.type === "student" && corporateUserGroups.includes(user.id);
const teacherFilter = (user: User) => user.type === "teacher" && corporateUserGroups.includes(user.id);
const studentFilter = (user: User) => user.type === "student";
const teacherFilter = (user: User) => user.type === "teacher";
const getStatsByStudent = (user: User) => stats.filter((s) => s.user === user.id);
const UserDisplay = (displayUser: User) => (
@@ -365,74 +365,7 @@ export default function MasterCorporateDashboard({user}: Props) {
</div>
);
const StudentsList = () => {
const filter = (x: User) =>
x.type === "student" && (!!selectedUser ? corporateUserGroups.includes(x.id) || false : corporateUserGroups.includes(x.id));
return (
<UserList
user={user}
filters={[filter]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Students ({total})</h2>
</div>
)}
/>
);
};
const TeachersList = () => {
const filter = (x: User) =>
x.type === "teacher" && (!!selectedUser ? corporateUserGroups.includes(x.id) || false : corporateUserGroups.includes(x.id));
return (
<UserList
user={user}
filters={[filter]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Teachers ({total})</h2>
</div>
)}
/>
);
};
const corporateUserFilter = (x: User) =>
x.type === "corporate" && (!!selectedUser ? masterCorporateUserGroups.includes(x.id) || false : masterCorporateUserGroups.includes(x.id));
const CorporateList = () => {
return (
<UserList
user={user}
filters={[corporateUserFilter]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Corporates ({total})</h2>
</div>
)}
/>
);
};
const corporateUserFilter = (x: User) => x.type === "corporate";
const GroupsList = () => {
return (
@@ -704,10 +637,58 @@ export default function MasterCorporateDashboard({user}: Props) {
)}
</>
</Modal>
{router.asPath === "/#students" && <StudentsList />}
{router.asPath === "/#teachers" && <TeachersList />}
{router.asPath === "/#students" && (
<UserList
user={user}
filters={[(x) => x.type === "student"]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Students ({total})</h2>
</div>
)}
/>
)}
{router.asPath === "/#teachers" && (
<UserList
user={user}
filters={[(x) => x.type === "teacher"]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Teachers ({total})</h2>
</div>
)}
/>
)}
{router.asPath === "/#groups" && <GroupsList />}
{router.asPath === "/#corporate" && <CorporateList />}
{router.asPath === "/#corporate" && (
<UserList
user={user}
filters={[(x) => x.type === "corporate"]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Corporate ({total})</h2>
</div>
)}
/>
)}
{router.asPath === "/#assignments" && (
<AssignmentsPage
assignments={assignments}

View File

@@ -29,6 +29,7 @@ import {BsArrowRepeat, BsBook, BsClipboard, BsFileEarmarkText, BsHeadphones, BsM
import {toast} from "react-toastify";
import {activeAssignmentFilter} from "@/utils/assignments";
import ModuleBadge from "@/components/ModuleBadge";
import useSessions from "@/hooks/useSessions";
interface Props {
user: User;
@@ -38,6 +39,7 @@ interface Props {
export default function StudentDashboard({user, users, linkedCorporate}: Props) {
const {gradingSystem} = useGradingSystem();
const {sessions} = useSessions(user.id);
const {data: stats} = useFilterRecordsByUser<Stat[]>(user.id, !user?.id);
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({assignees: user?.id});
const {invites, isLoading: isInvitesLoading, reload: reloadInvites} = useInvites({to: user.id});
@@ -160,12 +162,20 @@ export default function StudentDashboard({user, users, linkedCorporate}: Props)
Start
</Button>
</div>
<Button
className="-md:hidden h-full w-full max-w-[50%] !rounded-xl"
onClick={() => startAssignment(assignment)}
variant="outline">
Start
</Button>
<div
data-tip="You have already started this assignment!"
className={clsx(
"-md:hidden h-full w-full max-w-[50%] cursor-pointer",
sessions.filter((x) => x.assignment?.id === assignment.id).length > 0 && "tooltip",
)}>
<Button
className={clsx("w-full h-full !rounded-xl")}
onClick={() => startAssignment(assignment)}
variant="outline"
disabled={sessions.filter((x) => x.assignment?.id === assignment.id).length > 0}>
Start
</Button>
</div>
</>
)}
{assignment.results.map((r) => r.user).includes(user.id) && (

View File

@@ -51,6 +51,7 @@ import usePermissions from "@/hooks/usePermissions";
import {futureAssignmentFilter, pastAssignmentFilter, archivedAssignmentFilter, activeAssignmentFilter} from "@/utils/assignments";
import AssignmentsPage from "./views/AssignmentsPage";
import {useRouter} from "next/router";
import useFilterStore from "@/stores/listFilterStore";
interface Props {
user: User;
@@ -67,6 +68,7 @@ export default function TeacherDashboard({user, linkedCorporate}: Props) {
const {permissions} = usePermissions(user.id);
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({assigner: user.id});
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
const router = useRouter();
const assignmentsGroups = useMemo(() => groups.filter((x) => x.admin === user.id || x.participants.includes(user.id)), [groups, user.id]);
@@ -90,7 +92,7 @@ export default function TeacherDashboard({user, linkedCorporate}: Props) {
setShowModal(!!selectedUser && router.asPath === "/#");
}, [selectedUser, router.asPath]);
const studentFilter = (user: User) => user.type === "student" && groups.flatMap((g) => g.participants).includes(user.id);
const studentFilter = (user: User) => user.type === "student";
const getStatsByStudent = (user: User) => stats.filter((s) => s.user === user.id);
@@ -106,35 +108,6 @@ export default function TeacherDashboard({user, linkedCorporate}: Props) {
</div>
);
const StudentsList = () => {
const filter = (x: User) =>
x.type === "student" &&
(!!selectedUser
? groups
.filter((g) => g.admin === selectedUser.id)
.flatMap((g) => g.participants)
.includes(x.id) || false
: groups.flatMap((g) => g.participants).includes(x.id));
return (
<UserList
user={user}
filters={[filter]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Students ({total})</h2>
</div>
)}
/>
);
};
const GroupsList = () => {
const filter = (x: Group) => x.admin === user.id;
@@ -285,16 +258,68 @@ export default function TeacherDashboard({user, linkedCorporate}: Props) {
if (shouldReload) reload();
}}
onViewStudents={
selectedUser.type === "corporate" || selectedUser.type === "teacher" ? () => router.push("/#students") : undefined
selectedUser.type === "corporate" || selectedUser.type === "teacher"
? () => {
appendUserFilters({
id: "view-students",
filter: (x: User) => x.type === "student",
});
appendUserFilters({
id: "belongs-to-admin",
filter: (x: User) =>
groups
.filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id))
.flatMap((g) => g.participants)
.includes(x.id),
});
router.push("/list/users");
}
: undefined
}
onViewTeachers={
selectedUser.type === "corporate" || selectedUser.type === "student"
? () => {
appendUserFilters({
id: "view-teachers",
filter: (x: User) => x.type === "teacher",
});
appendUserFilters({
id: "belongs-to-admin",
filter: (x: User) =>
groups
.filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id))
.flatMap((g) => g.participants)
.includes(x.id),
});
router.push("/list/users");
}
: undefined
}
onViewTeachers={selectedUser.type === "corporate" ? () => router.push("/#teachers") : undefined}
user={selectedUser}
/>
</div>
)}
</>
</Modal>
{router.asPath === "/#students" && <StudentsList />}
{router.asPath === "/#students" && (
<UserList
user={user}
filters={[(x) => x.type === "student"]}
renderHeader={(total) => (
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Students ({total})</h2>
</div>
)}
/>
)}
{router.asPath === "/#groups" && <GroupsList />}
{router.asPath === "/#assignments" && (
<AssignmentsPage

View File

@@ -212,7 +212,7 @@ export default function Finish({user, scores, modules, information, solutions, i
</span>
</div>
)}
{assignment && !assignment.released && (
{assignment && !assignment.released && !isLoading && (
<div className="absolute left-1/2 top-1/2 flex h-fit w-fit -translate-x-1/2 -translate-y-1/2 flex-col items-center gap-12">
{/* <span className={clsx("loading loading-infinity w-32", moduleColors[selectedModule].progress)} /> */}
<BsBan size={64} className={clsx(moduleColors[selectedModule].progress)} />
@@ -223,7 +223,7 @@ export default function Finish({user, scores, modules, information, solutions, i
</span>
</div>
)}
{!isLoading && (
{!isLoading && !(assignment && !assignment.released) && (
<div className="mb-20 mt-32 flex w-full items-center justify-between gap-9">
<span className="max-w-3xl">{moduleResultText(selectedModule, bandScore)}</span>
<div className="flex gap-9 px-16">

View File

@@ -80,19 +80,15 @@ export default function UserList({
useEffect(() => {
(async () => {
if (user && users) {
const filterUsers = ["corporate", "teacher", "mastercorporate"].includes(user.type)
? users.filter((u) => groups.flatMap((g) => g.participants).includes(u.id))
: users;
const filteredUsers = filters.reduce((d, f) => d.filter(f), filterUsers);
if (users) {
const filteredUsers = filters.reduce((d, f) => d.filter(f), users);
const sortedUsers = await asyncSorter<User>(filteredUsers, sortFunction);
setDisplayUsers([...sortedUsers]);
}
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [user, users, sorter, groups]);
}, [users, sorter]);
const deleteAccount = (user: User) => {
if (!confirm(`Are you sure you want to delete ${user.name}'s account?`)) return;

View File

@@ -30,30 +30,10 @@ async function get(req: NextApiRequest, res: NextApiResponse) {
participant: string;
};
if (req.session?.user?.type === "mastercorporate") {
try {
const masterCorporateGroups = await getGroupsForUser(admin, participant);
const participants = uniq(masterCorporateGroups.flatMap((g) => g.participants));
const corporatesFromMaster = (await Promise.all(participants.map(getUser))).filter((x) => x.type === "corporate");
if (corporatesFromMaster.length === 0) return res.status(200).json(masterCorporateGroups);
const groups = await Promise.all(corporatesFromMaster.map((c) => getGroupsForUser(c.id, participant)));
return res.status(200).json([...masterCorporateGroups, ...uniqBy(groups.flat(), "id")]);
} catch (e) {
console.error(e);
res.status(500).json({ok: false});
return;
}
}
try {
const groups = await getGroupsForUser(admin, participant);
res.status(200).json(groups);
} catch (e) {
console.error(e);
res.status(500).json({ok: false});
}
const adminGroups = await getGroupsForUser(admin, participant);
const participants = uniq(adminGroups.flatMap((g) => g.participants));
const groups = await Promise.all(participants.map(async (c) => await getGroupsForUser(c, participant)));
return res.status(200).json([...adminGroups, ...uniqBy(groups.flat(), "id")]);
}
async function post(req: NextApiRequest, res: NextApiResponse) {

View File

@@ -4,10 +4,12 @@ import {getFirestore, setDoc, doc, query, collection, where, getDocs, getDoc, de
import {withIronSessionApiRoute} from "iron-session/next";
import {sessionOptions} from "@/lib/session";
import {v4} from "uuid";
import {CorporateUser, Group} from "@/interfaces/user";
import {CorporateUser, Group, Type} from "@/interfaces/user";
import {createUserWithEmailAndPassword, getAuth} from "firebase/auth";
import ShortUniqueId from "short-unique-id";
import {getUserCorporate} from "@/utils/groups.be";
import {getUserCorporate, getUserGroups} from "@/utils/groups.be";
import {uniq} from "lodash";
import {getUser} from "@/utils/users.be";
const DEFAULT_DESIRED_LEVELS = {
reading: 9,
@@ -28,6 +30,13 @@ const db = getFirestore(app);
export default withIronSessionApiRoute(handler, sessionOptions);
const getUsersOfType = async (admin: string, type: Type) => {
const groups = await getUserGroups(admin);
const users = await Promise.all(uniq(groups.flatMap((x) => x.participants)).map(getUser));
return users.filter((x) => x.type === type).map((x) => x.id);
};
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "POST") return post(req, res);
@@ -106,11 +115,14 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
});
if (type === "corporate") {
const students = maker.type === "corporate" ? await getUsersOfType(maker.id, "student") : [];
const teachers = maker.type === "corporate" ? await getUsersOfType(maker.id, "teacher") : [];
const defaultTeachersGroup: Group = {
admin: userId,
id: v4(),
name: "Teachers",
participants: [],
participants: teachers,
disableEditing: true,
};
@@ -118,21 +130,12 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
admin: userId,
id: v4(),
name: "Students",
participants: [],
disableEditing: true,
};
const defaultCorporateGroup: Group = {
admin: userId,
id: v4(),
name: "Corporate",
participants: [],
participants: students,
disableEditing: true,
};
await setDoc(doc(db, "groups", defaultTeachersGroup.id), defaultTeachersGroup);
await setDoc(doc(db, "groups", defaultStudentsGroup.id), defaultStudentsGroup);
await setDoc(doc(db, "groups", defaultCorporateGroup.id), defaultCorporateGroup);
}
if (!!corporate) {

View File

@@ -4,6 +4,8 @@ import {app} from "@/firebase";
import {getFirestore, collection, getDocs, query, where, doc, setDoc, addDoc, getDoc} from "firebase/firestore";
import {withIronSessionApiRoute} from "iron-session/next";
import {sessionOptions} from "@/lib/session";
import {Session} from "@/hooks/useSessions";
import moment from "moment";
const db = getFirestore(app);
@@ -24,12 +26,17 @@ async function get(req: NextApiRequest, res: NextApiResponse) {
const q = user ? query(collection(db, "sessions"), where("user", "==", user)) : collection(db, "sessions");
const snapshot = await getDocs(q);
const sessions = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
})) as Session[];
res.status(200).json(
snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
})),
sessions.filter((x) => {
if (!x.assignment) return true;
if (x.assignment.results.filter((y) => y.user === user).length > 0) return false;
return !moment().isAfter(moment(x.assignment.endDate));
}),
);
}

View File

@@ -4,6 +4,8 @@ import {app} from "@/firebase";
import {getFirestore, collection, getDocs} from "firebase/firestore";
import {withIronSessionApiRoute} from "iron-session/next";
import {sessionOptions} from "@/lib/session";
import {getGroupsForUser} from "@/utils/groups.be";
import {uniq} from "lodash";
const db = getFirestore(app);
@@ -16,11 +18,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
const snapshot = await getDocs(collection(db, "users"));
const users = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
res.status(200).json(
snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
})),
);
if (!req.session.user) return res.status(200).json(users);
if (req.session.user.type === "admin" || req.session.user.type === "developer") return res.status(200).json(users);
const adminGroups = await getGroupsForUser(req.session.user.id);
const groups = await Promise.all(adminGroups.flatMap((x) => x.participants).map(async (x) => await getGroupsForUser(x)));
const participants = uniq([...adminGroups.flatMap((x) => x.participants), ...groups.flat().flatMap((x) => x.participants)]);
res.status(200).json(users.filter((x) => participants.includes(x.id)));
}

View File

@@ -40,7 +40,7 @@ export const getUserCorporate = async (id: string) => {
const groups = await getParticipantGroups(id);
const admins = await Promise.all(groups.map((x) => x.admin).map(getUser));
const corporates = admins.filter((x) => x.type === "corporate" || x.type === "mastercorporate");
const corporates = admins.filter((x) => (user.type === "corporate" ? x.type === "mastercorporate" : x.type === "corporate"));
if (corporates.length === 0) return undefined;
return corporates.shift() as CorporateUser | MasterCorporateUser;