From a84edcd237176da6c427796d9006d5ea87139e2c Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Wed, 11 Oct 2023 11:20:28 +0100 Subject: [PATCH] - Added the option to select an expiry date when an owner or dev creates a code - Made it so the student's expiry date is the same as the admin when created by one --- src/pages/(admin)/BatchCodeGenerator.tsx | 70 ++++++++++++++++++++--- src/pages/(admin)/CodeGenerator.tsx | 71 +++++++++++++++++++++--- src/pages/admin.tsx | 4 +- src/pages/api/code.ts | 4 +- src/pages/api/register.ts | 3 +- 5 files changed, 132 insertions(+), 20 deletions(-) diff --git a/src/pages/(admin)/BatchCodeGenerator.tsx b/src/pages/(admin)/BatchCodeGenerator.tsx index a48b05e4..5c961f16 100644 --- a/src/pages/(admin)/BatchCodeGenerator.tsx +++ b/src/pages/(admin)/BatchCodeGenerator.tsx @@ -1,21 +1,38 @@ import Button from "@/components/Low/Button"; -import {Type} from "@/interfaces/user"; +import Checkbox from "@/components/Low/Checkbox"; +import {PERMISSIONS} from "@/constants/userPermissions"; +import {Type, User} from "@/interfaces/user"; import axios from "axios"; import clsx from "clsx"; import {capitalize} 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 {useFilePicker} from "use-file-picker"; -export default function BatchCodeGenerator() { +export default function BatchCodeGenerator({user}: {user: User}) { const [emails, setEmails] = useState([]); const [isLoading, setIsLoading] = useState(false); + const [expiryDate, setExpiryDate] = useState(null); + const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true); + const {openFilePicker, filesContent} = useFilePicker({ accept: ".txt", multiple: false, }); + useEffect(() => { + if (user.type === "admin" || user.type === "teacher") { + setExpiryDate(user.subscriptionExpirationDate || null); + } + }, [user]); + + useEffect(() => { + if (!isExpiryDateEnabled) setExpiryDate(null); + }, [isExpiryDateEnabled]); + useEffect(() => { if (filesContent.length > 0) { const file = filesContent[0]; @@ -38,7 +55,7 @@ export default function BatchCodeGenerator() { setIsLoading(true); axios - .post("/api/code", {type, codes, emails}) + .post("/api/code", {type, codes, emails, expiryDate}) .then(({data, status}) => { if (data.ok) { toast.success(`Successfully generated ${capitalize(type)} codes and they have been notified by e-mail!`, {toastId: "success"}); @@ -66,18 +83,57 @@ export default function BatchCodeGenerator() { + {(user.type === "developer" || user.type === "owner") && ( + <> +
+ + + Enabled + +
+ {isExpiryDateEnabled && ( + moment(date).isAfter(new Date())} + dateFormat="dd/MM/yyyy" + selected={expiryDate} + onChange={(date) => setExpiryDate(date)} + /> + )} + + )}
- - - -
diff --git a/src/pages/(admin)/CodeGenerator.tsx b/src/pages/(admin)/CodeGenerator.tsx index b1111c92..cd80729d 100644 --- a/src/pages/(admin)/CodeGenerator.tsx +++ b/src/pages/(admin)/CodeGenerator.tsx @@ -1,21 +1,37 @@ import Button from "@/components/Low/Button"; -import {Type} from "@/interfaces/user"; +import Checkbox from "@/components/Low/Checkbox"; +import {PERMISSIONS} from "@/constants/userPermissions"; +import {Type, User} from "@/interfaces/user"; import axios from "axios"; import clsx from "clsx"; import {capitalize} from "lodash"; -import {useState} from "react"; +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"; -export default function CodeGenerator() { +export default function CodeGenerator({user}: {user: User}) { const [generatedCode, setGeneratedCode] = useState(); + const [expiryDate, setExpiryDate] = useState(null); + const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true); + + useEffect(() => { + if (user.type === "admin" || user.type === "teacher") { + setExpiryDate(user.subscriptionExpirationDate || null); + } + }, [user]); + + useEffect(() => { + if (!isExpiryDateEnabled) setExpiryDate(null); + }, [isExpiryDateEnabled]); const generateCode = (type: Type) => { const uid = new ShortUniqueId(); const code = uid.randomUUID(6); axios - .post("/api/code", {type, codes: [code]}) + .post("/api/code", {type, codes: [code], expiryDate}) .then(({data, status}) => { if (data.ok) { toast.success(`Successfully generated a ${capitalize(type)} code!`, {toastId: "success"}); @@ -41,19 +57,58 @@ export default function CodeGenerator() {
- - - -
+ {(user.type === "developer" || user.type === "owner") && ( + <> +
+ + + Enabled + +
+ {isExpiryDateEnabled && ( + moment(date).isAfter(new Date())} + dateFormat="dd/MM/yyyy" + selected={expiryDate} + onChange={(date) => setExpiryDate(date)} + /> + )} + + )}
- - + +
diff --git a/src/pages/api/code.ts b/src/pages/api/code.ts index df569480..51a2f20b 100644 --- a/src/pages/api/code.ts +++ b/src/pages/api/code.ts @@ -19,7 +19,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { return; } - const {type, codes, emails} = req.body as {type: Type; codes: string[]; emails?: string[]}; + const {type, codes, emails, expiryDate} = req.body as {type: Type; codes: string[]; emails?: string[]; expiryDate: null | Date}; const permission = PERMISSIONS.generateCode[type]; if (!permission.includes(req.session.user.type)) { @@ -29,7 +29,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { const codePromises = codes.map(async (code, index) => { const codeRef = doc(db, "codes", code); - await setDoc(codeRef, {type, code, creator: req.session.user!.id}); + await setDoc(codeRef, {type, code, creator: req.session.user!.id, expiryDate}); if (emails && emails.length > index) { const transport = prepareMailer(); diff --git a/src/pages/api/register.ts b/src/pages/api/register.ts index b947c0e0..5310bbef 100644 --- a/src/pages/api/register.ts +++ b/src/pages/api/register.ts @@ -37,7 +37,7 @@ async function login(req: NextApiRequest, res: NextApiResponse) { return; } - const codeData = codeDocs[0].data() as {code: string; type: Type; creator: string}; + const codeData = codeDocs[0].data() as {code: string; type: Type; creator: string; expiryDate: Date | null}; createUserWithEmailAndPassword(auth, email, password) .then(async (userCredentials) => { @@ -52,6 +52,7 @@ async function login(req: NextApiRequest, res: NextApiResponse) { isFirstLogin: codeData.type === "student", focus: "academic", type: codeData.type, + subscriptionExpirationDate: codeData.expiryDate, }; await setDoc(doc(db, "users", userId), user);