Updated the register to only allow to create users if they have a code available
This commit is contained in:
@@ -28,7 +28,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
}
|
||||
|
||||
const codePromises = codes.map(async (code, index) => {
|
||||
const codeRef = doc(db, "codes", uuidv4());
|
||||
const codeRef = doc(db, "codes", code);
|
||||
await setDoc(codeRef, {type, code});
|
||||
|
||||
if (emails && emails.length > index) {
|
||||
|
||||
@@ -3,8 +3,8 @@ import {createUserWithEmailAndPassword, getAuth, sendPasswordResetEmail} from "f
|
||||
import {app} from "@/firebase";
|
||||
import {sessionOptions} from "@/lib/session";
|
||||
import {withIronSessionApiRoute} from "iron-session/next";
|
||||
import {getFirestore, getDoc, doc, setDoc} from "firebase/firestore";
|
||||
import {DemographicInformation} from "@/interfaces/user";
|
||||
import {getFirestore, getDoc, doc, setDoc, deleteDoc} from "firebase/firestore";
|
||||
import {DemographicInformation, Type} from "@/interfaces/user";
|
||||
|
||||
const auth = getAuth(app);
|
||||
const db = getFirestore(app);
|
||||
@@ -26,7 +26,15 @@ const DEFAULT_LEVELS = {
|
||||
};
|
||||
|
||||
async function login(req: NextApiRequest, res: NextApiResponse) {
|
||||
const {email, password} = req.body as {email: string; password: string; demographicInformation: DemographicInformation};
|
||||
const {email, password, code} = req.body as {email: string; password: string; code: string; demographicInformation: DemographicInformation};
|
||||
|
||||
const codeRef = await getDoc(doc(db, "codes", code));
|
||||
if (!codeRef.exists()) {
|
||||
res.status(400).json({error: "Invalid Code!"});
|
||||
return;
|
||||
}
|
||||
|
||||
const codeData = codeRef.data() as {code: string; type: Type};
|
||||
|
||||
createUserWithEmailAndPassword(auth, email, password)
|
||||
.then(async (userCredentials) => {
|
||||
@@ -38,12 +46,13 @@ async function login(req: NextApiRequest, res: NextApiResponse) {
|
||||
desiredLevels: DEFAULT_DESIRED_LEVELS,
|
||||
levels: DEFAULT_LEVELS,
|
||||
bio: "",
|
||||
isFirstLogin: true,
|
||||
isFirstLogin: codeData.type === "student",
|
||||
focus: "academic",
|
||||
type: "student",
|
||||
type: codeData.type,
|
||||
};
|
||||
|
||||
await setDoc(doc(db, "users", userId), user);
|
||||
await deleteDoc(codeRef.ref);
|
||||
|
||||
req.session.user = {...user, id: userId};
|
||||
await req.session.save();
|
||||
|
||||
@@ -11,12 +11,23 @@ import axios from "axios";
|
||||
import {Divider} from "primereact/divider";
|
||||
import {useRouter} from "next/router";
|
||||
import clsx from "clsx";
|
||||
import {NextPageContext} from "next";
|
||||
import {NextRequest, NextResponse} from "next/server";
|
||||
|
||||
export default function Register() {
|
||||
export const getServerSideProps = (context: any) => {
|
||||
const {code} = context.query;
|
||||
|
||||
return {
|
||||
props: {code},
|
||||
};
|
||||
};
|
||||
|
||||
export default function Register({code: queryCode}: {code: string}) {
|
||||
const [name, setName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
const [code, setCode] = useState(queryCode || "");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
@@ -40,6 +51,7 @@ export default function Register() {
|
||||
name,
|
||||
email,
|
||||
password,
|
||||
code,
|
||||
profilePicture: "/defaultAvatar.png",
|
||||
})
|
||||
.then((response) => {
|
||||
@@ -52,6 +64,12 @@ export default function Register() {
|
||||
toast.error("There is already a user with that e-mail!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (error.response.status === 400) {
|
||||
toast.error("The provided code is invalid!");
|
||||
return;
|
||||
}
|
||||
|
||||
toast.error("There was something wrong, please try again!");
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
@@ -95,10 +113,11 @@ export default function Register() {
|
||||
defaultValue={confirmPassword}
|
||||
required
|
||||
/>
|
||||
<Input type="text" name="code" onChange={(e) => setCode(e)} placeholder="Enter your registration code" defaultValue={code} required />
|
||||
<Button
|
||||
className="lg:mt-8 w-full"
|
||||
color="purple"
|
||||
disabled={isLoading || !email || !name || !password || !confirmPassword || password !== confirmPassword}>
|
||||
disabled={isLoading || !email || !name || !password || !confirmPassword || password !== confirmPassword || !code}>
|
||||
Create account
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -19,6 +19,8 @@ import {Chart} from "react-chartjs-2";
|
||||
import useUsers from "@/hooks/useUsers";
|
||||
import Select from "react-select";
|
||||
import useGroups from "@/hooks/useGroups";
|
||||
import DatePicker from "react-datepicker";
|
||||
import moment from "moment";
|
||||
|
||||
ChartJS.register(LinearScale, CategoryScale, PointElement, LineElement, LineController, Legend, Tooltip);
|
||||
|
||||
@@ -45,6 +47,8 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||
|
||||
export default function Stats() {
|
||||
const [statsUserId, setStatsUserId] = useState<string>();
|
||||
const [startDate, setStartDate] = useState<Date | null>(null);
|
||||
const [endDate, setEndDate] = useState<Date | null>(new Date());
|
||||
|
||||
const {user} = useUser({redirectTo: "/login"});
|
||||
const {users} = useUsers();
|
||||
@@ -56,6 +60,17 @@ export default function Stats() {
|
||||
if (user) setStatsUserId(user.id);
|
||||
}, [user]);
|
||||
|
||||
useEffect(() => {
|
||||
if (stats && stats.length > 0) {
|
||||
const sortedStats = stats.sort((a, b) => a.date - b.date);
|
||||
const firstStat = sortedStats.shift()!;
|
||||
|
||||
setStartDate(moment.unix(firstStat.date).toDate());
|
||||
console.log(stats.filter((x) => moment.unix(x.date).isAfter(startDate)));
|
||||
console.log(stats.filter((x) => moment.unix(x.date).isBefore(endDate)));
|
||||
}
|
||||
}, [stats]);
|
||||
|
||||
const calculateTotalScorePerSession = () => {
|
||||
const groupedBySession = groupBySession(stats);
|
||||
const sessionAverage = Object.keys(groupedBySession).map((x: string) => {
|
||||
@@ -160,22 +175,53 @@ export default function Stats() {
|
||||
</section>
|
||||
{stats.length > 0 && (
|
||||
<section className="flex flex-col gap-3">
|
||||
{(user.type === "developer" || user.type === "owner") && (
|
||||
<Select
|
||||
options={users.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))}
|
||||
defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}}
|
||||
onChange={(value) => setStatsUserId(value?.value)}
|
||||
<div className="w-full flex justify-between gap-8 items-center">
|
||||
<>
|
||||
{(user.type === "developer" || user.type === "owner") && (
|
||||
<Select
|
||||
className="w-full"
|
||||
options={users.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))}
|
||||
defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}}
|
||||
onChange={(value) => setStatsUserId(value?.value)}
|
||||
styles={{
|
||||
option: (styles, state) => ({
|
||||
...styles,
|
||||
backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white",
|
||||
color: state.isFocused ? "black" : styles.color,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{(user.type === "admin" || user.type === "teacher") && groups.length > 0 && (
|
||||
<Select
|
||||
className="w-full"
|
||||
options={users
|
||||
.filter((x) => groups.flatMap((y) => y.participants).includes(x.id))
|
||||
.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))}
|
||||
defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}}
|
||||
onChange={(value) => setStatsUserId(value?.value)}
|
||||
styles={{
|
||||
option: (styles, state) => ({
|
||||
...styles,
|
||||
backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white",
|
||||
color: state.isFocused ? "black" : styles.color,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
<DatePicker
|
||||
dateFormat="dd/MM/yyyy"
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
selectsRange
|
||||
filterDate={(date) => !moment(date).isSameOrBefore(moment(startDate))}
|
||||
onChange={([initialDate, finalDate]) => {
|
||||
setStartDate(initialDate);
|
||||
setEndDate(finalDate);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{(user.type === "admin" || user.type === "teacher") && groups.length > 0 && (
|
||||
<Select
|
||||
options={users
|
||||
.filter((x) => groups.flatMap((y) => y.participants).includes(x.id))
|
||||
.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))}
|
||||
defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}}
|
||||
onChange={(value) => setStatsUserId(value?.value)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-4 flex-wrap">
|
||||
{/* Exams per module */}
|
||||
<div className="flex flex-col gap-12 border w-full h-fit max-w-xs border-mti-gray-platinum p-4 pb-12 rounded-xl">
|
||||
@@ -319,7 +365,13 @@ export default function Stats() {
|
||||
<Chart
|
||||
type="line"
|
||||
data={{
|
||||
labels: Object.keys(groupBySession(stats)).map((_, index) => index),
|
||||
labels: Object.keys(
|
||||
groupBySession(
|
||||
stats.filter(
|
||||
(x) => moment.unix(x.date).isAfter(startDate) && moment.unix(x.date).isBefore(endDate),
|
||||
),
|
||||
),
|
||||
).map((_, index) => index),
|
||||
datasets: [
|
||||
{
|
||||
type: "line",
|
||||
@@ -342,7 +394,13 @@ export default function Stats() {
|
||||
<Chart
|
||||
type="line"
|
||||
data={{
|
||||
labels: Object.keys(groupBySession(stats)).map((_, index) => index),
|
||||
labels: Object.keys(
|
||||
groupBySession(
|
||||
stats.filter(
|
||||
(x) => moment.unix(x.date).isAfter(startDate) && moment.unix(x.date).isBefore(endDate),
|
||||
),
|
||||
),
|
||||
).map((_, index) => index),
|
||||
datasets: [
|
||||
...MODULE_ARRAY.map((module, index) => ({
|
||||
type: "line" as const,
|
||||
|
||||
Reference in New Issue
Block a user