Improved the overall stability and speed of the app
This commit is contained in:
@@ -212,7 +212,7 @@ export default function Sidebar({path, navDisabled = false, focusMode = false, u
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="fixed bottom-12 flex flex-col gap-0">
|
<div className="2xl:fixed bottom-12 flex flex-col gap-0 -2xl:mt-8">
|
||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
|
|||||||
@@ -55,7 +55,14 @@ const USER_TYPE_PERMISSIONS: {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function BatchCodeGenerator({user, onFinish}: {user: User; onFinish: () => 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 [infos, setInfos] = useState<{email: string; name: string; passport_id: string}[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [expiryDate, setExpiryDate] = useState<Date | null>(
|
const [expiryDate, setExpiryDate] = useState<Date | null>(
|
||||||
@@ -65,9 +72,6 @@ export default function BatchCodeGenerator({user, onFinish}: {user: User; onFini
|
|||||||
const [type, setType] = useState<Type>("student");
|
const [type, setType] = useState<Type>("student");
|
||||||
const [showHelp, setShowHelp] = useState(false);
|
const [showHelp, setShowHelp] = useState(false);
|
||||||
|
|
||||||
const {users} = useUsers();
|
|
||||||
const {permissions} = usePermissions(user?.id || "");
|
|
||||||
|
|
||||||
const {openFilePicker, filesContent, clear} = useFilePicker({
|
const {openFilePicker, filesContent, clear} = useFilePicker({
|
||||||
accept: ".xlsx",
|
accept: ".xlsx",
|
||||||
multiple: false,
|
multiple: false,
|
||||||
|
|||||||
@@ -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<
|
const [infos, setInfos] = useState<
|
||||||
{
|
{
|
||||||
email: string;
|
email: string;
|
||||||
@@ -83,9 +90,6 @@ export default function BatchCreateUser({user, onFinish}: {user: User; onFinish:
|
|||||||
const [type, setType] = useState<Type>("student");
|
const [type, setType] = useState<Type>("student");
|
||||||
const [showHelp, setShowHelp] = useState(false);
|
const [showHelp, setShowHelp] = useState(false);
|
||||||
|
|
||||||
const {users} = useUsers();
|
|
||||||
const {permissions} = usePermissions(user?.id || "");
|
|
||||||
|
|
||||||
const {openFilePicker, filesContent, clear} = useFilePicker({
|
const {openFilePicker, filesContent, clear} = useFilePicker({
|
||||||
accept: ".xlsx",
|
accept: ".xlsx",
|
||||||
multiple: false,
|
multiple: false,
|
||||||
|
|||||||
@@ -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<string>();
|
const [generatedCode, setGeneratedCode] = useState<string>();
|
||||||
const [expiryDate, setExpiryDate] = useState<Date | null>(
|
const [expiryDate, setExpiryDate] = useState<Date | null>(
|
||||||
user?.subscriptionExpirationDate ? moment(user.subscriptionExpirationDate).toDate() : null,
|
user?.subscriptionExpirationDate ? moment(user.subscriptionExpirationDate).toDate() : null,
|
||||||
);
|
);
|
||||||
const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true);
|
const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true);
|
||||||
const [type, setType] = useState<Type>("student");
|
const [type, setType] = useState<Type>("student");
|
||||||
const {permissions} = usePermissions(user?.id || "");
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isExpiryDateEnabled) setExpiryDate(null);
|
if (!isExpiryDateEnabled) setExpiryDate(null);
|
||||||
|
|||||||
@@ -9,10 +9,15 @@ import PackageList from "./PackageList";
|
|||||||
import UserList from "./UserList";
|
import UserList from "./UserList";
|
||||||
import {checkAccess} from "@/utils/permissions";
|
import {checkAccess} from "@/utils/permissions";
|
||||||
import usePermissions from "@/hooks/usePermissions";
|
import usePermissions from "@/hooks/usePermissions";
|
||||||
|
import {PermissionType} from "@/interfaces/permissions";
|
||||||
|
|
||||||
export default function Lists({user}: {user: User}) {
|
interface Props {
|
||||||
const {permissions} = usePermissions(user?.id || "");
|
user: User;
|
||||||
|
users: User[];
|
||||||
|
permissions: PermissionType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Lists({user, users, permissions}: Props) {
|
||||||
return (
|
return (
|
||||||
<TabGroup>
|
<TabGroup>
|
||||||
<TabList className="flex space-x-1 rounded-xl bg-mti-purple-ultralight/40 p-1">
|
<TabList className="flex space-x-1 rounded-xl bg-mti-purple-ultralight/40 p-1">
|
||||||
|
|||||||
@@ -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<string>();
|
const [name, setName] = useState<string>();
|
||||||
const [email, setEmail] = useState<string>();
|
const [email, setEmail] = useState<string>();
|
||||||
const [phone, setPhone] = useState<string>();
|
const [phone, setPhone] = useState<string>();
|
||||||
@@ -74,9 +81,7 @@ export default function UserCreator({user, onFinish}: {user: User; onFinish: ()
|
|||||||
const [type, setType] = useState<Type>("student");
|
const [type, setType] = useState<Type>("student");
|
||||||
const [position, setPosition] = useState<string>();
|
const [position, setPosition] = useState<string>();
|
||||||
|
|
||||||
const {permissions} = usePermissions(user?.id || "");
|
|
||||||
const {groups} = useGroups({admin: ["developer", "admin"].includes(user?.type) ? undefined : user?.id, userType: user?.type});
|
const {groups} = useGroups({admin: ["developer", "admin"].includes(user?.type) ? undefined : user?.id, userType: user?.type});
|
||||||
const {users} = useUsers();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isExpiryDateEnabled) setExpiryDate(null);
|
if (!isExpiryDateEnabled) setExpiryDate(null);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import Speaking from "@/exams/Speaking";
|
|||||||
import Writing from "@/exams/Writing";
|
import Writing from "@/exams/Writing";
|
||||||
import useUser from "@/hooks/useUser";
|
import useUser from "@/hooks/useUser";
|
||||||
import {Exam, LevelExam, UserSolution, Variant} from "@/interfaces/exam";
|
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 useExamStore from "@/stores/examStore";
|
||||||
import {evaluateSpeakingAnswer, evaluateWritingAnswer} from "@/utils/evaluation";
|
import {evaluateSpeakingAnswer, evaluateWritingAnswer} from "@/utils/evaluation";
|
||||||
import {defaultExamUserSolutions, getExam} from "@/utils/exams";
|
import {defaultExamUserSolutions, getExam} from "@/utils/exams";
|
||||||
@@ -28,9 +28,10 @@ import useGradingSystem from "@/hooks/useGrading";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
page: "exams" | "exercises";
|
page: "exams" | "exercises";
|
||||||
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ExamPage({page}: Props) {
|
export default function ExamPage({page, user}: Props) {
|
||||||
const [variant, setVariant] = useState<Variant>("full");
|
const [variant, setVariant] = useState<Variant>("full");
|
||||||
const [avoidRepeated, setAvoidRepeated] = useState(false);
|
const [avoidRepeated, setAvoidRepeated] = useState(false);
|
||||||
const [hasBeenUploaded, setHasBeenUploaded] = useState(false);
|
const [hasBeenUploaded, setHasBeenUploaded] = useState(false);
|
||||||
@@ -59,7 +60,6 @@ export default function ExamPage({page}: Props) {
|
|||||||
const {bgColor, setBgColor} = useExamStore((state) => state);
|
const {bgColor, setBgColor} = useExamStore((state) => state);
|
||||||
const setShuffleMaps = useExamStore((state) => state.setShuffles);
|
const setShuffleMaps = useExamStore((state) => state.setShuffles);
|
||||||
|
|
||||||
const {user} = useUser({redirectTo: "/login"});
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {sessionOptions} from "@/lib/session";
|
|||||||
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
||||||
import ExamPage from "./(exam)/ExamPage";
|
import ExamPage from "./(exam)/ExamPage";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
import {User} from "@/interfaces/user";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
@@ -14,7 +15,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
permanent: false,
|
permanent: false,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
redirect: {
|
redirect: {
|
||||||
destination: "/",
|
destination: "/",
|
||||||
permanent: false,
|
permanent: false,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +33,11 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
export default function Page() {
|
interface Props {
|
||||||
|
user: User;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Page({user}: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@@ -44,7 +49,7 @@ export default function Page() {
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<ExamPage page="exams" />
|
<ExamPage page="exams" user={user} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {sessionOptions} from "@/lib/session";
|
|||||||
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
||||||
import ExamPage from "./(exam)/ExamPage";
|
import ExamPage from "./(exam)/ExamPage";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
import {User} from "@/interfaces/user";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
@@ -14,7 +15,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
permanent: false,
|
permanent: false,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
redirect: {
|
redirect: {
|
||||||
destination: "/",
|
destination: "/",
|
||||||
permanent: false,
|
permanent: false,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +33,11 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
export default function Page() {
|
interface Props {
|
||||||
|
user: User;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Page({user}: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@@ -44,7 +49,7 @@ export default function Page() {
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<ExamPage page="exercises" />
|
<ExamPage page="exercises" user={user} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import WritingGeneration from "./(generation)/WritingGeneration";
|
|||||||
import LevelGeneration from "./(generation)/LevelGeneration";
|
import LevelGeneration from "./(generation)/LevelGeneration";
|
||||||
import SpeakingGeneration from "./(generation)/SpeakingGeneration";
|
import SpeakingGeneration from "./(generation)/SpeakingGeneration";
|
||||||
import {checkAccess} from "@/utils/permissions";
|
import {checkAccess} from "@/utils/permissions";
|
||||||
|
import {User} from "@/interfaces/user";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
@@ -49,10 +50,12 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
export default function Generation() {
|
interface Props {
|
||||||
const [module, setModule] = useState<Module>("reading");
|
user: User;
|
||||||
|
}
|
||||||
|
|
||||||
const {user} = useUser({redirectTo: "/login"});
|
export default function Generation({user}: Props) {
|
||||||
|
const [module, setModule] = useState<Module>("reading");
|
||||||
|
|
||||||
const [title, setTitle] = useState<string>("");
|
const [title, setTitle] = useState<string>("");
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import MasterCorporateDashboard from "@/dashboards/MasterCorporate";
|
|||||||
import PaymentDue from "./(status)/PaymentDue";
|
import PaymentDue from "./(status)/PaymentDue";
|
||||||
import {useRouter} from "next/router";
|
import {useRouter} from "next/router";
|
||||||
import {PayPalScriptProvider} from "@paypal/react-paypal-js";
|
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 Select from "react-select";
|
||||||
import {USER_TYPE_LABELS} from "@/resources/user";
|
import {USER_TYPE_LABELS} from "@/resources/user";
|
||||||
import {checkAccess, getTypesOfUser} from "@/utils/permissions";
|
import {checkAccess, getTypesOfUser} from "@/utils/permissions";
|
||||||
@@ -37,8 +37,10 @@ import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
|||||||
import useGroups from "@/hooks/useGroups";
|
import useGroups from "@/hooks/useGroups";
|
||||||
import useUsers from "@/hooks/useUsers";
|
import useUsers from "@/hooks/useUsers";
|
||||||
import {getUserName} from "@/utils/users";
|
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;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
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 {
|
return {
|
||||||
props: {user: req.session.user},
|
props: {user, groups, users},
|
||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: User;
|
user: User;
|
||||||
envVariables: {[key: string]: string};
|
groups: Group[];
|
||||||
|
users: User[];
|
||||||
}
|
}
|
||||||
export default function Home(props: Props) {
|
export default function Home({user, groups, users}: Props) {
|
||||||
const {user, mutateUser} = useUser({redirectTo: "/login"});
|
|
||||||
const {groups} = useGroups({});
|
|
||||||
const {users} = useUsers();
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log(groups);
|
|
||||||
}, [groups]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
|
|||||||
@@ -12,7 +12,16 @@ import Link from "next/link";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {ErrorMessage} from "@/constants/errors";
|
import {ErrorMessage} from "@/constants/errors";
|
||||||
import clsx from "clsx";
|
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 CountrySelect from "@/components/Low/CountrySelect";
|
||||||
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
@@ -34,8 +43,9 @@ import {capitalize} from "lodash";
|
|||||||
import TopicModal from "@/components/Medium/TopicModal";
|
import TopicModal from "@/components/Medium/TopicModal";
|
||||||
import {v4} from "uuid";
|
import {v4} from "uuid";
|
||||||
import {checkAccess, getTypesOfUser} from "@/utils/permissions";
|
import {checkAccess, getTypesOfUser} from "@/utils/permissions";
|
||||||
import {getUserCorporate} from "@/utils/groups.be";
|
import {getParticipantGroups, getUserCorporate} from "@/utils/groups.be";
|
||||||
import {InferGetServerSidePropsType} from "next";
|
import {InferGetServerSidePropsType} from "next";
|
||||||
|
import {getUsers} from "@/utils/users.be";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
@@ -59,19 +69,26 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
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);
|
}, sessionOptions);
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: User;
|
user: User;
|
||||||
|
groups: Group[];
|
||||||
|
users: User[];
|
||||||
mutateUser: Function;
|
mutateUser: Function;
|
||||||
linkedCorporate?: CorporateUser | MasterCorporateUser;
|
linkedCorporate?: CorporateUser | MasterCorporateUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DoubleColumnRow = ({children}: {children: ReactNode}) => <div className="flex flex-col lg:flex-row gap-8 w-full">{children}</div>;
|
const DoubleColumnRow = ({children}: {children: ReactNode}) => <div className="flex flex-col lg:flex-row gap-8 w-full">{children}</div>;
|
||||||
|
|
||||||
function UserProfile({user, mutateUser, linkedCorporate}: Props) {
|
function UserProfile({user, mutateUser, linkedCorporate, groups, users}: Props) {
|
||||||
const [bio, setBio] = useState(user.bio || "");
|
const [bio, setBio] = useState(user.bio || "");
|
||||||
const [name, setName] = useState(user.name || "");
|
const [name, setName] = useState(user.name || "");
|
||||||
const [email, setEmail] = useState(user.email || "");
|
const [email, setEmail] = useState(user.email || "");
|
||||||
@@ -114,9 +131,6 @@ function UserProfile({user, mutateUser, linkedCorporate}: Props) {
|
|||||||
|
|
||||||
const [isPreferredTopicsOpen, setIsPreferredTopicsOpen] = useState(false);
|
const [isPreferredTopicsOpen, setIsPreferredTopicsOpen] = useState(false);
|
||||||
|
|
||||||
const {groups} = useGroups({});
|
|
||||||
const {users} = useUsers();
|
|
||||||
|
|
||||||
const profilePictureInput = useRef(null);
|
const profilePictureInput = useRef(null);
|
||||||
const expirationDateColor = (date: Date) => {
|
const expirationDateColor = (date: Date) => {
|
||||||
const momentDate = moment(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"});
|
const {user, mutateUser} = useUser({redirectTo: "/login"});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -658,7 +672,7 @@ export default function Home({linkedCorporate}: {linkedCorporate?: CorporateUser
|
|||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
{user && <UserProfile linkedCorporate={linkedCorporate} user={user} mutateUser={mutateUser} />}
|
{user && <UserProfile user={user} mutateUser={mutateUser} {...props} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,31 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { withIronSessionSsr } from "iron-session/next";
|
import {withIronSessionSsr} from "iron-session/next";
|
||||||
import { sessionOptions } from "@/lib/session";
|
import {sessionOptions} from "@/lib/session";
|
||||||
import { Stat, User } from "@/interfaces/user";
|
import {Stat, User} from "@/interfaces/user";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import {useEffect, useRef, useState} from "react";
|
||||||
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
|
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
|
||||||
import { groupByDate } from "@/utils/stats";
|
import {groupByDate} from "@/utils/stats";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import useUsers from "@/hooks/useUsers";
|
import useUsers from "@/hooks/useUsers";
|
||||||
import useExamStore from "@/stores/examStore";
|
import useExamStore from "@/stores/examStore";
|
||||||
import { ToastContainer } from "react-toastify";
|
import {ToastContainer} from "react-toastify";
|
||||||
import Layout from "@/components/High/Layout";
|
import Layout from "@/components/High/Layout";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { shouldRedirectHome } from "@/utils/navigation.disabled";
|
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
||||||
import useAssignments from "@/hooks/useAssignments";
|
import useAssignments from "@/hooks/useAssignments";
|
||||||
import { uuidv4 } from "@firebase/util";
|
import {uuidv4} from "@firebase/util";
|
||||||
import { usePDFDownload } from "@/hooks/usePDFDownload";
|
import {usePDFDownload} from "@/hooks/usePDFDownload";
|
||||||
import useRecordStore from "@/stores/recordStore";
|
import useRecordStore from "@/stores/recordStore";
|
||||||
import StatsGridItem from "@/components/Medium/StatGridItem";
|
import StatsGridItem from "@/components/Medium/StatGridItem";
|
||||||
import RecordFilter from "@/components/Medium/RecordFilter";
|
import RecordFilter from "@/components/Medium/RecordFilter";
|
||||||
import { useRouter } from "next/router";
|
import {useRouter} from "next/router";
|
||||||
import useTrainingContentStore from "@/stores/trainingContentStore";
|
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;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user || !user.isVerified) {
|
||||||
@@ -43,14 +46,23 @@ export const getServerSideProps = withIronSessionSsr(({ req, res }) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const users = await getUsers();
|
||||||
|
const assignments = await getAssignments();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: { user: req.session.user },
|
props: {user, users, assignments},
|
||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
type Filter = "months" | "weeks" | "days" | "assignments" | undefined;
|
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 router = useRouter();
|
||||||
const [statsUserId, setStatsUserId, training, setTraining] = useRecordStore((state) => [
|
const [statsUserId, setStatsUserId, training, setTraining] = useRecordStore((state) => [
|
||||||
state.selectedUser,
|
state.selectedUser,
|
||||||
@@ -60,12 +72,10 @@ export default function History({ user }: { user: User }) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// const [statsUserId, setStatsUserId] = useState<string | undefined>(user.id);
|
// const [statsUserId, setStatsUserId] = useState<string | undefined>(user.id);
|
||||||
const [groupedStats, setGroupedStats] = useState<{ [key: string]: Stat[] }>();
|
const [groupedStats, setGroupedStats] = useState<{[key: string]: Stat[]}>();
|
||||||
const [filter, setFilter] = useState<Filter>();
|
const [filter, setFilter] = useState<Filter>();
|
||||||
const { assignments } = useAssignments({});
|
|
||||||
|
|
||||||
const { users } = useUsers();
|
const {data: stats, isLoading: isStatsLoading} = useFilterRecordsByUser<Stat[]>(statsUserId || user?.id);
|
||||||
const { data: stats, isLoading: isStatsLoading } = useFilterRecordsByUser<Stat[]>(statsUserId || user?.id);
|
|
||||||
|
|
||||||
const setExams = useExamStore((state) => state.setExams);
|
const setExams = useExamStore((state) => state.setExams);
|
||||||
const setShowSolutions = useExamStore((state) => state.setShowSolutions);
|
const setShowSolutions = useExamStore((state) => state.setShowSolutions);
|
||||||
@@ -100,12 +110,12 @@ export default function History({ user }: { user: User }) {
|
|||||||
// if (!statsUserId) setStatsUserId(user.id);
|
// if (!statsUserId) setStatsUserId(user.id);
|
||||||
// }, []);
|
// }, []);
|
||||||
|
|
||||||
const filterStatsByDate = (stats: { [key: string]: Stat[] }) => {
|
const filterStatsByDate = (stats: {[key: string]: Stat[]}) => {
|
||||||
if (filter && filter !== "assignments") {
|
if (filter && filter !== "assignments") {
|
||||||
const filterDate = moment()
|
const filterDate = moment()
|
||||||
.subtract({ [filter as string]: 1 })
|
.subtract({[filter as string]: 1})
|
||||||
.format("x");
|
.format("x");
|
||||||
const filteredStats: { [key: string]: Stat[] } = {};
|
const filteredStats: {[key: string]: Stat[]} = {};
|
||||||
|
|
||||||
Object.keys(stats).forEach((timestamp) => {
|
Object.keys(stats).forEach((timestamp) => {
|
||||||
if (timestamp >= filterDate) filteredStats[timestamp] = stats[timestamp];
|
if (timestamp >= filterDate) filteredStats[timestamp] = stats[timestamp];
|
||||||
@@ -114,7 +124,7 @@ export default function History({ user }: { user: User }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (filter && filter === "assignments") {
|
if (filter && filter === "assignments") {
|
||||||
const filteredStats: { [key: string]: Stat[] } = {};
|
const filteredStats: {[key: string]: Stat[]} = {};
|
||||||
|
|
||||||
Object.keys(stats).forEach((timestamp) => {
|
Object.keys(stats).forEach((timestamp) => {
|
||||||
if (stats[timestamp].map((s) => s.assignment === undefined).includes(false))
|
if (stats[timestamp].map((s) => s.assignment === undefined).includes(false))
|
||||||
@@ -127,7 +137,6 @@ export default function History({ user }: { user: User }) {
|
|||||||
return stats;
|
return stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const MAX_TRAINING_EXAMS = 10;
|
const MAX_TRAINING_EXAMS = 10;
|
||||||
const [selectedTrainingExams, setSelectedTrainingExams] = useState<string[]>([]);
|
const [selectedTrainingExams, setSelectedTrainingExams] = useState<string[]>([]);
|
||||||
const setTrainingStats = useTrainingContentStore((state) => state.setStats);
|
const setTrainingStats = useTrainingContentStore((state) => state.setStats);
|
||||||
@@ -200,7 +209,7 @@ export default function History({ user }: { user: User }) {
|
|||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
{user && (
|
{user && (
|
||||||
<Layout user={user}>
|
<Layout user={user}>
|
||||||
<RecordFilter user={user} filterState={{ filter: filter, setFilter: setFilter }} >
|
<RecordFilter user={user} filterState={{filter: filter, setFilter: setFilter}}>
|
||||||
{training && (
|
{training && (
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<div className="font-semibold text-2xl mr-4">
|
<div className="font-semibold text-2xl mr-4">
|
||||||
|
|||||||
@@ -24,8 +24,12 @@ import UserCreator from "./(admin)/UserCreator";
|
|||||||
import CorporateGradingSystem from "./(admin)/CorporateGradingSystem";
|
import CorporateGradingSystem from "./(admin)/CorporateGradingSystem";
|
||||||
import useGradingSystem from "@/hooks/useGrading";
|
import useGradingSystem from "@/hooks/useGrading";
|
||||||
import {CEFR_STEPS} from "@/resources/grading";
|
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;
|
const user = req.session.user;
|
||||||
if (!user || !user.isVerified) {
|
if (!user || !user.isVerified) {
|
||||||
return {
|
return {
|
||||||
@@ -45,14 +49,21 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const permissions = await getUserPermissions(user.id);
|
||||||
|
const users = await getUsers();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {user: req.session.user},
|
props: {user, permissions, users},
|
||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
export default function Admin() {
|
interface Props {
|
||||||
const {user} = useUser({redirectTo: "/login"});
|
user: User;
|
||||||
const {permissions} = usePermissions(user?.id || "");
|
users: User[];
|
||||||
|
permissions: PermissionType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Admin({user, users, permissions}: Props) {
|
||||||
const {gradingSystem, mutate} = useGradingSystem();
|
const {gradingSystem, mutate} = useGradingSystem();
|
||||||
|
|
||||||
const [modalOpen, setModalOpen] = useState<string>();
|
const [modalOpen, setModalOpen] = useState<string>();
|
||||||
@@ -69,80 +80,78 @@ export default function Admin() {
|
|||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
{user && (
|
<Layout user={user} className="gap-6">
|
||||||
<Layout user={user} className="gap-6">
|
<Modal isOpen={modalOpen === "batchCreateUser"} onClose={() => setModalOpen(undefined)}>
|
||||||
<Modal isOpen={modalOpen === "batchCreateUser"} onClose={() => setModalOpen(undefined)}>
|
<BatchCreateUser user={user} users={users} permissions={permissions} onFinish={() => setModalOpen(undefined)} />
|
||||||
<BatchCreateUser user={user} onFinish={() => setModalOpen(undefined)} />
|
</Modal>
|
||||||
</Modal>
|
<Modal isOpen={modalOpen === "batchCreateCode"} onClose={() => setModalOpen(undefined)}>
|
||||||
<Modal isOpen={modalOpen === "batchCreateCode"} onClose={() => setModalOpen(undefined)}>
|
<BatchCodeGenerator user={user} users={users} permissions={permissions} onFinish={() => setModalOpen(undefined)} />
|
||||||
<BatchCodeGenerator user={user} onFinish={() => setModalOpen(undefined)} />
|
</Modal>
|
||||||
</Modal>
|
<Modal isOpen={modalOpen === "createCode"} onClose={() => setModalOpen(undefined)}>
|
||||||
<Modal isOpen={modalOpen === "createCode"} onClose={() => setModalOpen(undefined)}>
|
<CodeGenerator user={user} permissions={permissions} onFinish={() => setModalOpen(undefined)} />
|
||||||
<CodeGenerator user={user} onFinish={() => setModalOpen(undefined)} />
|
</Modal>
|
||||||
</Modal>
|
<Modal isOpen={modalOpen === "createUser"} onClose={() => setModalOpen(undefined)}>
|
||||||
<Modal isOpen={modalOpen === "createUser"} onClose={() => setModalOpen(undefined)}>
|
<UserCreator user={user} users={users} permissions={permissions} onFinish={() => setModalOpen(undefined)} />
|
||||||
<UserCreator user={user} onFinish={() => setModalOpen(undefined)} />
|
</Modal>
|
||||||
</Modal>
|
<Modal isOpen={modalOpen === "gradingSystem"} onClose={() => setModalOpen(undefined)}>
|
||||||
<Modal isOpen={modalOpen === "gradingSystem"} onClose={() => setModalOpen(undefined)}>
|
<CorporateGradingSystem
|
||||||
<CorporateGradingSystem
|
user={user}
|
||||||
user={user}
|
defaultSteps={gradingSystem?.steps || CEFR_STEPS}
|
||||||
defaultSteps={gradingSystem?.steps || CEFR_STEPS}
|
mutate={(steps) => {
|
||||||
mutate={(steps) => {
|
mutate({user: user.id, steps});
|
||||||
mutate({user: user.id, steps});
|
setModalOpen(undefined);
|
||||||
setModalOpen(undefined);
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</Modal>
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<section className="w-full grid grid-cols-2 -md:grid-cols-1 gap-8">
|
<section className="w-full grid grid-cols-2 -md:grid-cols-1 gap-8">
|
||||||
<ExamLoader />
|
<ExamLoader />
|
||||||
{checkAccess(user, getTypesOfUser(["teacher"]), permissions, "viewCodes") && (
|
{checkAccess(user, getTypesOfUser(["teacher"]), permissions, "viewCodes") && (
|
||||||
<div className="w-full grid grid-cols-2 gap-4">
|
<div className="w-full grid grid-cols-2 gap-4">
|
||||||
|
<IconCard
|
||||||
|
Icon={BsCode}
|
||||||
|
label="Generate Single Code"
|
||||||
|
color="purple"
|
||||||
|
className="w-full h-full"
|
||||||
|
onClick={() => setModalOpen("createCode")}
|
||||||
|
/>
|
||||||
|
<IconCard
|
||||||
|
Icon={BsCodeSquare}
|
||||||
|
label="Generate Codes in Batch"
|
||||||
|
color="purple"
|
||||||
|
className="w-full h-full"
|
||||||
|
onClick={() => setModalOpen("batchCreateCode")}
|
||||||
|
/>
|
||||||
|
<IconCard
|
||||||
|
Icon={BsPersonFill}
|
||||||
|
label="Create Single User"
|
||||||
|
color="purple"
|
||||||
|
className="w-full h-full"
|
||||||
|
onClick={() => setModalOpen("createUser")}
|
||||||
|
/>
|
||||||
|
<IconCard
|
||||||
|
Icon={BsPeopleFill}
|
||||||
|
label="Create Users in Batch"
|
||||||
|
color="purple"
|
||||||
|
className="w-full h-full"
|
||||||
|
onClick={() => setModalOpen("batchCreateUser")}
|
||||||
|
/>
|
||||||
|
{checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) && (
|
||||||
<IconCard
|
<IconCard
|
||||||
Icon={BsCode}
|
Icon={BsGearFill}
|
||||||
label="Generate Single Code"
|
label="Grading System"
|
||||||
color="purple"
|
color="purple"
|
||||||
className="w-full h-full"
|
className="w-full h-full col-span-2"
|
||||||
onClick={() => setModalOpen("createCode")}
|
onClick={() => setModalOpen("gradingSystem")}
|
||||||
/>
|
/>
|
||||||
<IconCard
|
)}
|
||||||
Icon={BsCodeSquare}
|
</div>
|
||||||
label="Generate Codes in Batch"
|
)}
|
||||||
color="purple"
|
</section>
|
||||||
className="w-full h-full"
|
<section className="w-full">
|
||||||
onClick={() => setModalOpen("batchCreateCode")}
|
<Lists user={user} users={users} permissions={permissions} />
|
||||||
/>
|
</section>
|
||||||
<IconCard
|
</Layout>
|
||||||
Icon={BsPersonFill}
|
|
||||||
label="Create Single User"
|
|
||||||
color="purple"
|
|
||||||
className="w-full h-full"
|
|
||||||
onClick={() => setModalOpen("createUser")}
|
|
||||||
/>
|
|
||||||
<IconCard
|
|
||||||
Icon={BsPeopleFill}
|
|
||||||
label="Create Users in Batch"
|
|
||||||
color="purple"
|
|
||||||
className="w-full h-full"
|
|
||||||
onClick={() => setModalOpen("batchCreateUser")}
|
|
||||||
/>
|
|
||||||
{checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) && (
|
|
||||||
<IconCard
|
|
||||||
Icon={BsGearFill}
|
|
||||||
label="Grading System"
|
|
||||||
color="purple"
|
|
||||||
className="w-full h-full col-span-2"
|
|
||||||
onClick={() => setModalOpen("gradingSystem")}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
<section className="w-full">
|
|
||||||
<Lists user={user} />
|
|
||||||
</section>
|
|
||||||
</Layout>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,36 @@
|
|||||||
import { app } from "@/firebase";
|
import {app} from "@/firebase";
|
||||||
import { Assignment } from "@/interfaces/results";
|
import {Assignment} from "@/interfaces/results";
|
||||||
import {
|
import {collection, getDocs, getFirestore, query, where} from "firebase/firestore";
|
||||||
collection,
|
|
||||||
getDocs,
|
|
||||||
getFirestore,
|
|
||||||
query,
|
|
||||||
where,
|
|
||||||
} from "firebase/firestore";
|
|
||||||
|
|
||||||
const db = getFirestore(app);
|
const db = getFirestore(app);
|
||||||
|
|
||||||
export const getAssignmentsByAssigner = async (
|
export const getAssignmentsByAssigner = async (id: string, startDate?: Date, endDate?: Date) => {
|
||||||
id: string,
|
const {docs} = await getDocs(
|
||||||
startDate?: Date,
|
query(
|
||||||
endDate?: Date
|
collection(db, "assignments"),
|
||||||
) => {
|
...[
|
||||||
const { docs } = await getDocs(
|
where("assigner", "==", id),
|
||||||
query(
|
...(startDate ? [where("startDate", ">=", startDate.toISOString())] : []),
|
||||||
collection(db, "assignments"),
|
// firebase doesnt accept compound queries so we have to filter on the server
|
||||||
...[
|
// ...endDate ? [where("endDate", "<=", endDate)] : [],
|
||||||
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[];
|
||||||
if (endDate) {
|
};
|
||||||
return docs
|
export const getAssignments = async () => {
|
||||||
.map((x) => ({ ...(x.data() as Assignment), id: x.id }))
|
const {docs} = await getDocs(collection(db, "assignments"));
|
||||||
.filter((x) => new Date(x.endDate) <= endDate) as Assignment[];
|
return docs.map((x) => ({...x.data(), id: x.id})) as Assignment[];
|
||||||
}
|
|
||||||
return docs.map((x) => ({ ...x.data(), id: x.id })) as Assignment[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAssignmentsByAssignerBetweenDates = async (
|
export const getAssignmentsByAssignerBetweenDates = async (id: string, startDate: Date, endDate: Date) => {
|
||||||
id: string,
|
const {docs} = await getDocs(query(collection(db, "assignments"), where("assigner", "==", id)));
|
||||||
startDate: Date,
|
return docs.map((x) => ({...x.data(), id: x.id})) as Assignment[];
|
||||||
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 (
|
export const getAssignmentsByAssigners = async (ids: string[], startDate?: Date, endDate?: Date) => {
|
||||||
ids: string[],
|
return (await Promise.all(ids.map((id) => getAssignmentsByAssigner(id, startDate, endDate)))).flat();
|
||||||
startDate?: Date,
|
|
||||||
endDate?: Date
|
|
||||||
) => {
|
|
||||||
return (
|
|
||||||
await Promise.all(
|
|
||||||
ids.map((id) => getAssignmentsByAssigner(id, startDate, endDate))
|
|
||||||
)
|
|
||||||
).flat();
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,99 +1,89 @@
|
|||||||
import { app, adminApp } from "@/firebase";
|
import {app, adminApp} from "@/firebase";
|
||||||
import { getAuth } from "firebase-admin/auth";
|
import {getAuth} from "firebase-admin/auth";
|
||||||
|
|
||||||
import {
|
import {collection, deleteDoc, doc, getDoc, getDocs, getFirestore, query, setDoc, where} from "firebase/firestore";
|
||||||
collection,
|
import {Permission, PermissionType, permissions, PermissionTopic} from "@/interfaces/permissions";
|
||||||
deleteDoc,
|
import {v4} from "uuid";
|
||||||
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);
|
const db = getFirestore(app);
|
||||||
|
|
||||||
async function createPermission(topic: string, type: string) {
|
async function createPermission(topic: string, type: string) {
|
||||||
const permData = doc(db, "permissions", v4());
|
const permData = doc(db, "permissions", v4());
|
||||||
const permDoc = await getDoc(permData);
|
const permDoc = await getDoc(permData);
|
||||||
if (permDoc.exists()) {
|
if (permDoc.exists()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await setDoc(permData, {
|
await setDoc(permData, {
|
||||||
type,
|
type,
|
||||||
topic,
|
topic,
|
||||||
users: [],
|
users: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
interface PermissionsHelperList {
|
interface PermissionsHelperList {
|
||||||
topic: string;
|
topic: string;
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
export function getPermissions(userId: string | undefined, docs: Permission[]) {
|
export function getPermissions(userId: string | undefined, docs: Permission[]) {
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
// the concept is like a blacklist
|
// the concept is like a blacklist
|
||||||
// if the user exists in the list, he can't access this permission
|
// if the user exists in the list, he can't access this permission
|
||||||
// even if his profile allows
|
// even if his profile allows
|
||||||
const permissions = docs.reduce((acc: PermissionType[], doc: Permission) => {
|
const permissions = docs.reduce((acc: PermissionType[], doc: Permission) => {
|
||||||
// typescript was complaining even with the validation on the top
|
// typescript was complaining even with the validation on the top
|
||||||
if (doc.users.includes(userId)) {
|
if (doc.users.includes(userId)) {
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [...acc, doc.type];
|
return [...acc, doc.type];
|
||||||
}, []) as PermissionType[];
|
}, []) as PermissionType[];
|
||||||
return permissions;
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserPermissions(id: string) {
|
||||||
|
const permissions = await getPermissionDocs();
|
||||||
|
return getPermissions(id, permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function bootstrap() {
|
export async function bootstrap() {
|
||||||
await permissions
|
await permissions
|
||||||
.reduce((accm: PermissionsHelperList[], permissionData) => {
|
.reduce((accm: PermissionsHelperList[], permissionData) => {
|
||||||
return [
|
return [
|
||||||
...accm,
|
...accm,
|
||||||
...permissionData.list.map((type) => ({
|
...permissionData.list.map((type) => ({
|
||||||
topic: permissionData.topic,
|
topic: permissionData.topic,
|
||||||
type,
|
type,
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
}, [])
|
}, [])
|
||||||
.forEach(async ({ topic, type }) => {
|
.forEach(async ({topic, type}) => {
|
||||||
await createPermission(topic, type);
|
await createPermission(topic, type);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPermissionDoc(id: string) {
|
export async function getPermissionDoc(id: string) {
|
||||||
const docRef = doc(db, "permissions", id);
|
const docRef = doc(db, "permissions", id);
|
||||||
const docSnap = await getDoc(docRef);
|
const docSnap = await getDoc(docRef);
|
||||||
|
|
||||||
if (docSnap.exists()) {
|
if (docSnap.exists()) {
|
||||||
return docSnap.data() as Permission;
|
return docSnap.data() as Permission;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("Permission not found");
|
throw new Error("Permission not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPermissionDocs() {
|
export async function getPermissionDocs() {
|
||||||
const q = query(collection(db, "permissions"));
|
const q = query(collection(db, "permissions"));
|
||||||
// firebase is missing something like array-not-contains
|
// firebase is missing something like array-not-contains
|
||||||
|
|
||||||
const snapshot = await getDocs(q);
|
const snapshot = await getDocs(q);
|
||||||
|
|
||||||
const docs = snapshot.docs.map((doc) => ({
|
const docs = snapshot.docs.map((doc) => ({
|
||||||
id: doc.id,
|
id: doc.id,
|
||||||
...doc.data(),
|
...doc.data(),
|
||||||
})) as Permission[];
|
})) as Permission[];
|
||||||
|
|
||||||
return docs;
|
return docs;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,23 @@ import {CorporateUser, Group, User} from "@/interfaces/user";
|
|||||||
import {getGroupsForUser} from "./groups.be";
|
import {getGroupsForUser} from "./groups.be";
|
||||||
import {uniq, uniqBy} from "lodash";
|
import {uniq, uniqBy} from "lodash";
|
||||||
import {getUserCodes} from "./codes.be";
|
import {getUserCodes} from "./codes.be";
|
||||||
|
import moment from "moment";
|
||||||
const db = getFirestore(app);
|
const db = getFirestore(app);
|
||||||
|
|
||||||
export async function getUsers() {
|
export async function getUsers() {
|
||||||
const snapshot = await getDocs(collection(db, "users"));
|
const snapshot = await getDocs(collection(db, "users"));
|
||||||
|
|
||||||
return snapshot.docs.map((doc) => ({
|
return snapshot.docs.map((doc) => ({
|
||||||
id: doc.id,
|
|
||||||
...doc.data(),
|
...doc.data(),
|
||||||
})) as User[];
|
id: doc.id,
|
||||||
|
registrationDate: moment(doc.data().registrationDate).toISOString(),
|
||||||
|
})) as unknown as User[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUser(id: string) {
|
export async function getUser(id: string) {
|
||||||
const userDoc = await getDoc(doc(db, "users", id));
|
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[]) {
|
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 snapshot = await getDocs(query(collection(db, "users"), where("id", "in", ids)));
|
||||||
|
|
||||||
const groups = snapshot.docs.map((doc) => ({
|
const groups = snapshot.docs.map((doc) => ({
|
||||||
id: doc.id,
|
|
||||||
...doc.data(),
|
...doc.data(),
|
||||||
})) as User[];
|
id: doc.id,
|
||||||
|
registrationDate: moment(doc.data().registrationDate).toISOString(),
|
||||||
|
})) as unknown as User[];
|
||||||
|
|
||||||
return groups;
|
return groups;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user