diff --git a/src/dashboards/IconCard.tsx b/src/dashboards/IconCard.tsx
index 5fd55486..b1ce3c7a 100644
--- a/src/dashboards/IconCard.tsx
+++ b/src/dashboards/IconCard.tsx
@@ -6,11 +6,12 @@ interface Props {
label: string;
value?: string | number;
color: "purple" | "rose" | "red" | "green";
+ className?: string;
tooltip?: string;
onClick?: () => void;
}
-export default function IconCard({Icon, label, value, color, tooltip, onClick}: Props) {
+export default function IconCard({Icon, label, value, color, tooltip, className, onClick}: Props) {
const colorClasses: {[key in typeof color]: string} = {
purple: "text-mti-purple-light",
red: "text-mti-red-light",
@@ -24,6 +25,7 @@ export default function IconCard({Icon, label, value, color, tooltip, onClick}:
className={clsx(
"bg-white rounded-xl shadow p-4 flex flex-col gap-4 items-center text-center w-52 h-52 justify-center cursor-pointer hover:shadow-xl transition ease-in-out duration-300",
tooltip && "tooltip tooltip-bottom",
+ className,
)}
data-tip={tooltip}>
diff --git a/src/pages/(admin)/BatchCreateUser.tsx b/src/pages/(admin)/BatchCreateUser.tsx
index 409b710a..a1d9b856 100644
--- a/src/pages/(admin)/BatchCreateUser.tsx
+++ b/src/pages/(admin)/BatchCreateUser.tsx
@@ -104,7 +104,7 @@ export default function BatchCreateUser({user}: {user: User}) {
const information = uniqBy(
rows
.map((row) => {
- const [firstName, lastName, country, passport_id, email, phone, group, studentID] = row as string[];
+ const [firstName, lastName, country, passport_id, email, phone, group, studentID, corporate] = row as string[];
const countryItem =
countryCodes.findOne("countryCode" as any, country.toUpperCase()) ||
countryCodes.all().find((x) => x.countryNameEn.toLowerCase() === country.toLowerCase());
@@ -116,6 +116,7 @@ export default function BatchCreateUser({user}: {user: User}) {
type: type,
passport_id: passport_id?.toString().trim() || undefined,
groupName: group,
+ corporate,
studentID,
demographicInformation: {
country: countryItem?.countryCode,
@@ -184,6 +185,7 @@ export default function BatchCreateUser({user}: {user: User}) {
Phone Number |
Group Name |
Student ID |
+ {user?.type !== "corporate" && Corporate (e-mail) | }
diff --git a/src/pages/(admin)/Lists/UserList.tsx b/src/pages/(admin)/Lists/UserList.tsx
index 4f72bfc9..12219c08 100644
--- a/src/pages/(admin)/Lists/UserList.tsx
+++ b/src/pages/(admin)/Lists/UserList.tsx
@@ -181,52 +181,6 @@ export default function UserList({
};
return (
- {checkAccess(user, updateUserPermission.list, permissions, updateUserPermission.perm) && (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )}
{!row.original.isVerified && checkAccess(user, updateUserPermission.list, permissions, updateUserPermission.perm) && (
verifyAccount(row.original)}>
diff --git a/src/pages/(admin)/UserCreator.tsx b/src/pages/(admin)/UserCreator.tsx
new file mode 100644
index 00000000..a0b955f5
--- /dev/null
+++ b/src/pages/(admin)/UserCreator.tsx
@@ -0,0 +1,266 @@
+import Button from "@/components/Low/Button";
+import Checkbox from "@/components/Low/Checkbox";
+import {PERMISSIONS} from "@/constants/userPermissions";
+import {CorporateUser, TeacherUser, Type, User} from "@/interfaces/user";
+import {USER_TYPE_LABELS} from "@/resources/user";
+import axios from "axios";
+import clsx from "clsx";
+import {capitalize, uniqBy} from "lodash";
+import moment from "moment";
+import {useEffect, useState} from "react";
+import ReactDatePicker from "react-datepicker";
+import {toast} from "react-toastify";
+import ShortUniqueId from "short-unique-id";
+import {checkAccess, getTypesOfUser} from "@/utils/permissions";
+import {PermissionType} from "@/interfaces/permissions";
+import usePermissions from "@/hooks/usePermissions";
+import Input from "@/components/Low/Input";
+import CountrySelect from "@/components/Low/CountrySelect";
+import useGroups from "@/hooks/useGroups";
+import useUsers from "@/hooks/useUsers";
+import {getUserName} from "@/utils/users";
+import Select from "@/components/Low/Select";
+
+const USER_TYPE_PERMISSIONS: {
+ [key in Type]: {perm: PermissionType | undefined; list: Type[]};
+} = {
+ student: {
+ perm: "createCodeStudent",
+ list: [],
+ },
+ teacher: {
+ perm: "createCodeTeacher",
+ list: [],
+ },
+ agent: {
+ perm: "createCodeCountryManager",
+ list: ["student", "teacher", "corporate", "mastercorporate"],
+ },
+ corporate: {
+ perm: "createCodeCorporate",
+ list: ["student", "teacher"],
+ },
+ mastercorporate: {
+ perm: undefined,
+ list: ["student", "teacher", "corporate"],
+ },
+ admin: {
+ perm: "createCodeAdmin",
+ list: ["student", "teacher", "agent", "corporate", "admin", "mastercorporate"],
+ },
+ developer: {
+ perm: undefined,
+ list: ["student", "teacher", "agent", "corporate", "admin", "developer", "mastercorporate"],
+ },
+};
+
+export default function UserCreator({user}: {user: User}) {
+ const [name, setName] = useState
();
+ const [email, setEmail] = useState();
+ const [phone, setPhone] = useState();
+ const [passportID, setPassportID] = useState();
+ const [studentID, setStudentID] = useState();
+ const [country, setCountry] = useState(user?.demographicInformation?.country);
+ const [group, setGroup] = useState();
+ const [availableCorporates, setAvailableCorporates] = useState([]);
+ const [selectedCorporate, setSelectedCorporate] = useState();
+ const [password, setPassword] = useState();
+ const [confirmPassword, setConfirmPassword] = useState();
+ const [expiryDate, setExpiryDate] = useState(
+ user?.subscriptionExpirationDate ? moment(user?.subscriptionExpirationDate).toDate() : null,
+ );
+ const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true);
+ const [isLoading, setIsLoading] = useState(false);
+ const [type, setType] = useState("student");
+
+ 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);
+ }, [isExpiryDateEnabled]);
+
+ useEffect(() => {
+ setAvailableCorporates(
+ uniqBy(
+ users.filter((u) => u.type === "corporate" && groups.flatMap((g) => g.participants).includes(u.id)),
+ "id",
+ ),
+ );
+ }, [users, groups]);
+
+ const createUser = () => {
+ if (!name || name.trim().length === 0) return toast.error("Please enter a valid name!");
+ if (!email || email.trim().length === 0) return toast.error("Please enter a valid e-mail address!");
+ if (users.map((x) => x.email).includes(email.trim())) return toast.error("That e-mail is already in use!");
+ if (!password || password.trim().length === 0) return toast.error("Please enter a valid password!");
+ if (password !== confirmPassword) return toast.error("The passwords do not match!");
+
+ setIsLoading(true);
+
+ const body = {
+ name,
+ email,
+ password,
+ groupID: group,
+ type,
+ studentID: type === "student" ? studentID : undefined,
+ expiryDate,
+ demographicInformation: {
+ passport_id: type === "student" ? passportID : undefined,
+ phone,
+ country,
+ },
+ };
+
+ axios
+ .post("/api/make_user", body)
+ .then(() => {
+ toast.success("That user has been created!");
+
+ setName("");
+ setEmail("");
+ setPhone("");
+ setPassportID("");
+ setStudentID("");
+ setCountry(user?.demographicInformation?.country);
+ setGroup(null);
+ setSelectedCorporate(null);
+ setExpiryDate(user?.subscriptionExpirationDate ? moment(user?.subscriptionExpirationDate).toDate() : null);
+ setIsExpiryDateEnabled(true);
+ setType("student");
+ })
+ .catch(() => toast.error("Something went wrong! Please try again later!"))
+ .finally(() => setIsLoading(false));
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {type === "student" && (
+ <>
+
+
+ >
+ )}
+
+ {["student", "teacher"].includes(type) && !["corporate", "teacher"].includes(user?.type) && (
+
+
+
+ )}
+
+
+
+
+
+
+
+ {user && (
+
+ )}
+
+
+
+ {user && checkAccess(user, ["developer", "admin", "corporate", "mastercorporate"]) && (
+ <>
+
+
+
+ Enabled
+
+
+ {isExpiryDateEnabled && (
+
+ moment(date).isAfter(new Date()) &&
+ (user?.subscriptionExpirationDate ? moment(date).isBefore(user?.subscriptionExpirationDate) : true)
+ }
+ dateFormat="dd/MM/yyyy"
+ selected={expiryDate}
+ onChange={(date) => setExpiryDate(date)}
+ />
+ )}
+ >
+ )}
+
+
+
+
+
+ );
+}
diff --git a/src/pages/api/make_user.ts b/src/pages/api/make_user.ts
index bdcb14a2..a8ceab07 100644
--- a/src/pages/api/make_user.ts
+++ b/src/pages/api/make_user.ts
@@ -4,7 +4,7 @@ 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 {Group} from "@/interfaces/user";
+import {CorporateUser, Group} from "@/interfaces/user";
import {createUserWithEmailAndPassword, getAuth} from "firebase/auth";
const DEFAULT_DESIRED_LEVELS = {
@@ -37,19 +37,25 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
if (!maker) {
return res.status(401).json({ok: false, reason: "You must be logged in to make user!"});
}
- const {email, passport_id, type, groupName, expiryDate} = req.body as {
+ const {email, passport_id, password, type, groupName, groupID, expiryDate, corporate} = req.body as {
email: string;
+ password?: string;
passport_id: string;
type: string;
- groupName: string;
+ groupName?: string;
+ groupID?: string;
+ corporate?: string;
expiryDate: null | Date;
};
// cleaning data
delete req.body.passport_id;
delete req.body.groupName;
+ delete req.body.groupID;
delete req.body.expiryDate;
+ delete req.body.password;
+ delete req.body.corporate;
- await createUserWithEmailAndPassword(auth, email.toLowerCase(), passport_id)
+ await createUserWithEmailAndPassword(auth, email.toLowerCase(), !!password ? password : passport_id)
.then(async (userCredentials) => {
const userId = userCredentials.user.uid;
@@ -66,6 +72,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
registrationDate: new Date(),
subscriptionExpirationDate: expiryDate || null,
};
+
await setDoc(doc(db, "users", userId), user);
if (type === "corporate") {
const defaultTeachersGroup: Group = {
@@ -97,6 +104,34 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
await setDoc(doc(db, "groups", defaultCorporateGroup.id), defaultCorporateGroup);
}
+ if (!!corporate) {
+ const corporateQ = query(collection(db, "users"), where("email", "==", corporate));
+ const corporateSnapshot = await getDocs(corporateQ);
+
+ if (!corporateSnapshot.empty) {
+ const corporateUser = corporateSnapshot.docs[0].data() as CorporateUser;
+
+ const q = query(
+ collection(db, "groups"),
+ where("admin", "==", corporateUser.id),
+ where("name", "==", type === "student" ? "Students" : "Teachers"),
+ limit(1),
+ );
+ const snapshot = await getDocs(q);
+
+ if (!snapshot.empty) {
+ const doc = snapshot.docs[0];
+ const participants: string[] = doc.get("participants");
+
+ if (!participants.includes(userId)) {
+ updateDoc(doc.ref, {
+ participants: [...participants, userId],
+ });
+ }
+ }
+ }
+ }
+
if (typeof groupName === "string" && groupName.trim().length > 0) {
const q = query(collection(db, "groups"), where("admin", "==", maker.id), where("name", "==", groupName.trim()), limit(1));
const snapshot = await getDocs(q);
@@ -123,6 +158,11 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
}
}
+ if (!!groupID) {
+ const groupSnapshot = await getDoc(doc(db, "groups", groupID));
+ await setDoc(groupSnapshot.ref, {participants: [...groupSnapshot.data()!.participants, userId]}, {merge: true});
+ }
+
console.log(`Returning - ${email}`);
return res.status(200).json({ok: true});
})
diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx
index 8fc93230..9fdd3781 100644
--- a/src/pages/settings.tsx
+++ b/src/pages/settings.tsx
@@ -16,6 +16,11 @@ import ExamGenerator from "./(admin)/ExamGenerator";
import BatchCreateUser from "./(admin)/BatchCreateUser";
import {checkAccess, getTypesOfUser} from "@/utils/permissions";
import usePermissions from "@/hooks/usePermissions";
+import {useState} from "react";
+import Modal from "@/components/Modal";
+import IconCard from "@/dashboards/IconCard";
+import {BsCode, BsCodeSquare, BsPeopleFill, BsPersonFill} from "react-icons/bs";
+import UserCreator from "./(admin)/UserCreator";
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
const user = req.session.user;
@@ -46,6 +51,8 @@ export default function Admin() {
const {user} = useUser({redirectTo: "/login"});
const {permissions} = usePermissions(user?.id || "");
+ const [modalOpen, setModalOpen] = useState();
+
return (
<>
@@ -60,14 +67,52 @@ export default function Admin() {
{user && (
+ setModalOpen(undefined)}>
+
+
+ setModalOpen(undefined)}>
+
+
+ setModalOpen(undefined)}>
+
+
+ setModalOpen(undefined)}>
+
+
+
{checkAccess(user, getTypesOfUser(["teacher"]), permissions, "viewCodes") && (
- <>
-
-
-
- >
+
+ setModalOpen("createCode")}
+ />
+ setModalOpen("batchCreateCode")}
+ />
+ setModalOpen("createUser")}
+ />
+ setModalOpen("batchCreateUser")}
+ />
+
)}