New feature on the account creation:

It automatically stores who created the code and adds the registered user to a group administrated by that creator
This commit is contained in:
Tiago Ribeiro
2023-10-10 23:00:36 +01:00
parent 1aa4f0ddfd
commit 634a396434
6 changed files with 55 additions and 13 deletions

View File

@@ -6,11 +6,12 @@ interface Props {
label?: string; label?: string;
placeholder?: string; placeholder?: string;
defaultValue?: string; defaultValue?: string;
disabled?: boolean;
name: string; name: string;
onChange: (value: string) => void; onChange: (value: string) => void;
} }
export default function Input({type, label, placeholder, name, required = false, defaultValue, onChange}: Props) { export default function Input({type, label, placeholder, name, required = false, defaultValue, disabled = false, onChange}: Props) {
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
if (type === "password") { if (type === "password") {
@@ -53,9 +54,10 @@ export default function Input({type, label, placeholder, name, required = false,
<input <input
type={type} type={type}
name={name} name={name}
disabled={disabled}
onChange={(e) => onChange(e.target.value)} onChange={(e) => onChange(e.target.value)}
placeholder={placeholder} placeholder={placeholder}
className="px-8 py-6 text-sm font-normal placeholder:text-mti-gray-cool bg-white rounded-full border border-mti-gray-platinum focus:outline-none" className="px-8 py-6 text-sm font-normal placeholder:text-mti-gray-cool disabled:bg-mti-gray-platinum/40 disabled:text-mti-gray-dim disabled:cursor-not-allowed bg-white rounded-full border border-mti-gray-platinum focus:outline-none"
required={required} required={required}
defaultValue={defaultValue} defaultValue={defaultValue}
/> />

View File

@@ -57,6 +57,7 @@ export interface Group {
name: string; name: string;
participants: string[]; participants: string[];
id: string; id: string;
disableEditing?: boolean;
} }
export type Type = "student" | "teacher" | "admin" | "owner" | "developer"; export type Type = "student" | "teacher" | "admin" | "owner" | "developer";

View File

@@ -69,7 +69,7 @@ const CreatePanel = ({user, users, group, onCreate}: CreateDialogProps) => {
return ( return (
<div className="flex flex-col gap-12 mt-4 w-full px-4 py-2"> <div className="flex flex-col gap-12 mt-4 w-full px-4 py-2">
<div className="flex flex-col gap-8"> <div className="flex flex-col gap-8">
<Input name="name" type="text" label="Name" defaultValue={name} onChange={setName} required /> <Input name="name" type="text" label="Name" defaultValue={name} onChange={setName} required disabled={group?.disableEditing} />
<div className="flex flex-col gap-3 w-full"> <div className="flex flex-col gap-3 w-full">
<label className="font-normal text-base text-mti-gray-dim">Participants</label> <label className="font-normal text-base text-mti-gray-dim">Participants</label>
<div className="flex gap-8 w-full"> <div className="flex gap-8 w-full">

View File

@@ -5,6 +5,7 @@ import {sessionOptions} from "@/lib/session";
import {withIronSessionApiRoute} from "iron-session/next"; import {withIronSessionApiRoute} from "iron-session/next";
import {getFirestore, doc, setDoc, query, collection, where, getDocs} from "firebase/firestore"; import {getFirestore, doc, setDoc, query, collection, where, getDocs} from "firebase/firestore";
import {DemographicInformation, Type} from "@/interfaces/user"; import {DemographicInformation, Type} from "@/interfaces/user";
import {addUserToGroupOnCreation} from "@/utils/registration";
const auth = getAuth(app); const auth = getAuth(app);
const db = getFirestore(app); const db = getFirestore(app);
@@ -36,7 +37,7 @@ async function login(req: NextApiRequest, res: NextApiResponse) {
return; return;
} }
const codeData = codeDocs[0].data() as {code: string; type: Type}; const codeData = codeDocs[0].data() as {code: string; type: Type; creator: string};
createUserWithEmailAndPassword(auth, email, password) createUserWithEmailAndPassword(auth, email, password)
.then(async (userCredentials) => { .then(async (userCredentials) => {
@@ -55,6 +56,7 @@ async function login(req: NextApiRequest, res: NextApiResponse) {
await setDoc(doc(db, "users", userId), user); await setDoc(doc(db, "users", userId), user);
await setDoc(codeDocs[0].ref, {userId: userId}, {merge: true}); await setDoc(codeDocs[0].ref, {userId: userId}, {merge: true});
await addUserToGroupOnCreation(userId, codeData.type, codeData.creator);
req.session.user = {...user, id: userId}; req.session.user = {...user, id: userId};
await req.session.save(); await req.session.save();

View File

@@ -2,7 +2,7 @@ import {PERMISSIONS} from "@/constants/userPermissions";
import {app, adminApp} from "@/firebase"; import {app, adminApp} from "@/firebase";
import {User} from "@/interfaces/user"; import {User} from "@/interfaces/user";
import {sessionOptions} from "@/lib/session"; import {sessionOptions} from "@/lib/session";
import {collection, deleteDoc, doc, getDoc, getDocs, getFirestore, query, where} from "firebase/firestore"; import {collection, deleteDoc, doc, getDoc, getDocs, getFirestore, query, setDoc, where} from "firebase/firestore";
import {getAuth} from "firebase-admin/auth"; import {getAuth} from "firebase-admin/auth";
import {withIronSessionApiRoute} from "iron-session/next"; import {withIronSessionApiRoute} from "iron-session/next";
import {NextApiRequest, NextApiResponse} from "next"; import {NextApiRequest, NextApiResponse} from "next";
@@ -49,18 +49,24 @@ async function del(req: NextApiRequest, res: NextApiResponse) {
return; return;
} }
const userCodeDocs = await getDocs(query(collection(db, "codes"), where("userId", "==", id)));
const userParticipantGroup = await getDocs(query(collection(db, "groups"), where("participants", "array-contains", id)));
const userGroupAdminDocs = await getDocs(query(collection(db, "groups"), where("admin", "==", id)));
const userStatsDocs = await getDocs(query(collection(db, "stats"), where("user", "==", id)));
await Promise.all([
...userCodeDocs.docs.map(async (x) => await deleteDoc(x.ref)),
...userGroupAdminDocs.docs.map(async (x) => await deleteDoc(x.ref)),
...userStatsDocs.docs.map(async (x) => await deleteDoc(x.ref)),
...userParticipantGroup.docs.map(
async (x) => await setDoc(x.ref, {participants: x.data().participants.filter((y: string) => y !== id)}, {merge: true}),
),
]);
await auth.deleteUser(id); await auth.deleteUser(id);
await deleteDoc(doc(db, "users", id)); await deleteDoc(doc(db, "users", id));
res.json({ok: true}); res.json({ok: true});
const statsQuery = query(collection(db, "stats"), where("user", "==", targetUser.id));
const statsSnapshot = await getDocs(statsQuery);
await Promise.all(
statsSnapshot.docs.map(async (doc) => {
return await deleteDoc(doc.ref);
}),
);
} }
async function get(req: NextApiRequest, res: NextApiResponse) { async function get(req: NextApiRequest, res: NextApiResponse) {

31
src/utils/registration.ts Normal file
View File

@@ -0,0 +1,31 @@
import {collection, doc, getDoc, getDocs, getFirestore, query, setDoc, where} from "firebase/firestore";
import {app} from "@/firebase";
import {Group, Type, User} from "@/interfaces/user";
import {uuidv4} from "@firebase/util";
const db = getFirestore(app);
export const addUserToGroupOnCreation = async (userId: string, type: Type, creatorId: string) => {
const creatorDoc = await getDoc(doc(db, "users", creatorId));
if (!creatorDoc.exists()) return false;
const creator = {...creatorDoc.data(), id: creatorDoc.id} as User;
const creatorGroupsDocs = await getDocs(query(collection(db, "groups"), where("admin", "==", creator.id)));
const typeGroup = creatorGroupsDocs.docs.find((x) => (x.data() as Group).name === (type === "student" ? "Students" : "Teachers"));
if (typeGroup && typeGroup.exists()) {
await setDoc(typeGroup.ref, {participants: [...typeGroup.data().participants, userId]}, {merge: true});
return true;
}
const groupId = uuidv4();
await setDoc(doc(db, "groups", groupId), {
admin: creatorId,
name: type === "student" ? "Students" : "Teachers",
id: groupId,
participants: [userId],
disableEditing: true,
} as Group);
return true;
};