From 39710aaea15ec3f7321c5442223195e095d6b981 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Thu, 29 Aug 2024 23:21:20 +0100 Subject: [PATCH] Improved the overall stability and speed of the app --- src/components/Sidebar.tsx | 2 +- src/pages/(admin)/BatchCodeGenerator.tsx | 12 +- src/pages/(admin)/BatchCreateUser.tsx | 12 +- src/pages/(admin)/CodeGenerator.tsx | 9 +- src/pages/(admin)/Lists/index.tsx | 9 +- src/pages/(admin)/UserCreator.tsx | 11 +- src/pages/(exam)/ExamPage.tsx | 6 +- src/pages/exam.tsx | 13 +- src/pages/exercises.tsx | 13 +- src/pages/generation.tsx | 9 +- src/pages/groups.tsx | 26 ++-- src/pages/profile.tsx | 32 +++-- src/pages/record.tsx | 55 ++++---- src/pages/settings.tsx | 159 ++++++++++++----------- src/utils/assignments.be.ts | 78 ++++------- src/utils/permissions.be.ts | 136 +++++++++---------- src/utils/users.be.ts | 13 +- 17 files changed, 315 insertions(+), 280 deletions(-) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index cf1bdeee..19d5d40f 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -212,7 +212,7 @@ export default function Sidebar({path, navDisabled = false, focusMode = false, u )} -
+
void}) { +interface Props { + user: User; + users: User[]; + permissions: PermissionType[]; + onFinish: () => void; +} + +export default function BatchCodeGenerator({user, users, permissions, onFinish}: Props) { const [infos, setInfos] = useState<{email: string; name: string; passport_id: string}[]>([]); const [isLoading, setIsLoading] = useState(false); const [expiryDate, setExpiryDate] = useState( @@ -65,9 +72,6 @@ export default function BatchCodeGenerator({user, onFinish}: {user: User; onFini const [type, setType] = useState("student"); const [showHelp, setShowHelp] = useState(false); - const {users} = useUsers(); - const {permissions} = usePermissions(user?.id || ""); - const {openFilePicker, filesContent, clear} = useFilePicker({ accept: ".xlsx", multiple: false, diff --git a/src/pages/(admin)/BatchCreateUser.tsx b/src/pages/(admin)/BatchCreateUser.tsx index bf08962a..03417eba 100644 --- a/src/pages/(admin)/BatchCreateUser.tsx +++ b/src/pages/(admin)/BatchCreateUser.tsx @@ -61,7 +61,14 @@ const USER_TYPE_PERMISSIONS: { }, }; -export default function BatchCreateUser({user, onFinish}: {user: User; onFinish: () => void}) { +interface Props { + user: User; + users: User[]; + permissions: PermissionType[]; + onFinish: () => void; +} + +export default function BatchCreateUser({user, users, permissions, onFinish}: Props) { const [infos, setInfos] = useState< { email: string; @@ -83,9 +90,6 @@ export default function BatchCreateUser({user, onFinish}: {user: User; onFinish: const [type, setType] = useState("student"); const [showHelp, setShowHelp] = useState(false); - const {users} = useUsers(); - const {permissions} = usePermissions(user?.id || ""); - const {openFilePicker, filesContent, clear} = useFilePicker({ accept: ".xlsx", multiple: false, diff --git a/src/pages/(admin)/CodeGenerator.tsx b/src/pages/(admin)/CodeGenerator.tsx index cbd1b5fb..c1799783 100644 --- a/src/pages/(admin)/CodeGenerator.tsx +++ b/src/pages/(admin)/CodeGenerator.tsx @@ -48,14 +48,19 @@ const USER_TYPE_PERMISSIONS: { }, }; -export default function CodeGenerator({user, onFinish}: {user: User; onFinish: () => void}) { +interface Props { + user: User; + permissions: PermissionType[]; + onFinish: () => void; +} + +export default function CodeGenerator({user, permissions, onFinish}: Props) { const [generatedCode, setGeneratedCode] = useState(); const [expiryDate, setExpiryDate] = useState( user?.subscriptionExpirationDate ? moment(user.subscriptionExpirationDate).toDate() : null, ); const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true); const [type, setType] = useState("student"); - const {permissions} = usePermissions(user?.id || ""); useEffect(() => { if (!isExpiryDateEnabled) setExpiryDate(null); diff --git a/src/pages/(admin)/Lists/index.tsx b/src/pages/(admin)/Lists/index.tsx index e9626763..477e40e0 100644 --- a/src/pages/(admin)/Lists/index.tsx +++ b/src/pages/(admin)/Lists/index.tsx @@ -9,10 +9,15 @@ import PackageList from "./PackageList"; import UserList from "./UserList"; import {checkAccess} from "@/utils/permissions"; import usePermissions from "@/hooks/usePermissions"; +import {PermissionType} from "@/interfaces/permissions"; -export default function Lists({user}: {user: User}) { - const {permissions} = usePermissions(user?.id || ""); +interface Props { + user: User; + users: User[]; + permissions: PermissionType[]; +} +export default function Lists({user, users, permissions}: Props) { return ( diff --git a/src/pages/(admin)/UserCreator.tsx b/src/pages/(admin)/UserCreator.tsx index cff30043..38dd4f3c 100644 --- a/src/pages/(admin)/UserCreator.tsx +++ b/src/pages/(admin)/UserCreator.tsx @@ -54,7 +54,14 @@ const USER_TYPE_PERMISSIONS: { }, }; -export default function UserCreator({user, onFinish}: {user: User; onFinish: () => void}) { +interface Props { + user: User; + users: User[]; + permissions: PermissionType[]; + onFinish: () => void; +} + +export default function UserCreator({user, users, permissions, onFinish}: Props) { const [name, setName] = useState(); const [email, setEmail] = useState(); const [phone, setPhone] = useState(); @@ -74,9 +81,7 @@ export default function UserCreator({user, onFinish}: {user: User; onFinish: () const [type, setType] = useState("student"); const [position, setPosition] = useState(); - const {permissions} = usePermissions(user?.id || ""); const {groups} = useGroups({admin: ["developer", "admin"].includes(user?.type) ? undefined : user?.id, userType: user?.type}); - const {users} = useUsers(); useEffect(() => { if (!isExpiryDateEnabled) setExpiryDate(null); diff --git a/src/pages/(exam)/ExamPage.tsx b/src/pages/(exam)/ExamPage.tsx index ad18ed05..079be535 100644 --- a/src/pages/(exam)/ExamPage.tsx +++ b/src/pages/(exam)/ExamPage.tsx @@ -13,7 +13,7 @@ import Speaking from "@/exams/Speaking"; import Writing from "@/exams/Writing"; import useUser from "@/hooks/useUser"; import {Exam, LevelExam, UserSolution, Variant} from "@/interfaces/exam"; -import {Stat} from "@/interfaces/user"; +import {Stat, User} from "@/interfaces/user"; import useExamStore from "@/stores/examStore"; import {evaluateSpeakingAnswer, evaluateWritingAnswer} from "@/utils/evaluation"; import {defaultExamUserSolutions, getExam} from "@/utils/exams"; @@ -28,9 +28,10 @@ import useGradingSystem from "@/hooks/useGrading"; interface Props { page: "exams" | "exercises"; + user: User; } -export default function ExamPage({page}: Props) { +export default function ExamPage({page, user}: Props) { const [variant, setVariant] = useState("full"); const [avoidRepeated, setAvoidRepeated] = useState(false); const [hasBeenUploaded, setHasBeenUploaded] = useState(false); @@ -59,7 +60,6 @@ export default function ExamPage({page}: Props) { const {bgColor, setBgColor} = useExamStore((state) => state); const setShuffleMaps = useExamStore((state) => state.setShuffles); - const {user} = useUser({redirectTo: "/login"}); const router = useRouter(); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/pages/exam.tsx b/src/pages/exam.tsx index 3f683604..779ababd 100644 --- a/src/pages/exam.tsx +++ b/src/pages/exam.tsx @@ -5,6 +5,7 @@ import {sessionOptions} from "@/lib/session"; import {shouldRedirectHome} from "@/utils/navigation.disabled"; import ExamPage from "./(exam)/ExamPage"; import Head from "next/head"; +import {User} from "@/interfaces/user"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { const user = req.session.user; @@ -14,7 +15,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { redirect: { destination: "/login", permanent: false, - } + }, }; } @@ -23,7 +24,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { redirect: { destination: "/", permanent: false, - } + }, }; } @@ -32,7 +33,11 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { }; }, sessionOptions); -export default function Page() { +interface Props { + user: User; +} + +export default function Page({user}: Props) { return ( <> @@ -44,7 +49,7 @@ export default function Page() { - + ); } diff --git a/src/pages/exercises.tsx b/src/pages/exercises.tsx index 9ab3b244..9a24775c 100644 --- a/src/pages/exercises.tsx +++ b/src/pages/exercises.tsx @@ -5,6 +5,7 @@ import {sessionOptions} from "@/lib/session"; import {shouldRedirectHome} from "@/utils/navigation.disabled"; import ExamPage from "./(exam)/ExamPage"; import Head from "next/head"; +import {User} from "@/interfaces/user"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { const user = req.session.user; @@ -14,7 +15,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { redirect: { destination: "/login", permanent: false, - } + }, }; } @@ -23,7 +24,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { redirect: { destination: "/", permanent: false, - } + }, }; } @@ -32,7 +33,11 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { }; }, sessionOptions); -export default function Page() { +interface Props { + user: User; +} + +export default function Page({user}: Props) { return ( <> @@ -44,7 +49,7 @@ export default function Page() { - + ); } diff --git a/src/pages/generation.tsx b/src/pages/generation.tsx index 82577a61..8505434d 100644 --- a/src/pages/generation.tsx +++ b/src/pages/generation.tsx @@ -22,6 +22,7 @@ import WritingGeneration from "./(generation)/WritingGeneration"; import LevelGeneration from "./(generation)/LevelGeneration"; import SpeakingGeneration from "./(generation)/SpeakingGeneration"; import {checkAccess} from "@/utils/permissions"; +import {User} from "@/interfaces/user"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { const user = req.session.user; @@ -49,10 +50,12 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { }; }, sessionOptions); -export default function Generation() { - const [module, setModule] = useState("reading"); +interface Props { + user: User; +} - const {user} = useUser({redirectTo: "/login"}); +export default function Generation({user}: Props) { + const [module, setModule] = useState("reading"); const [title, setTitle] = useState(""); return ( diff --git a/src/pages/groups.tsx b/src/pages/groups.tsx index deec7b8a..70b38f4b 100644 --- a/src/pages/groups.tsx +++ b/src/pages/groups.tsx @@ -29,7 +29,7 @@ import MasterCorporateDashboard from "@/dashboards/MasterCorporate"; import PaymentDue from "./(status)/PaymentDue"; import {useRouter} from "next/router"; import {PayPalScriptProvider} from "@paypal/react-paypal-js"; -import {CorporateUser, MasterCorporateUser, Type, User, userTypes} from "@/interfaces/user"; +import {CorporateUser, Group, MasterCorporateUser, Type, User, userTypes} from "@/interfaces/user"; import Select from "react-select"; import {USER_TYPE_LABELS} from "@/resources/user"; import {checkAccess, getTypesOfUser} from "@/utils/permissions"; @@ -37,8 +37,10 @@ import {shouldRedirectHome} from "@/utils/navigation.disabled"; import useGroups from "@/hooks/useGroups"; import useUsers from "@/hooks/useUsers"; import {getUserName} from "@/utils/users"; +import {getParticipantGroups, getUserGroups} from "@/utils/groups.be"; +import {getUsers} from "@/utils/users.be"; -export const getServerSideProps = withIronSessionSsr(({req, res}) => { +export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { const user = req.session.user; if (!user || !user.isVerified) { @@ -59,26 +61,20 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { }; } + const groups = await getParticipantGroups(user.id); + const users = await getUsers(); + return { - props: {user: req.session.user}, + props: {user, groups, users}, }; }, sessionOptions); interface Props { user: User; - envVariables: {[key: string]: string}; + groups: Group[]; + users: User[]; } -export default function Home(props: Props) { - const {user, mutateUser} = useUser({redirectTo: "/login"}); - const {groups} = useGroups({}); - const {users} = useUsers(); - - const router = useRouter(); - - useEffect(() => { - console.log(groups); - }, [groups]); - +export default function Home({user, groups, users}: Props) { return ( <> diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index 5930afdc..a6bef9f2 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -12,7 +12,16 @@ import Link from "next/link"; import axios from "axios"; import {ErrorMessage} from "@/constants/errors"; import clsx from "clsx"; -import {CorporateUser, EmploymentStatus, EMPLOYMENT_STATUS, Gender, User, DemographicInformation, MasterCorporateUser} from "@/interfaces/user"; +import { + CorporateUser, + EmploymentStatus, + EMPLOYMENT_STATUS, + Gender, + User, + DemographicInformation, + MasterCorporateUser, + Group, +} from "@/interfaces/user"; import CountrySelect from "@/components/Low/CountrySelect"; import {shouldRedirectHome} from "@/utils/navigation.disabled"; import moment from "moment"; @@ -34,8 +43,9 @@ import {capitalize} from "lodash"; import TopicModal from "@/components/Medium/TopicModal"; import {v4} from "uuid"; import {checkAccess, getTypesOfUser} from "@/utils/permissions"; -import {getUserCorporate} from "@/utils/groups.be"; +import {getParticipantGroups, getUserCorporate} from "@/utils/groups.be"; import {InferGetServerSidePropsType} from "next"; +import {getUsers} from "@/utils/users.be"; export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { const user = req.session.user; @@ -59,19 +69,26 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { } return { - props: {user, linkedCorporate: (await getUserCorporate(user.id)) || null}, + props: { + user, + linkedCorporate: (await getUserCorporate(user.id)) || null, + groups: await getParticipantGroups(user.id), + users: await getUsers(), + }, }; }, sessionOptions); interface Props { user: User; + groups: Group[]; + users: User[]; mutateUser: Function; linkedCorporate?: CorporateUser | MasterCorporateUser; } const DoubleColumnRow = ({children}: {children: ReactNode}) =>
{children}
; -function UserProfile({user, mutateUser, linkedCorporate}: Props) { +function UserProfile({user, mutateUser, linkedCorporate, groups, users}: Props) { const [bio, setBio] = useState(user.bio || ""); const [name, setName] = useState(user.name || ""); const [email, setEmail] = useState(user.email || ""); @@ -114,9 +131,6 @@ function UserProfile({user, mutateUser, linkedCorporate}: Props) { const [isPreferredTopicsOpen, setIsPreferredTopicsOpen] = useState(false); - const {groups} = useGroups({}); - const {users} = useUsers(); - const profilePictureInput = useRef(null); const expirationDateColor = (date: Date) => { const momentDate = moment(date); @@ -643,7 +657,7 @@ function UserProfile({user, mutateUser, linkedCorporate}: Props) { ); } -export default function Home({linkedCorporate}: {linkedCorporate?: CorporateUser | MasterCorporateUser}) { +export default function Home(props: {linkedCorporate?: CorporateUser | MasterCorporateUser; groups: Group[]; users: User[]}) { const {user, mutateUser} = useUser({redirectTo: "/login"}); return ( @@ -658,7 +672,7 @@ export default function Home({linkedCorporate}: {linkedCorporate?: CorporateUser - {user && } + {user && } ); } diff --git a/src/pages/record.tsx b/src/pages/record.tsx index 798655ca..43d41d12 100644 --- a/src/pages/record.tsx +++ b/src/pages/record.tsx @@ -1,28 +1,31 @@ /* eslint-disable @next/next/no-img-element */ import Head from "next/head"; -import { withIronSessionSsr } from "iron-session/next"; -import { sessionOptions } from "@/lib/session"; -import { Stat, User } from "@/interfaces/user"; -import { useEffect, useRef, useState } from "react"; +import {withIronSessionSsr} from "iron-session/next"; +import {sessionOptions} from "@/lib/session"; +import {Stat, User} from "@/interfaces/user"; +import {useEffect, useRef, useState} from "react"; import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser"; -import { groupByDate } from "@/utils/stats"; +import {groupByDate} from "@/utils/stats"; import moment from "moment"; import useUsers from "@/hooks/useUsers"; import useExamStore from "@/stores/examStore"; -import { ToastContainer } from "react-toastify"; +import {ToastContainer} from "react-toastify"; import Layout from "@/components/High/Layout"; import clsx from "clsx"; -import { shouldRedirectHome } from "@/utils/navigation.disabled"; +import {shouldRedirectHome} from "@/utils/navigation.disabled"; import useAssignments from "@/hooks/useAssignments"; -import { uuidv4 } from "@firebase/util"; -import { usePDFDownload } from "@/hooks/usePDFDownload"; +import {uuidv4} from "@firebase/util"; +import {usePDFDownload} from "@/hooks/usePDFDownload"; import useRecordStore from "@/stores/recordStore"; import StatsGridItem from "@/components/Medium/StatGridItem"; import RecordFilter from "@/components/Medium/RecordFilter"; -import { useRouter } from "next/router"; +import {useRouter} from "next/router"; import useTrainingContentStore from "@/stores/trainingContentStore"; +import {Assignment} from "@/interfaces/results"; +import {getUsers} from "@/utils/users.be"; +import {getAssignments, getAssignmentsByAssigner} from "@/utils/assignments.be"; -export const getServerSideProps = withIronSessionSsr(({ req, res }) => { +export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { const user = req.session.user; if (!user || !user.isVerified) { @@ -43,14 +46,23 @@ export const getServerSideProps = withIronSessionSsr(({ req, res }) => { }; } + const users = await getUsers(); + const assignments = await getAssignments(); + return { - props: { user: req.session.user }, + props: {user, users, assignments}, }; }, sessionOptions); type Filter = "months" | "weeks" | "days" | "assignments" | undefined; -export default function History({ user }: { user: User }) { +interface Props { + user: User; + users: User[]; + assignments: Assignment[]; +} + +export default function History({user, users, assignments}: Props) { const router = useRouter(); const [statsUserId, setStatsUserId, training, setTraining] = useRecordStore((state) => [ state.selectedUser, @@ -60,12 +72,10 @@ export default function History({ user }: { user: User }) { ]); // const [statsUserId, setStatsUserId] = useState(user.id); - const [groupedStats, setGroupedStats] = useState<{ [key: string]: Stat[] }>(); + const [groupedStats, setGroupedStats] = useState<{[key: string]: Stat[]}>(); const [filter, setFilter] = useState(); - const { assignments } = useAssignments({}); - const { users } = useUsers(); - const { data: stats, isLoading: isStatsLoading } = useFilterRecordsByUser(statsUserId || user?.id); + const {data: stats, isLoading: isStatsLoading} = useFilterRecordsByUser(statsUserId || user?.id); const setExams = useExamStore((state) => state.setExams); const setShowSolutions = useExamStore((state) => state.setShowSolutions); @@ -100,12 +110,12 @@ export default function History({ user }: { user: User }) { // if (!statsUserId) setStatsUserId(user.id); // }, []); - const filterStatsByDate = (stats: { [key: string]: Stat[] }) => { + const filterStatsByDate = (stats: {[key: string]: Stat[]}) => { if (filter && filter !== "assignments") { const filterDate = moment() - .subtract({ [filter as string]: 1 }) + .subtract({[filter as string]: 1}) .format("x"); - const filteredStats: { [key: string]: Stat[] } = {}; + const filteredStats: {[key: string]: Stat[]} = {}; Object.keys(stats).forEach((timestamp) => { if (timestamp >= filterDate) filteredStats[timestamp] = stats[timestamp]; @@ -114,7 +124,7 @@ export default function History({ user }: { user: User }) { } if (filter && filter === "assignments") { - const filteredStats: { [key: string]: Stat[] } = {}; + const filteredStats: {[key: string]: Stat[]} = {}; Object.keys(stats).forEach((timestamp) => { if (stats[timestamp].map((s) => s.assignment === undefined).includes(false)) @@ -127,7 +137,6 @@ export default function History({ user }: { user: User }) { return stats; }; - const MAX_TRAINING_EXAMS = 10; const [selectedTrainingExams, setSelectedTrainingExams] = useState([]); const setTrainingStats = useTrainingContentStore((state) => state.setStats); @@ -200,7 +209,7 @@ export default function History({ user }: { user: User }) { {user && ( - + {training && (
diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index 0276d94d..0e75039d 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -24,8 +24,12 @@ import UserCreator from "./(admin)/UserCreator"; import CorporateGradingSystem from "./(admin)/CorporateGradingSystem"; import useGradingSystem from "@/hooks/useGrading"; import {CEFR_STEPS} from "@/resources/grading"; +import {User} from "@/interfaces/user"; +import {getUserPermissions} from "@/utils/permissions.be"; +import {Permission, PermissionType} from "@/interfaces/permissions"; +import {getUsers} from "@/utils/users.be"; -export const getServerSideProps = withIronSessionSsr(({req, res}) => { +export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { const user = req.session.user; if (!user || !user.isVerified) { return { @@ -45,14 +49,21 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { }; } + const permissions = await getUserPermissions(user.id); + const users = await getUsers(); + return { - props: {user: req.session.user}, + props: {user, permissions, users}, }; }, sessionOptions); -export default function Admin() { - const {user} = useUser({redirectTo: "/login"}); - const {permissions} = usePermissions(user?.id || ""); +interface Props { + user: User; + users: User[]; + permissions: PermissionType[]; +} + +export default function Admin({user, users, permissions}: Props) { const {gradingSystem, mutate} = useGradingSystem(); const [modalOpen, setModalOpen] = useState(); @@ -69,80 +80,78 @@ export default function Admin() { - {user && ( - - setModalOpen(undefined)}> - setModalOpen(undefined)} /> - - setModalOpen(undefined)}> - setModalOpen(undefined)} /> - - setModalOpen(undefined)}> - setModalOpen(undefined)} /> - - setModalOpen(undefined)}> - setModalOpen(undefined)} /> - - setModalOpen(undefined)}> - { - mutate({user: user.id, steps}); - setModalOpen(undefined); - }} - /> - + + setModalOpen(undefined)}> + setModalOpen(undefined)} /> + + setModalOpen(undefined)}> + setModalOpen(undefined)} /> + + setModalOpen(undefined)}> + setModalOpen(undefined)} /> + + setModalOpen(undefined)}> + setModalOpen(undefined)} /> + + setModalOpen(undefined)}> + { + mutate({user: user.id, steps}); + setModalOpen(undefined); + }} + /> + -
- - {checkAccess(user, getTypesOfUser(["teacher"]), permissions, "viewCodes") && ( -
+
+ + {checkAccess(user, getTypesOfUser(["teacher"]), permissions, "viewCodes") && ( +
+ setModalOpen("createCode")} + /> + setModalOpen("batchCreateCode")} + /> + setModalOpen("createUser")} + /> + setModalOpen("batchCreateUser")} + /> + {checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) && ( setModalOpen("createCode")} + className="w-full h-full col-span-2" + onClick={() => setModalOpen("gradingSystem")} /> - setModalOpen("batchCreateCode")} - /> - setModalOpen("createUser")} - /> - setModalOpen("batchCreateUser")} - /> - {checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) && ( - setModalOpen("gradingSystem")} - /> - )} -
- )} -
-
- -
- - )} + )} +
+ )} +
+
+ +
+
); } diff --git a/src/utils/assignments.be.ts b/src/utils/assignments.be.ts index 7f46718f..03be8da3 100644 --- a/src/utils/assignments.be.ts +++ b/src/utils/assignments.be.ts @@ -1,58 +1,36 @@ -import { app } from "@/firebase"; -import { Assignment } from "@/interfaces/results"; -import { - collection, - getDocs, - getFirestore, - query, - where, -} from "firebase/firestore"; +import {app} from "@/firebase"; +import {Assignment} from "@/interfaces/results"; +import {collection, getDocs, getFirestore, query, where} from "firebase/firestore"; const db = getFirestore(app); -export const getAssignmentsByAssigner = async ( - id: string, - startDate?: Date, - endDate?: Date -) => { - const { docs } = await getDocs( - query( - collection(db, "assignments"), - ...[ - where("assigner", "==", id), - ...(startDate ? [where("startDate", ">=", startDate.toISOString())] : []), - // firebase doesnt accept compound queries so we have to filter on the server - // ...endDate ? [where("endDate", "<=", endDate)] : [], - ] - ) - ); - if (endDate) { - return docs - .map((x) => ({ ...(x.data() as Assignment), id: x.id })) - .filter((x) => new Date(x.endDate) <= endDate) as Assignment[]; - } - return docs.map((x) => ({ ...x.data(), id: x.id })) as Assignment[]; +export const getAssignmentsByAssigner = async (id: string, startDate?: Date, endDate?: Date) => { + const {docs} = await getDocs( + query( + collection(db, "assignments"), + ...[ + where("assigner", "==", id), + ...(startDate ? [where("startDate", ">=", startDate.toISOString())] : []), + // firebase doesnt accept compound queries so we have to filter on the server + // ...endDate ? [where("endDate", "<=", endDate)] : [], + ], + ), + ); + if (endDate) { + return docs.map((x) => ({...(x.data() as Assignment), id: x.id})).filter((x) => new Date(x.endDate) <= endDate) as Assignment[]; + } + return docs.map((x) => ({...x.data(), id: x.id})) as Assignment[]; +}; +export const getAssignments = async () => { + const {docs} = await getDocs(collection(db, "assignments")); + return docs.map((x) => ({...x.data(), id: x.id})) as Assignment[]; }; -export const getAssignmentsByAssignerBetweenDates = async ( - id: string, - startDate: Date, - endDate: Date -) => { - const { docs } = await getDocs( - query(collection(db, "assignments"), where("assigner", "==", id)) - ); - return docs.map((x) => ({ ...x.data(), id: x.id })) as Assignment[]; +export const getAssignmentsByAssignerBetweenDates = async (id: string, startDate: Date, endDate: Date) => { + const {docs} = await getDocs(query(collection(db, "assignments"), where("assigner", "==", id))); + return docs.map((x) => ({...x.data(), id: x.id})) as Assignment[]; }; -export const getAssignmentsByAssigners = async ( - ids: string[], - startDate?: Date, - endDate?: Date -) => { - return ( - await Promise.all( - ids.map((id) => getAssignmentsByAssigner(id, startDate, endDate)) - ) - ).flat(); +export const getAssignmentsByAssigners = async (ids: string[], startDate?: Date, endDate?: Date) => { + return (await Promise.all(ids.map((id) => getAssignmentsByAssigner(id, startDate, endDate)))).flat(); }; diff --git a/src/utils/permissions.be.ts b/src/utils/permissions.be.ts index 1f03ffcc..93dffcba 100644 --- a/src/utils/permissions.be.ts +++ b/src/utils/permissions.be.ts @@ -1,99 +1,89 @@ -import { app, adminApp } from "@/firebase"; -import { getAuth } from "firebase-admin/auth"; +import {app, adminApp} from "@/firebase"; +import {getAuth} from "firebase-admin/auth"; -import { - collection, - deleteDoc, - doc, - getDoc, - getDocs, - getFirestore, - query, - setDoc, - where, -} from "firebase/firestore"; -import { - Permission, - PermissionType, - permissions, - PermissionTopic, -} from "@/interfaces/permissions"; -import { v4 } from "uuid"; +import {collection, deleteDoc, doc, getDoc, getDocs, getFirestore, query, setDoc, where} from "firebase/firestore"; +import {Permission, PermissionType, permissions, PermissionTopic} from "@/interfaces/permissions"; +import {v4} from "uuid"; const db = getFirestore(app); async function createPermission(topic: string, type: string) { - const permData = doc(db, "permissions", v4()); - const permDoc = await getDoc(permData); - if (permDoc.exists()) { - return true; - } + const permData = doc(db, "permissions", v4()); + const permDoc = await getDoc(permData); + if (permDoc.exists()) { + return true; + } - await setDoc(permData, { - type, - topic, - users: [], - }); + await setDoc(permData, { + type, + topic, + users: [], + }); } interface PermissionsHelperList { - topic: string; - type: string; + topic: string; + type: string; } export function getPermissions(userId: string | undefined, docs: Permission[]) { - if (!userId) { - return []; - } - // the concept is like a blacklist - // if the user exists in the list, he can't access this permission - // even if his profile allows - const permissions = docs.reduce((acc: PermissionType[], doc: Permission) => { - // typescript was complaining even with the validation on the top - if (doc.users.includes(userId)) { - return acc; - } + if (!userId) { + return []; + } + // the concept is like a blacklist + // if the user exists in the list, he can't access this permission + // even if his profile allows + const permissions = docs.reduce((acc: PermissionType[], doc: Permission) => { + // typescript was complaining even with the validation on the top + if (doc.users.includes(userId)) { + return acc; + } - return [...acc, doc.type]; - }, []) as PermissionType[]; - return permissions; + return [...acc, doc.type]; + }, []) as PermissionType[]; + return permissions; +} + +export async function getUserPermissions(id: string) { + const permissions = await getPermissionDocs(); + return getPermissions(id, permissions); } export async function bootstrap() { - await permissions - .reduce((accm: PermissionsHelperList[], permissionData) => { - return [ - ...accm, - ...permissionData.list.map((type) => ({ - topic: permissionData.topic, - type, - })), - ]; - }, []) - .forEach(async ({ topic, type }) => { - await createPermission(topic, type); - }); + await permissions + .reduce((accm: PermissionsHelperList[], permissionData) => { + return [ + ...accm, + ...permissionData.list.map((type) => ({ + topic: permissionData.topic, + type, + })), + ]; + }, []) + .forEach(async ({topic, type}) => { + await createPermission(topic, type); + }); } export async function getPermissionDoc(id: string) { - const docRef = doc(db, "permissions", id); - const docSnap = await getDoc(docRef); + const docRef = doc(db, "permissions", id); + const docSnap = await getDoc(docRef); - if (docSnap.exists()) { - return docSnap.data() as Permission; - } + if (docSnap.exists()) { + return docSnap.data() as Permission; + } - throw new Error("Permission not found"); + throw new Error("Permission not found"); } export async function getPermissionDocs() { - const q = query(collection(db, "permissions")); - // firebase is missing something like array-not-contains + const q = query(collection(db, "permissions")); + // firebase is missing something like array-not-contains - const snapshot = await getDocs(q); + const snapshot = await getDocs(q); - const docs = snapshot.docs.map((doc) => ({ - id: doc.id, - ...doc.data(), - })) as Permission[]; + const docs = snapshot.docs.map((doc) => ({ + id: doc.id, + ...doc.data(), + })) as Permission[]; - return docs; + return docs; } diff --git a/src/utils/users.be.ts b/src/utils/users.be.ts index b50467fb..be8d1f9b 100644 --- a/src/utils/users.be.ts +++ b/src/utils/users.be.ts @@ -5,21 +5,23 @@ import {CorporateUser, Group, User} from "@/interfaces/user"; import {getGroupsForUser} from "./groups.be"; import {uniq, uniqBy} from "lodash"; import {getUserCodes} from "./codes.be"; +import moment from "moment"; const db = getFirestore(app); export async function getUsers() { const snapshot = await getDocs(collection(db, "users")); return snapshot.docs.map((doc) => ({ - id: doc.id, ...doc.data(), - })) as User[]; + id: doc.id, + registrationDate: moment(doc.data().registrationDate).toISOString(), + })) as unknown as User[]; } export async function getUser(id: string) { const userDoc = await getDoc(doc(db, "users", id)); - return {...userDoc.data(), id} as User; + return {...userDoc.data(), id, registrationDate: moment(userDoc.data()?.registrationDate).toISOString()} as unknown as User; } export async function getSpecificUsers(ids: string[]) { @@ -28,9 +30,10 @@ export async function getSpecificUsers(ids: string[]) { const snapshot = await getDocs(query(collection(db, "users"), where("id", "in", ids))); const groups = snapshot.docs.map((doc) => ({ - id: doc.id, ...doc.data(), - })) as User[]; + id: doc.id, + registrationDate: moment(doc.data().registrationDate).toISOString(), + })) as unknown as User[]; return groups; }