Added the ability for Corporate accounts to register without codes
This commit is contained in:
@@ -111,7 +111,7 @@ export default function OwnerDashboard({user}: Props) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const InactiveStudentsList = () => {
|
const InactiveStudentsList = () => {
|
||||||
const filter = (x: User) => x.type === "student" && (x.isDisabled || moment().isAfter(x.subscriptionExpirationDate));
|
const filter = (x: User) => x.type === "student" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -131,7 +131,7 @@ export default function OwnerDashboard({user}: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const InactiveCorporateList = () => {
|
const InactiveCorporateList = () => {
|
||||||
const filter = (x: User) => x.type === "corporate" && (x.isDisabled || moment().isAfter(x.subscriptionExpirationDate));
|
const filter = (x: User) => x.type === "corporate" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -196,7 +196,11 @@ export default function OwnerDashboard({user}: Props) {
|
|||||||
<span className="flex flex-col gap-1 items-center text-xl">
|
<span className="flex flex-col gap-1 items-center text-xl">
|
||||||
<span className="text-lg">Inactive Students</span>
|
<span className="text-lg">Inactive Students</span>
|
||||||
<span className="font-semibold text-mti-rose">
|
<span className="font-semibold text-mti-rose">
|
||||||
{users.filter((x) => x.type === "student" && (x.isDisabled || moment().isAfter(x.subscriptionExpirationDate))).length}
|
{
|
||||||
|
users.filter(
|
||||||
|
(x) => x.type === "student" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)),
|
||||||
|
).length
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -207,7 +211,11 @@ export default function OwnerDashboard({user}: Props) {
|
|||||||
<span className="flex flex-col gap-1 items-center text-xl">
|
<span className="flex flex-col gap-1 items-center text-xl">
|
||||||
<span className="text-lg text-center">Inactive Corporate</span>
|
<span className="text-lg text-center">Inactive Corporate</span>
|
||||||
<span className="font-semibold text-mti-rose">
|
<span className="font-semibold text-mti-rose">
|
||||||
{users.filter((x) => x.type === "corporate" && (x.isDisabled || moment().isAfter(x.subscriptionExpirationDate))).length}
|
{
|
||||||
|
users.filter(
|
||||||
|
(x) => x.type === "corporate" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)),
|
||||||
|
).length
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -219,7 +227,7 @@ export default function OwnerDashboard({user}: Props) {
|
|||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{users
|
||||||
.filter((x) => x.type === "student")
|
.filter((x) => x.type === "student")
|
||||||
.sort((a, b) => dateSorter(a, b, "asc", "registrationDate"))
|
.sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
|
||||||
.map((x) => (
|
.map((x) => (
|
||||||
<UserDisplay key={x.id} {...x} />
|
<UserDisplay key={x.id} {...x} />
|
||||||
))}
|
))}
|
||||||
@@ -230,17 +238,17 @@ export default function OwnerDashboard({user}: Props) {
|
|||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{users
|
||||||
.filter((x) => x.type === "corporate")
|
.filter((x) => x.type === "corporate")
|
||||||
.sort((a, b) => dateSorter(a, b, "asc", "registrationDate"))
|
.sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
|
||||||
.map((x) => (
|
.map((x) => (
|
||||||
<UserDisplay key={x.id} {...x} />
|
<UserDisplay key={x.id} {...x} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Disabled Corporate</span>
|
<span className="p-4">Unpaid Corporate</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{users
|
||||||
.filter((x) => x.type === "corporate" && x.isDisabled)
|
.filter((x) => x.type === "corporate" && x.status === "paymentDue")
|
||||||
.map((x) => (
|
.map((x) => (
|
||||||
<UserDisplay key={x.id} {...x} />
|
<UserDisplay key={x.id} {...x} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ export interface User {
|
|||||||
demographicInformation?: DemographicInformation;
|
demographicInformation?: DemographicInformation;
|
||||||
corporateInformation?: CorporateInformation;
|
corporateInformation?: CorporateInformation;
|
||||||
subscriptionExpirationDate?: null | Date;
|
subscriptionExpirationDate?: null | Date;
|
||||||
isDisabled?: boolean;
|
|
||||||
registrationDate?: Date;
|
registrationDate?: Date;
|
||||||
|
status: "active" | "disabled" | "paymentDue";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CorporateInformation {
|
export interface CorporateInformation {
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export default function UserList({user, filter}: {user: User; filter?: (user: Us
|
|||||||
const toggleDisableAccount = (user: User) => {
|
const toggleDisableAccount = (user: User) => {
|
||||||
if (
|
if (
|
||||||
!confirm(
|
!confirm(
|
||||||
`Are you sure you want to ${user.isDisabled ? "enable" : "disable"} ${
|
`Are you sure you want to ${user.status === "disabled" ? "enable" : "disable"} ${
|
||||||
user.name
|
user.name
|
||||||
}'s account? This change is usually related to their payment state.`,
|
}'s account? This change is usually related to their payment state.`,
|
||||||
)
|
)
|
||||||
@@ -93,9 +93,12 @@ export default function UserList({user, filter}: {user: User; filter?: (user: Us
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.post<{user?: User; ok?: boolean}>(`/api/users/update?id=${user.id}`, {...user, isDisabled: !user.isDisabled})
|
.post<{user?: User; ok?: boolean}>(`/api/users/update?id=${user.id}`, {
|
||||||
|
...user,
|
||||||
|
status: user.status === "disabled" ? "active" : "disabled",
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success(`User ${user.isDisabled ? "enabled" : "disabled"} successfully!`);
|
toast.success(`User ${user.status === "disabled" ? "enabled" : "disabled"} successfully!`);
|
||||||
reload();
|
reload();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -166,10 +169,10 @@ export default function UserList({user, filter}: {user: User; filter?: (user: Us
|
|||||||
)}
|
)}
|
||||||
{PERMISSIONS.updateUser[row.original.type].includes(user.type) && (
|
{PERMISSIONS.updateUser[row.original.type].includes(user.type) && (
|
||||||
<div
|
<div
|
||||||
data-tip={row.original.isDisabled ? "Enable User" : "Disable User"}
|
data-tip={row.original.status === "disabled" ? "Enable User" : "Disable User"}
|
||||||
className="cursor-pointer tooltip"
|
className="cursor-pointer tooltip"
|
||||||
onClick={() => toggleDisableAccount(row.original)}>
|
onClick={() => toggleDisableAccount(row.original)}>
|
||||||
{row.original.isDisabled ? (
|
{row.original.status === "disabled" ? (
|
||||||
<BsCheckCircle className="hover:text-mti-purple-light transition ease-in-out duration-300" />
|
<BsCheckCircle className="hover:text-mti-purple-light transition ease-in-out duration-300" />
|
||||||
) : (
|
) : (
|
||||||
<BsFillExclamationOctagonFill className="hover:text-mti-purple-light transition ease-in-out duration-300" />
|
<BsFillExclamationOctagonFill className="hover:text-mti-purple-light transition ease-in-out duration-300" />
|
||||||
|
|||||||
@@ -47,6 +47,13 @@ export default function RegisterCorporate({isLoading, setIsLoading, mutateUser,
|
|||||||
password,
|
password,
|
||||||
type: "corporate",
|
type: "corporate",
|
||||||
profilePicture: "/defaultAvatar.png",
|
profilePicture: "/defaultAvatar.png",
|
||||||
|
corporateInformation: {
|
||||||
|
companyInformation: {
|
||||||
|
name: companyName,
|
||||||
|
userAmount: companyUsers,
|
||||||
|
},
|
||||||
|
allowedUserAmount: companyUsers,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
mutateUser(response.data.user).then(() => sendEmailVerification(setIsLoading, onSuccess, onError));
|
mutateUser(response.data.user).then(() => sendEmailVerification(setIsLoading, onSuccess, onError));
|
||||||
@@ -114,7 +121,9 @@ export default function RegisterCorporate({isLoading, setIsLoading, mutateUser,
|
|||||||
<Button
|
<Button
|
||||||
className="lg:mt-8 w-full"
|
className="lg:mt-8 w-full"
|
||||||
color="purple"
|
color="purple"
|
||||||
disabled={isLoading || !email || !name || !password || !confirmPassword || password !== confirmPassword}>
|
disabled={
|
||||||
|
isLoading || !email || !name || !password || !confirmPassword || password !== confirmPassword || !companyName || companyUsers <= 0
|
||||||
|
}>
|
||||||
Create account
|
Create account
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export default function RegisterIndividual({queryCode, isLoading, setIsLoading,
|
|||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
|
type: "individual",
|
||||||
code,
|
code,
|
||||||
profilePicture: "/defaultAvatar.png",
|
profilePicture: "/defaultAvatar.png",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import {app} from "@/firebase";
|
|||||||
import {sessionOptions} from "@/lib/session";
|
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 {CorporateInformation, DemographicInformation, Type} from "@/interfaces/user";
|
||||||
import {addUserToGroupOnCreation} from "@/utils/registration";
|
import {addUserToGroupOnCreation} from "@/utils/registration";
|
||||||
|
|
||||||
const auth = getAuth(app);
|
const auth = getAuth(app);
|
||||||
const db = getFirestore(app);
|
const db = getFirestore(app);
|
||||||
|
|
||||||
export default withIronSessionApiRoute(login, sessionOptions);
|
export default withIronSessionApiRoute(register, sessionOptions);
|
||||||
|
|
||||||
const DEFAULT_DESIRED_LEVELS = {
|
const DEFAULT_DESIRED_LEVELS = {
|
||||||
reading: 9,
|
reading: 9,
|
||||||
@@ -26,8 +26,21 @@ const DEFAULT_LEVELS = {
|
|||||||
speaking: 0,
|
speaking: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
async function login(req: NextApiRequest, res: NextApiResponse) {
|
async function register(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const {email, password, code} = req.body as {email: string; password: string; code: string; demographicInformation: DemographicInformation};
|
const {type} = req.body as {
|
||||||
|
type: "individual" | "corporate";
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type === "individual") return registerIndividual(req, res);
|
||||||
|
if (type === "corporate") return registerCorporate(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function registerIndividual(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
const {email, password, code} = req.body as {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
code?: string;
|
||||||
|
};
|
||||||
|
|
||||||
const codeQuery = query(collection(db, "codes"), where("code", "==", code));
|
const codeQuery = query(collection(db, "codes"), where("code", "==", code));
|
||||||
const codeDocs = (await getDocs(codeQuery)).docs.filter((x) => !Object.keys(x.data()).includes("userId"));
|
const codeDocs = (await getDocs(codeQuery)).docs.filter((x) => !Object.keys(x.data()).includes("userId"));
|
||||||
@@ -70,3 +83,41 @@ async function login(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
res.status(401).json({error});
|
res.status(401).json({error});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function registerCorporate(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
const {email, password} = req.body as {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
corporateInformation: CorporateInformation;
|
||||||
|
};
|
||||||
|
|
||||||
|
createUserWithEmailAndPassword(auth, email, password)
|
||||||
|
.then(async (userCredentials) => {
|
||||||
|
const userId = userCredentials.user.uid;
|
||||||
|
delete req.body.password;
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
...req.body,
|
||||||
|
desiredLevels: DEFAULT_DESIRED_LEVELS,
|
||||||
|
levels: DEFAULT_LEVELS,
|
||||||
|
bio: "",
|
||||||
|
isFirstLogin: false,
|
||||||
|
focus: "academic",
|
||||||
|
type: "corporate",
|
||||||
|
subscriptionExpirationDate: null,
|
||||||
|
status: "paymentDue",
|
||||||
|
registrationDate: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await setDoc(doc(db, "users", userId), user);
|
||||||
|
|
||||||
|
req.session.user = {...user, id: userId};
|
||||||
|
await req.session.save();
|
||||||
|
|
||||||
|
res.status(200).json({user: {...user, id: userId}});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
res.status(401).json({error});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ async function sendVerification(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
const short = new ShortUniqueId();
|
const short = new ShortUniqueId();
|
||||||
|
|
||||||
if (req.session.user) {
|
if (req.session.user) {
|
||||||
|
console.log("ME HERE");
|
||||||
|
|
||||||
const transport = prepareMailer("verification");
|
const transport = prepareMailer("verification");
|
||||||
const mailOptions = prepareMailOptions(
|
const mailOptions = prepareMailOptions(
|
||||||
{
|
{
|
||||||
@@ -25,7 +27,8 @@ async function sendVerification(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
"verification",
|
"verification",
|
||||||
);
|
);
|
||||||
|
|
||||||
await transport.sendMail(mailOptions);
|
const result = await transport.sendMail(mailOptions);
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
res.status(200).json({ok: true});
|
res.status(200).json({ok: true});
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default function Home() {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (user && (user.isDisabled || checkIfUserExpired())) {
|
if (user && (user.status === "disabled" || checkIfUserExpired())) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@@ -79,7 +79,7 @@ export default function Home() {
|
|||||||
</Head>
|
</Head>
|
||||||
<Layout user={user} navDisabled>
|
<Layout user={user} navDisabled>
|
||||||
<div className="flex flex-col items-center justify-center text-center w-full gap-4">
|
<div className="flex flex-col items-center justify-center text-center w-full gap-4">
|
||||||
{user.isDisabled ? (
|
{user.status === "disabled" ? (
|
||||||
<>
|
<>
|
||||||
<span className="font-bold text-lg">Your account has been disabled!</span>
|
<span className="font-bold text-lg">Your account has been disabled!</span>
|
||||||
<span>Please contact an administrator if you believe this to be a mistake.</span>
|
<span>Please contact an administrator if you believe this to be a mistake.</span>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const preventNavigation = (navDisabled: boolean, focusMode: boolean): boo
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const shouldRedirectHome = (user: User) => {
|
export const shouldRedirectHome = (user: User) => {
|
||||||
if (user.isDisabled) return true;
|
if (user.status === "disabled") return true;
|
||||||
if (user.isFirstLogin) return true;
|
if (user.isFirstLogin) return true;
|
||||||
if (!user.demographicInformation) return true;
|
if (!user.demographicInformation) return true;
|
||||||
if (user.subscriptionExpirationDate && moment(new Date()).isAfter(user.subscriptionExpirationDate)) return true;
|
if (user.subscriptionExpirationDate && moment(new Date()).isAfter(user.subscriptionExpirationDate)) return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user