ENCOA-263
This commit is contained in:
@@ -19,7 +19,6 @@ import Select from "react-select";
|
|||||||
import useUsers from "@/hooks/useUsers";
|
import useUsers from "@/hooks/useUsers";
|
||||||
import { USER_TYPE_LABELS } from "@/resources/user";
|
import { USER_TYPE_LABELS } from "@/resources/user";
|
||||||
import { CURRENCIES } from "@/resources/paypal";
|
import { CURRENCIES } from "@/resources/paypal";
|
||||||
import useCodes from "@/hooks/useCodes";
|
|
||||||
import { checkAccess, getTypesOfUser } from "@/utils/permissions";
|
import { checkAccess, getTypesOfUser } from "@/utils/permissions";
|
||||||
import { PERMISSIONS } from "@/constants/userPermissions";
|
import { PERMISSIONS } from "@/constants/userPermissions";
|
||||||
import { PermissionType } from "@/interfaces/permissions";
|
import { PermissionType } from "@/interfaces/permissions";
|
||||||
@@ -119,7 +118,6 @@ const UserCard = ({
|
|||||||
);
|
);
|
||||||
const { data: stats } = useFilterRecordsByUser<Stat[]>(user.id);
|
const { data: stats } = useFilterRecordsByUser<Stat[]>(user.id);
|
||||||
const { users } = useUsers();
|
const { users } = useUsers();
|
||||||
const { codes } = useCodes(user.id);
|
|
||||||
const { permissions } = usePermissions(loggedInUser.id);
|
const { permissions } = usePermissions(loggedInUser.id);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {Code, Group, User} from "@/interfaces/user";
|
import { Code, Group, User } from "@/interfaces/user";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {useEffect, useState} from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function useCodes(creator?: string) {
|
export default function useCodes(entity?: string) {
|
||||||
const [codes, setCodes] = useState<Code[]>([]);
|
const [codes, setCodes] = useState<Code[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isError, setIsError] = useState(false);
|
const [isError, setIsError] = useState(false);
|
||||||
@@ -10,12 +10,12 @@ export default function useCodes(creator?: string) {
|
|||||||
const getData = () => {
|
const getData = () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
axios
|
axios
|
||||||
.get<Code[]>(`/api/code${creator ? `?creator=${creator}` : ""}`)
|
.get<Code[]>(`/api/code${entity ? `?entity=${entity}` : ""}`)
|
||||||
.then((response) => setCodes(response.data))
|
.then((response) => setCodes(response.data))
|
||||||
.finally(() => setIsLoading(false));
|
.finally(() => setIsLoading(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(getData, [creator]);
|
useEffect(getData, [entity]);
|
||||||
|
|
||||||
return {codes, isLoading, isError, reload: getData};
|
return { codes, isLoading, isError, reload: getData };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,31 @@
|
|||||||
import Button from "@/components/Low/Button";
|
import Button from "@/components/Low/Button";
|
||||||
import Checkbox from "@/components/Low/Checkbox";
|
import Checkbox from "@/components/Low/Checkbox";
|
||||||
import {PERMISSIONS} from "@/constants/userPermissions";
|
import { PERMISSIONS } from "@/constants/userPermissions";
|
||||||
import useUsers from "@/hooks/useUsers";
|
import useUsers from "@/hooks/useUsers";
|
||||||
import {Type, User} from "@/interfaces/user";
|
import { Type, User } from "@/interfaces/user";
|
||||||
import {USER_TYPE_LABELS} from "@/resources/user";
|
import { USER_TYPE_LABELS } from "@/resources/user";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {capitalize, uniqBy} from "lodash";
|
import { capitalize, uniqBy } from "lodash";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {useEffect, useState} from "react";
|
import { useEffect, useState } from "react";
|
||||||
import ReactDatePicker from "react-datepicker";
|
import ReactDatePicker from "react-datepicker";
|
||||||
import {toast} from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import {useFilePicker} from "use-file-picker";
|
import { useFilePicker } from "use-file-picker";
|
||||||
import readXlsxFile from "read-excel-file";
|
import readXlsxFile from "read-excel-file";
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
import {BsFileEarmarkEaselFill, BsQuestionCircleFill} from "react-icons/bs";
|
import { BsFileEarmarkEaselFill, BsQuestionCircleFill } from "react-icons/bs";
|
||||||
import {checkAccess, getTypesOfUser} from "@/utils/permissions";
|
import { checkAccess, getTypesOfUser } from "@/utils/permissions";
|
||||||
import {PermissionType} from "@/interfaces/permissions";
|
import { PermissionType } from "@/interfaces/permissions";
|
||||||
import usePermissions from "@/hooks/usePermissions";
|
import usePermissions from "@/hooks/usePermissions";
|
||||||
|
import { EntityWithRoles } from "@/interfaces/entity";
|
||||||
|
import Select from "@/components/Low/Select";
|
||||||
|
|
||||||
const EMAIL_REGEX = new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/);
|
const EMAIL_REGEX = new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/);
|
||||||
|
|
||||||
const USER_TYPE_PERMISSIONS: {
|
const USER_TYPE_PERMISSIONS: {
|
||||||
[key in Type]: {perm: PermissionType | undefined; list: Type[]};
|
[key in Type]: { perm: PermissionType | undefined; list: Type[] };
|
||||||
} = {
|
} = {
|
||||||
student: {
|
student: {
|
||||||
perm: "createCodeStudent",
|
perm: "createCodeStudent",
|
||||||
@@ -59,11 +61,12 @@ interface Props {
|
|||||||
user: User;
|
user: User;
|
||||||
users: User[];
|
users: User[];
|
||||||
permissions: PermissionType[];
|
permissions: PermissionType[];
|
||||||
|
entities: EntityWithRoles[]
|
||||||
onFinish: () => void;
|
onFinish: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BatchCodeGenerator({user, users, permissions, onFinish}: Props) {
|
export default function BatchCodeGenerator({ user, users, entities = [], permissions, onFinish }: Props) {
|
||||||
const [infos, setInfos] = useState<{email: string; name: string; passport_id: string}[]>([]);
|
const [infos, setInfos] = useState<{ email: string; name: string; passport_id: string }[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [expiryDate, setExpiryDate] = useState<Date | null>(
|
const [expiryDate, setExpiryDate] = useState<Date | null>(
|
||||||
user?.subscriptionExpirationDate ? moment(user.subscriptionExpirationDate).toDate() : null,
|
user?.subscriptionExpirationDate ? moment(user.subscriptionExpirationDate).toDate() : null,
|
||||||
@@ -71,8 +74,9 @@ export default function BatchCodeGenerator({user, users, permissions, onFinish}:
|
|||||||
const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true);
|
const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true);
|
||||||
const [type, setType] = useState<Type>("student");
|
const [type, setType] = useState<Type>("student");
|
||||||
const [showHelp, setShowHelp] = useState(false);
|
const [showHelp, setShowHelp] = useState(false);
|
||||||
|
const [entity, setEntity] = useState((entities || [])[0]?.id || undefined)
|
||||||
|
|
||||||
const {openFilePicker, filesContent, clear} = useFilePicker({
|
const { openFilePicker, filesContent, clear } = useFilePicker({
|
||||||
accept: ".xlsx",
|
accept: ".xlsx",
|
||||||
multiple: false,
|
multiple: false,
|
||||||
readAs: "ArrayBuffer",
|
readAs: "ArrayBuffer",
|
||||||
@@ -93,10 +97,10 @@ export default function BatchCodeGenerator({user, users, permissions, onFinish}:
|
|||||||
const [firstName, lastName, country, passport_id, email, phone] = row as string[];
|
const [firstName, lastName, country, passport_id, email, phone] = row as string[];
|
||||||
return EMAIL_REGEX.test(email.toString().trim())
|
return EMAIL_REGEX.test(email.toString().trim())
|
||||||
? {
|
? {
|
||||||
email: email.toString().trim().toLowerCase(),
|
email: email.toString().trim().toLowerCase(),
|
||||||
name: `${firstName ?? ""} ${lastName ?? ""}`.trim(),
|
name: `${firstName ?? ""} ${lastName ?? ""}`.trim(),
|
||||||
passport_id: passport_id?.toString().trim() || undefined,
|
passport_id: passport_id?.toString().trim() || undefined,
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
})
|
})
|
||||||
.filter((x) => !!x) as typeof infos,
|
.filter((x) => !!x) as typeof infos,
|
||||||
@@ -139,7 +143,7 @@ export default function BatchCodeGenerator({user, users, permissions, onFinish}:
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
Promise.all(existingUsers.map(async (u) => await axios.post(`/api/invites`, {to: u.id, from: user.id})))
|
Promise.all(existingUsers.map(async (u) => await axios.post(`/api/invites`, { to: u.id, from: user.id })))
|
||||||
.then(() => toast.success(`Successfully invited ${existingUsers.length} registered student(s)!`))
|
.then(() => toast.success(`Successfully invited ${existingUsers.length} registered student(s)!`))
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (newUsers.length === 0) setIsLoading(false);
|
if (newUsers.length === 0) setIsLoading(false);
|
||||||
@@ -155,19 +159,20 @@ export default function BatchCodeGenerator({user, users, permissions, onFinish}:
|
|||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
axios
|
axios
|
||||||
.post<{ok: boolean; valid?: number; reason?: string}>("/api/code", {
|
.post<{ ok: boolean; valid?: number; reason?: string }>("/api/code", {
|
||||||
type,
|
type,
|
||||||
codes,
|
codes,
|
||||||
infos: informations,
|
infos: informations.map((info, index) => ({ ...info, code: codes[index] })),
|
||||||
expiryDate,
|
expiryDate,
|
||||||
|
entity
|
||||||
})
|
})
|
||||||
.then(({data, status}) => {
|
.then(({ data, status }) => {
|
||||||
if (data.ok) {
|
if (data.ok) {
|
||||||
toast.success(
|
toast.success(
|
||||||
`Successfully generated${data.valid ? ` ${data.valid}/${informations.length}` : ""} ${capitalize(
|
`Successfully generated${data.valid ? ` ${data.valid}/${informations.length}` : ""} ${capitalize(
|
||||||
type,
|
type,
|
||||||
)} codes and they have been notified by e-mail!`,
|
)} codes and they have been notified by e-mail!`,
|
||||||
{toastId: "success"},
|
{ toastId: "success" },
|
||||||
);
|
);
|
||||||
|
|
||||||
onFinish();
|
onFinish();
|
||||||
@@ -175,12 +180,12 @@ export default function BatchCodeGenerator({user, users, permissions, onFinish}:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status === 403) {
|
if (status === 403) {
|
||||||
toast.error(data.reason, {toastId: "forbidden"});
|
toast.error(data.reason, { toastId: "forbidden" });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(({response: {status, data}}) => {
|
.catch(({ response: { status, data } }) => {
|
||||||
if (status === 403) {
|
if (status === 403) {
|
||||||
toast.error(data.reason, {toastId: "forbidden"});
|
toast.error(data.reason, { toastId: "forbidden" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +263,15 @@ export default function BatchCodeGenerator({user, users, permissions, onFinish}:
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
<div className={clsx("flex flex-col gap-4")}>
|
||||||
|
<label className="font-normal text-base text-mti-gray-dim">Entity</label>
|
||||||
|
<Select
|
||||||
|
defaultValue={{ value: (entities || [])[0]?.id, label: (entities || [])[0]?.label }}
|
||||||
|
options={entities.map((e) => ({ value: e.id, label: e.label }))}
|
||||||
|
onChange={(e) => setEntity(e?.value || undefined)}
|
||||||
|
isClearable={checkAccess(user, ["admin", "developer"])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<label className="text-mti-gray-dim text-base font-normal">Select the type of user they should be</label>
|
<label className="text-mti-gray-dim text-base font-normal">Select the type of user they should be</label>
|
||||||
{user && (
|
{user && (
|
||||||
<select
|
<select
|
||||||
@@ -266,7 +280,7 @@ export default function BatchCodeGenerator({user, users, permissions, onFinish}:
|
|||||||
className="flex min-h-[70px] w-full min-w-[350px] cursor-pointer justify-center rounded-full border bg-white p-6 text-sm font-normal focus:outline-none">
|
className="flex min-h-[70px] w-full min-w-[350px] cursor-pointer justify-center rounded-full border bg-white p-6 text-sm font-normal focus:outline-none">
|
||||||
{Object.keys(USER_TYPE_LABELS)
|
{Object.keys(USER_TYPE_LABELS)
|
||||||
.filter((x) => {
|
.filter((x) => {
|
||||||
const {list, perm} = USER_TYPE_PERMISSIONS[x as Type];
|
const { list, perm } = USER_TYPE_PERMISSIONS[x as Type];
|
||||||
return checkAccess(user, getTypesOfUser(list), permissions, perm);
|
return checkAccess(user, getTypesOfUser(list), permissions, perm);
|
||||||
})
|
})
|
||||||
.map((type) => (
|
.map((type) => (
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
import Button from "@/components/Low/Button";
|
import Button from "@/components/Low/Button";
|
||||||
import Checkbox from "@/components/Low/Checkbox";
|
import Checkbox from "@/components/Low/Checkbox";
|
||||||
import {PERMISSIONS} from "@/constants/userPermissions";
|
import { PERMISSIONS } from "@/constants/userPermissions";
|
||||||
import {Type, User} from "@/interfaces/user";
|
import { Type, User } from "@/interfaces/user";
|
||||||
import {USER_TYPE_LABELS} from "@/resources/user";
|
import { USER_TYPE_LABELS } from "@/resources/user";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {capitalize} from "lodash";
|
import { capitalize } from "lodash";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {useEffect, useState} from "react";
|
import { useEffect, useState } from "react";
|
||||||
import ReactDatePicker from "react-datepicker";
|
import ReactDatePicker from "react-datepicker";
|
||||||
import {toast} from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import {checkAccess, getTypesOfUser} from "@/utils/permissions";
|
import { checkAccess, getTypesOfUser } from "@/utils/permissions";
|
||||||
import {PermissionType} from "@/interfaces/permissions";
|
import { PermissionType } from "@/interfaces/permissions";
|
||||||
import usePermissions from "@/hooks/usePermissions";
|
import usePermissions from "@/hooks/usePermissions";
|
||||||
|
import { EntityWithRoles } from "@/interfaces/entity";
|
||||||
|
import Select from "@/components/Low/Select";
|
||||||
|
import { useAllowedEntities } from "@/hooks/useEntityPermissions";
|
||||||
|
|
||||||
const USER_TYPE_PERMISSIONS: {
|
const USER_TYPE_PERMISSIONS: {
|
||||||
[key in Type]: {perm: PermissionType | undefined; list: Type[]};
|
[key in Type]: { perm: PermissionType | undefined; list: Type[] };
|
||||||
} = {
|
} = {
|
||||||
student: {
|
student: {
|
||||||
perm: "createCodeStudent",
|
perm: "createCodeStudent",
|
||||||
@@ -51,16 +54,19 @@ const USER_TYPE_PERMISSIONS: {
|
|||||||
interface Props {
|
interface Props {
|
||||||
user: User;
|
user: User;
|
||||||
permissions: PermissionType[];
|
permissions: PermissionType[];
|
||||||
|
entities: EntityWithRoles[]
|
||||||
onFinish: () => void;
|
onFinish: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CodeGenerator({user, permissions, onFinish}: Props) {
|
export default function CodeGenerator({ user, entities = [], permissions, onFinish }: Props) {
|
||||||
const [generatedCode, setGeneratedCode] = useState<string>();
|
const [generatedCode, setGeneratedCode] = useState<string>();
|
||||||
|
|
||||||
const [expiryDate, setExpiryDate] = useState<Date | null>(
|
const [expiryDate, setExpiryDate] = useState<Date | null>(
|
||||||
user?.subscriptionExpirationDate ? moment(user.subscriptionExpirationDate).toDate() : null,
|
user?.subscriptionExpirationDate ? moment(user.subscriptionExpirationDate).toDate() : null,
|
||||||
);
|
);
|
||||||
const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true);
|
const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true);
|
||||||
const [type, setType] = useState<Type>("student");
|
const [type, setType] = useState<Type>("student");
|
||||||
|
const [entity, setEntity] = useState((entities || [])[0]?.id || undefined)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isExpiryDateEnabled) setExpiryDate(null);
|
if (!isExpiryDateEnabled) setExpiryDate(null);
|
||||||
@@ -71,8 +77,8 @@ export default function CodeGenerator({user, permissions, onFinish}: Props) {
|
|||||||
const code = uid.randomUUID(6);
|
const code = uid.randomUUID(6);
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.post("/api/code", {type, codes: [code], expiryDate})
|
.post("/api/code", { type, codes: [code], expiryDate, entity })
|
||||||
.then(({data, status}) => {
|
.then(({ data, status }) => {
|
||||||
if (data.ok) {
|
if (data.ok) {
|
||||||
toast.success(`Successfully generated a ${capitalize(type)} code!`, {
|
toast.success(`Successfully generated a ${capitalize(type)} code!`, {
|
||||||
toastId: "success",
|
toastId: "success",
|
||||||
@@ -82,12 +88,12 @@ export default function CodeGenerator({user, permissions, onFinish}: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status === 403) {
|
if (status === 403) {
|
||||||
toast.error(data.reason, {toastId: "forbidden"});
|
toast.error(data.reason, { toastId: "forbidden" });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(({response: {status, data}}) => {
|
.catch(({ response: { status, data } }) => {
|
||||||
if (status === 403) {
|
if (status === 403) {
|
||||||
toast.error(data.reason, {toastId: "forbidden"});
|
toast.error(data.reason, { toastId: "forbidden" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,14 +106,25 @@ export default function CodeGenerator({user, permissions, onFinish}: Props) {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4 border p-4 border-mti-gray-platinum rounded-xl">
|
<div className="flex flex-col gap-4 border p-4 border-mti-gray-platinum rounded-xl">
|
||||||
<label className="font-normal text-base text-mti-gray-dim">User Code Generator</label>
|
<label className="font-normal text-base text-mti-gray-dim">User Code Generator</label>
|
||||||
{user && (
|
<div className={clsx("flex flex-col gap-4")}>
|
||||||
|
<label className="font-normal text-base text-mti-gray-dim">Entity</label>
|
||||||
|
<Select
|
||||||
|
defaultValue={{ value: (entities || [])[0]?.id, label: (entities || [])[0]?.label }}
|
||||||
|
options={entities.map((e) => ({ value: e.id, label: e.label }))}
|
||||||
|
onChange={(e) => setEntity(e?.value || undefined)}
|
||||||
|
isClearable={checkAccess(user, ["admin", "developer"])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={clsx("flex flex-col gap-4")}>
|
||||||
|
<label className="font-normal text-base text-mti-gray-dim">Type</label>
|
||||||
<select
|
<select
|
||||||
defaultValue="student"
|
defaultValue="student"
|
||||||
onChange={(e) => setType(e.target.value as typeof user.type)}
|
onChange={(e) => setType(e.target.value as typeof user.type)}
|
||||||
className="p-6 w-full min-w-[350px] min-h-[70px] flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer bg-white">
|
className="p-6 w-full min-w-[350px] min-h-[70px] flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer bg-white">
|
||||||
{Object.keys(USER_TYPE_LABELS)
|
{Object.keys(USER_TYPE_LABELS)
|
||||||
.filter((x) => {
|
.filter((x) => {
|
||||||
const {list, perm} = USER_TYPE_PERMISSIONS[x as Type];
|
const { list, perm } = USER_TYPE_PERMISSIONS[x as Type];
|
||||||
return checkAccess(user, getTypesOfUser(list), permissions, perm);
|
return checkAccess(user, getTypesOfUser(list), permissions, perm);
|
||||||
})
|
})
|
||||||
.map((type) => (
|
.map((type) => (
|
||||||
@@ -116,8 +133,9 @@ export default function CodeGenerator({user, permissions, onFinish}: Props) {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
)}
|
</div>
|
||||||
{user && checkAccess(user, ["developer", "admin", "corporate", "mastercorporate"]) && (
|
|
||||||
|
{checkAccess(user, ["developer", "admin", "corporate", "mastercorporate"]) && (
|
||||||
<>
|
<>
|
||||||
<div className="-md:flex-row -md:items-center flex justify-between gap-2 md:flex-col 2xl:flex-row 2xl:items-center">
|
<div className="-md:flex-row -md:items-center flex justify-between gap-2 md:flex-col 2xl:flex-row 2xl:items-center">
|
||||||
<label className="text-mti-gray-dim text-base font-normal">Expiry Date</label>
|
<label className="text-mti-gray-dim text-base font-normal">Expiry Date</label>
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import ReactDatePicker from "react-datepicker";
|
|||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { checkAccess } from "@/utils/permissions";
|
import { checkAccess } from "@/utils/permissions";
|
||||||
import usePermissions from "@/hooks/usePermissions";
|
import usePermissions from "@/hooks/usePermissions";
|
||||||
|
import { EntityWithRoles } from "@/interfaces/entity";
|
||||||
|
import { isAdmin } from "@/utils/users";
|
||||||
|
import { findBy } from "@/utils";
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<Code>();
|
const columnHelper = createColumnHelper<Code>();
|
||||||
|
|
||||||
@@ -34,16 +37,15 @@ const CreatorCell = ({ id, users }: { id: string; users: User[] }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function CodeList({ user, canDeleteCodes }: { user: User, canDeleteCodes?: boolean }) {
|
export default function CodeList({ user, entities, canDeleteCodes }
|
||||||
|
: { user: User, entities: EntityWithRoles[], canDeleteCodes?: boolean }) {
|
||||||
const [selectedCodes, setSelectedCodes] = useState<string[]>([]);
|
const [selectedCodes, setSelectedCodes] = useState<string[]>([]);
|
||||||
|
|
||||||
const [filteredCorporate, setFilteredCorporate] = useState<User | undefined>(user?.type === "corporate" ? user : undefined);
|
const [filteredCorporate, setFilteredCorporate] = useState<User | undefined>(user?.type === "corporate" ? user : undefined);
|
||||||
const [filterAvailability, setFilterAvailability] = useState<"in-use" | "unused">();
|
const [filterAvailability, setFilterAvailability] = useState<"in-use" | "unused">();
|
||||||
|
|
||||||
const { permissions } = usePermissions(user?.id || "");
|
|
||||||
|
|
||||||
const { users } = useUsers();
|
const { users } = useUsers();
|
||||||
const { codes, reload } = useCodes(user?.type === "corporate" ? user?.id : undefined);
|
const { codes, reload } = useCodes();
|
||||||
|
|
||||||
const [startDate, setStartDate] = useState<Date | null>(moment("01/01/2023").toDate());
|
const [startDate, setStartDate] = useState<Date | null>(moment("01/01/2023").toDate());
|
||||||
const [endDate, setEndDate] = useState<Date | null>(moment().endOf("day").toDate());
|
const [endDate, setEndDate] = useState<Date | null>(moment().endOf("day").toDate());
|
||||||
@@ -158,13 +160,17 @@ export default function CodeList({ user, canDeleteCodes }: { user: User, canDele
|
|||||||
cell: (info) => (info.getValue() ? moment(info.getValue()).format("DD/MM/YYYY") : "N/A"),
|
cell: (info) => (info.getValue() ? moment(info.getValue()).format("DD/MM/YYYY") : "N/A"),
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor("email", {
|
columnHelper.accessor("email", {
|
||||||
header: "Invited E-mail",
|
header: "E-mail",
|
||||||
cell: (info) => info.getValue() || "N/A",
|
cell: (info) => info.getValue() || "N/A",
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor("creator", {
|
columnHelper.accessor("creator", {
|
||||||
header: "Creator",
|
header: "Creator",
|
||||||
cell: (info) => <CreatorCell id={info.getValue()} users={users} />,
|
cell: (info) => <CreatorCell id={info.getValue()} users={users} />,
|
||||||
}),
|
}),
|
||||||
|
columnHelper.accessor("entity", {
|
||||||
|
header: "Entity",
|
||||||
|
cell: (info) => findBy(entities, 'id', info.getValue())?.label || "N/A",
|
||||||
|
}),
|
||||||
columnHelper.accessor("userId", {
|
columnHelper.accessor("userId", {
|
||||||
header: "Availability",
|
header: "Availability",
|
||||||
cell: (info) =>
|
cell: (info) =>
|
||||||
|
|||||||
@@ -3,31 +3,30 @@ import Checkbox from "@/components/Low/Checkbox";
|
|||||||
import Input from "@/components/Low/Input";
|
import Input from "@/components/Low/Input";
|
||||||
import Select from "@/components/Low/Select";
|
import Select from "@/components/Low/Select";
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
import useCodes from "@/hooks/useCodes";
|
|
||||||
import useDiscounts from "@/hooks/useDiscounts";
|
import useDiscounts from "@/hooks/useDiscounts";
|
||||||
import useUser from "@/hooks/useUser";
|
import useUser from "@/hooks/useUser";
|
||||||
import useUsers from "@/hooks/useUsers";
|
import useUsers from "@/hooks/useUsers";
|
||||||
import {Discount} from "@/interfaces/paypal";
|
import { Discount } from "@/interfaces/paypal";
|
||||||
import {Code, User} from "@/interfaces/user";
|
import { Code, User } from "@/interfaces/user";
|
||||||
import {USER_TYPE_LABELS} from "@/resources/user";
|
import { USER_TYPE_LABELS } from "@/resources/user";
|
||||||
import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table";
|
import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {useEffect, useState} from "react";
|
import { useEffect, useState } from "react";
|
||||||
import ReactDatePicker from "react-datepicker";
|
import ReactDatePicker from "react-datepicker";
|
||||||
import {BsPencil, BsTrash} from "react-icons/bs";
|
import { BsPencil, BsTrash } from "react-icons/bs";
|
||||||
import {toast} from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<Discount>();
|
const columnHelper = createColumnHelper<Discount>();
|
||||||
|
|
||||||
const DiscountCreator = ({discount, onClose}: {discount?: Discount; onClose: () => void}) => {
|
const DiscountCreator = ({ discount, onClose }: { discount?: Discount; onClose: () => void }) => {
|
||||||
const [percentage, setPercentage] = useState(discount?.percentage);
|
const [percentage, setPercentage] = useState(discount?.percentage);
|
||||||
const [domain, setDomain] = useState(discount?.domain);
|
const [domain, setDomain] = useState(discount?.domain);
|
||||||
const [validUntil, setValidUntil] = useState(discount?.validUntil);
|
const [validUntil, setValidUntil] = useState(discount?.validUntil);
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
const body = {percentage, domain, validUntil: validUntil?.toISOString() || undefined};
|
const body = { percentage, domain, validUntil: validUntil?.toISOString() || undefined };
|
||||||
|
|
||||||
if (discount) {
|
if (discount) {
|
||||||
return axios
|
return axios
|
||||||
@@ -112,7 +111,7 @@ const DiscountCreator = ({discount, onClose}: {discount?: Discount; onClose: ()
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function DiscountList({user}: {user: User}) {
|
export default function DiscountList({ user }: { user: User }) {
|
||||||
const [selectedDiscounts, setSelectedDiscounts] = useState<string[]>([]);
|
const [selectedDiscounts, setSelectedDiscounts] = useState<string[]>([]);
|
||||||
|
|
||||||
const [isCreating, setIsCreating] = useState(false);
|
const [isCreating, setIsCreating] = useState(false);
|
||||||
@@ -120,8 +119,8 @@ export default function DiscountList({user}: {user: User}) {
|
|||||||
|
|
||||||
const [filteredDiscounts, setFilteredDiscounts] = useState<Discount[]>([]);
|
const [filteredDiscounts, setFilteredDiscounts] = useState<Discount[]>([]);
|
||||||
|
|
||||||
const {users} = useUsers();
|
const { users } = useUsers();
|
||||||
const {discounts, reload} = useDiscounts();
|
const { discounts, reload } = useDiscounts();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFilteredDiscounts(discounts);
|
setFilteredDiscounts(discounts);
|
||||||
@@ -220,7 +219,7 @@ export default function DiscountList({user}: {user: User}) {
|
|||||||
{
|
{
|
||||||
header: "",
|
header: "",
|
||||||
id: "actions",
|
id: "actions",
|
||||||
cell: ({row}: {row: {original: Discount}}) => {
|
cell: ({ row }: { row: { original: Discount } }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export default function Lists({ user, entities = [], permissions }: Props) {
|
|||||||
)}
|
)}
|
||||||
{checkAccess(user, ["developer", "admin", "corporate", "mastercorporate"]) && entitiesViewCodes.length > 0 && (
|
{checkAccess(user, ["developer", "admin", "corporate", "mastercorporate"]) && entitiesViewCodes.length > 0 && (
|
||||||
<TabPanel className="overflow-y-scroll max-h-[600px] rounded-xl scrollbar-hide">
|
<TabPanel className="overflow-y-scroll max-h-[600px] rounded-xl scrollbar-hide">
|
||||||
<CodeList user={user} canDeleteCodes={entitiesDeleteCodes.length > 0} />
|
<CodeList user={user} entities={entitiesViewCodes} canDeleteCodes={entitiesDeleteCodes.length > 0} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
)}
|
)}
|
||||||
{checkAccess(user, ["developer", "admin"]) && (
|
{checkAccess(user, ["developer", "admin"]) && (
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ import { sessionOptions } from "@/lib/session";
|
|||||||
import { Code, Group, Type } from "@/interfaces/user";
|
import { Code, Group, Type } from "@/interfaces/user";
|
||||||
import { PERMISSIONS } from "@/constants/userPermissions";
|
import { PERMISSIONS } from "@/constants/userPermissions";
|
||||||
import { prepareMailer, prepareMailOptions } from "@/email";
|
import { prepareMailer, prepareMailOptions } from "@/email";
|
||||||
|
import { isAdmin } from "@/utils/users";
|
||||||
|
import { requestUser } from "@/utils/api";
|
||||||
|
import { doesEntityAllow } from "@/utils/permissions";
|
||||||
|
import { getEntity, getEntityWithRoles } from "@/utils/entities.be";
|
||||||
|
import { findBy } from "@/utils";
|
||||||
|
import { EntityWithRoles } from "@/interfaces/entity";
|
||||||
|
|
||||||
const db = client.db(process.env.MONGODB_DB);
|
const db = client.db(process.env.MONGODB_DB);
|
||||||
|
|
||||||
@@ -25,117 +31,98 @@ async function get(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { creator } = req.query as { creator?: string };
|
const { entity } = req.query as { entity?: string };
|
||||||
const snapshot = await db.collection("codes").find(creator ? { creator: creator } : {}).toArray();
|
const snapshot = await db.collection("codes").find(entity ? { entity } : {}).toArray();
|
||||||
|
|
||||||
res.status(200).json(snapshot);
|
res.status(200).json(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function post(req: NextApiRequest, res: NextApiResponse) {
|
const generateAndSendCode = async (
|
||||||
if (!req.session.user) {
|
code: string,
|
||||||
res.status(401).json({ ok: false, reason: "You must be logged in to generate a code!" });
|
type: Type,
|
||||||
return;
|
expiryDate: null | Date,
|
||||||
|
entity?: string,
|
||||||
|
info?: {
|
||||||
|
email: string; name: string; passport_id?: string
|
||||||
|
}) => {
|
||||||
|
if (!info) {
|
||||||
|
await db.collection("codes").insertOne({
|
||||||
|
code, type, expiryDate, entity
|
||||||
|
})
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const { type, codes, infos, expiryDate } = req.body as {
|
const previousCode = await db.collection("codes").findOne<Code>({ email: info.email, entity })
|
||||||
|
|
||||||
|
const transport = prepareMailer();
|
||||||
|
const mailOptions = prepareMailOptions(
|
||||||
|
{
|
||||||
|
type,
|
||||||
|
code: previousCode ? previousCode.code : code,
|
||||||
|
environment: process.env.ENVIRONMENT,
|
||||||
|
},
|
||||||
|
[info.email.toLowerCase().trim()],
|
||||||
|
"EnCoach Registration",
|
||||||
|
"main",
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await transport.sendMail(mailOptions);
|
||||||
|
if (!previousCode) {
|
||||||
|
await db.collection("codes").insertOne({
|
||||||
|
code, type, expiryDate, entity, name: info.name.trim(), email: info.email.trim().toLowerCase(),
|
||||||
|
...(info.passport_id ? { passport_id: info.passport_id.trim() } : {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const countAvailableCodes = async (entity: EntityWithRoles) => {
|
||||||
|
const usedUp = await db.collection("codes").countDocuments({ entity: entity.id })
|
||||||
|
const total = entity.licenses
|
||||||
|
|
||||||
|
return total - usedUp
|
||||||
|
}
|
||||||
|
|
||||||
|
async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
const user = await requestUser(req, res)
|
||||||
|
if (!user) return res.status(401).json({ ok: false, reason: "You must be logged in to generate a code!" });
|
||||||
|
|
||||||
|
const { type, codes, infos, expiryDate, entity } = req.body as {
|
||||||
type: Type;
|
type: Type;
|
||||||
codes: string[];
|
codes: string[];
|
||||||
infos?: { email: string; name: string; passport_id?: string }[];
|
infos?: { email: string; name: string; passport_id?: string, code: string }[];
|
||||||
expiryDate: null | Date;
|
expiryDate: null | Date;
|
||||||
|
entity?: string
|
||||||
};
|
};
|
||||||
const permission = PERMISSIONS.generateCode[type];
|
|
||||||
|
|
||||||
if (!permission.includes(req.session.user.type)) {
|
if (!entity && !isAdmin(user))
|
||||||
res.status(403).json({
|
return res.status(403).json({ ok: false, reason: "You must be an admin to generate a code without an entity!" });
|
||||||
ok: false,
|
|
||||||
reason: "Your account type does not have permissions to generate a code for that type of user!",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userCodes = await db.collection("codes").find<Code>({ creator: req.session.user.id }).toArray()
|
const entityObj = entity ? await getEntityWithRoles(entity) : undefined
|
||||||
const creatorGroupsSnapshot = await db.collection("groups").find<Group>({ admin: req.session.user.id }).toArray()
|
const isAllowed = entityObj ? doesEntityAllow(user, entityObj, 'create_code') : true
|
||||||
|
if (!isAllowed) return res.status(403).json({ ok: false, reason: "You do not have permissions to generate a code!" });
|
||||||
|
|
||||||
const creatorGroups = creatorGroupsSnapshot.filter((x) => x.name === "Students" || x.name === "Teachers" || x.name === "Corporate");
|
if (entityObj) {
|
||||||
const usersInGroups = creatorGroups.flatMap((x) => x.participants);
|
const availableCodes = await countAvailableCodes(entityObj)
|
||||||
|
if (availableCodes < codes.length)
|
||||||
|
return res.status(400).json({
|
||||||
if (req.session.user.type === "corporate") {
|
|
||||||
const totalCodes = userCodes.filter((x) => !x.userId || !usersInGroups.includes(x.userId)).length + usersInGroups.length + codes.length;
|
|
||||||
const allowedCodes = 0;
|
|
||||||
|
|
||||||
if (totalCodes > allowedCodes) {
|
|
||||||
res.status(403).json({
|
|
||||||
ok: false,
|
ok: false,
|
||||||
reason: `You have or would have exceeded your amount of allowed codes, you currently are allowed to generate ${allowedCodes - userCodes.length
|
reason: `You only have ${availableCodes} codes available, while trying to create ${codes.length} codes`
|
||||||
} codes.`,
|
})
|
||||||
});
|
}
|
||||||
return;
|
const valid = []
|
||||||
}
|
for (const code of codes) {
|
||||||
|
const info = findBy(infos || [], 'code', code)
|
||||||
|
const isValid = await generateAndSendCode(code, type, expiryDate, entity, info)
|
||||||
|
valid.push(isValid)
|
||||||
}
|
}
|
||||||
|
|
||||||
const codePromises = codes.map(async (code, index) => {
|
return res.status(200).json({ ok: true, valid: valid.length });
|
||||||
const codeRef = await db.collection("codes").findOne<Code>({ id: code });
|
|
||||||
let codeInformation = {
|
|
||||||
type,
|
|
||||||
code,
|
|
||||||
creator: req.session.user!.id,
|
|
||||||
creationDate: new Date().toISOString(),
|
|
||||||
expiryDate,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (infos && infos.length > index) {
|
|
||||||
const { email, name, passport_id } = infos[index];
|
|
||||||
const previousCode = userCodes.find((x) => x.email === email) as Code;
|
|
||||||
|
|
||||||
const transport = prepareMailer();
|
|
||||||
const mailOptions = prepareMailOptions(
|
|
||||||
{
|
|
||||||
type,
|
|
||||||
code: previousCode ? previousCode.code : code,
|
|
||||||
environment: process.env.ENVIRONMENT,
|
|
||||||
},
|
|
||||||
[email.toLowerCase().trim()],
|
|
||||||
"EnCoach Registration",
|
|
||||||
"main",
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await transport.sendMail(mailOptions);
|
|
||||||
|
|
||||||
if (!previousCode && codeRef) {
|
|
||||||
await db.collection("codes").updateOne(
|
|
||||||
{ id: codeRef.id },
|
|
||||||
{
|
|
||||||
$set: {
|
|
||||||
id: codeRef.id,
|
|
||||||
...codeInformation,
|
|
||||||
email: email.trim().toLowerCase(),
|
|
||||||
name: name.trim(),
|
|
||||||
...(passport_id ? { passport_id: passport_id.trim() } : {}),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ upsert: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// upsert: true -> if it doesnt exist insert
|
|
||||||
await db.collection("codes").updateOne(
|
|
||||||
{ id: code },
|
|
||||||
{ $set: { id: code, ...codeInformation } },
|
|
||||||
{ upsert: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Promise.all(codePromises).then((results) => {
|
|
||||||
res.status(200).json({ ok: true, valid: results.filter((x) => x).length });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function del(req: NextApiRequest, res: NextApiResponse) {
|
async function del(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import {NextApiRequest, NextApiResponse} from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
import {createUserWithEmailAndPassword, getAuth} from "firebase/auth";
|
import { createUserWithEmailAndPassword, getAuth } from "firebase/auth";
|
||||||
import {app} from "@/firebase";
|
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 {Code, CorporateInformation, DemographicInformation, Group, Type} from "@/interfaces/user";
|
import { Code, CorporateInformation, DemographicInformation, Group, Type } from "@/interfaces/user";
|
||||||
import {addUserToGroupOnCreation} from "@/utils/registration";
|
import { addUserToGroupOnCreation } from "@/utils/registration";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {v4} from "uuid";
|
import { v4 } from "uuid";
|
||||||
import client from "@/lib/mongodb";
|
import client from "@/lib/mongodb";
|
||||||
|
import { addUserToEntity, getEntityWithRoles } from "@/utils/entities.be";
|
||||||
|
import { findBy } from "@/utils";
|
||||||
|
|
||||||
const auth = getAuth(app);
|
const auth = getAuth(app);
|
||||||
const db = client.db(process.env.MONGODB_DB);
|
const db = client.db(process.env.MONGODB_DB);
|
||||||
@@ -29,7 +31,7 @@ const DEFAULT_LEVELS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function register(req: NextApiRequest, res: NextApiResponse) {
|
async function register(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const {type} = req.body as {
|
const { type } = req.body as {
|
||||||
type: "individual" | "corporate";
|
type: "individual" | "corporate";
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,19 +40,18 @@ async function register(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function registerIndividual(req: NextApiRequest, res: NextApiResponse) {
|
async function registerIndividual(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const {email, passport_id, password, code} = req.body as {
|
const { email, passport_id, password, code } = req.body as {
|
||||||
email: string;
|
email: string;
|
||||||
passport_id?: string;
|
passport_id?: string;
|
||||||
password: string;
|
password: string;
|
||||||
code?: string;
|
code?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const codeDoc = await db.collection("codes").findOne<Code>({code});
|
const codeDoc = await db.collection("codes").findOne<Code>({ code });
|
||||||
|
|
||||||
|
if (code && code.length > 0 && !codeDoc)
|
||||||
|
return res.status(400).json({ error: "Invalid Code!" });
|
||||||
|
|
||||||
if (code && code.length > 0 && !!codeDoc) {
|
|
||||||
res.status(400).json({error: "Invalid Code!"});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
createUserWithEmailAndPassword(auth, email.toLowerCase(), password)
|
createUserWithEmailAndPassword(auth, email.toLowerCase(), password)
|
||||||
.then(async (userCredentials) => {
|
.then(async (userCredentials) => {
|
||||||
@@ -69,7 +70,7 @@ async function registerIndividual(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
focus: "academic",
|
focus: "academic",
|
||||||
type: email.endsWith("@ecrop.dev") ? "developer" : codeDoc ? codeDoc.type : "student",
|
type: email.endsWith("@ecrop.dev") ? "developer" : codeDoc ? codeDoc.type : "student",
|
||||||
subscriptionExpirationDate: codeDoc ? codeDoc.expiryDate : moment().subtract(1, "days").toISOString(),
|
subscriptionExpirationDate: codeDoc ? codeDoc.expiryDate : moment().subtract(1, "days").toISOString(),
|
||||||
...(passport_id ? {demographicInformation: {passport_id}} : {}),
|
...(passport_id ? { demographicInformation: { passport_id } } : {}),
|
||||||
registrationDate: new Date().toISOString(),
|
registrationDate: new Date().toISOString(),
|
||||||
status: code ? "active" : "paymentDue",
|
status: code ? "active" : "paymentDue",
|
||||||
// apparently there's an issue with the verification email system
|
// apparently there's an issue with the verification email system
|
||||||
@@ -80,23 +81,29 @@ async function registerIndividual(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
await db.collection("users").insertOne(user);
|
await db.collection("users").insertOne(user);
|
||||||
|
|
||||||
if (!!codeDoc) {
|
if (!!codeDoc) {
|
||||||
await db.collection("codes").updateOne({code: codeDoc.code}, {$set: {userId}});
|
await db.collection("codes").updateOne({ code: codeDoc.code }, { $set: { userId } });
|
||||||
if (codeDoc.creator) await addUserToGroupOnCreation(userId, codeDoc.type, codeDoc.creator);
|
if (codeDoc.entity) {
|
||||||
|
const inviteEntity = await getEntityWithRoles(codeDoc.entity)
|
||||||
|
if (inviteEntity) {
|
||||||
|
const defaultRole = findBy(inviteEntity.roles, 'isDefault', true)!
|
||||||
|
await addUserToEntity(userId, codeDoc.entity, defaultRole.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.session.user = user;
|
req.session.user = user;
|
||||||
await req.session.save();
|
await req.session.save();
|
||||||
|
|
||||||
res.status(200).json({user});
|
res.status(200).json({ user });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
res.status(401).json({error});
|
res.status(401).json({ error });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function registerCorporate(req: NextApiRequest, res: NextApiResponse) {
|
async function registerCorporate(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const {email, password} = req.body as {
|
const { email, password } = req.body as {
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
corporateInformation: CorporateInformation;
|
corporateInformation: CorporateInformation;
|
||||||
@@ -155,10 +162,10 @@ async function registerCorporate(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
req.session.user = user;
|
req.session.user = user;
|
||||||
await req.session.save();
|
await req.session.save();
|
||||||
|
|
||||||
res.status(200).json({user});
|
res.status(200).json({ user });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
res.status(401).json({error});
|
res.status(401).json({ error });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,10 +94,21 @@ export default function Admin({ user, entities, permissions, allUsers, entitiesG
|
|||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal isOpen={modalOpen === "batchCreateCode"} onClose={() => setModalOpen(undefined)}>
|
<Modal isOpen={modalOpen === "batchCreateCode"} onClose={() => setModalOpen(undefined)}>
|
||||||
<BatchCodeGenerator user={user} users={allUsers} permissions={permissions} onFinish={() => setModalOpen(undefined)} />
|
<BatchCodeGenerator
|
||||||
|
entities={entitiesAllowCreateCodes}
|
||||||
|
user={user}
|
||||||
|
users={allUsers}
|
||||||
|
permissions={permissions}
|
||||||
|
onFinish={() => setModalOpen(undefined)}
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal isOpen={modalOpen === "createCode"} onClose={() => setModalOpen(undefined)}>
|
<Modal isOpen={modalOpen === "createCode"} onClose={() => setModalOpen(undefined)}>
|
||||||
<CodeGenerator user={user} permissions={permissions} onFinish={() => setModalOpen(undefined)} />
|
<CodeGenerator
|
||||||
|
entities={entitiesAllowCreateCode}
|
||||||
|
user={user}
|
||||||
|
permissions={permissions}
|
||||||
|
onFinish={() => setModalOpen(undefined)}
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal isOpen={modalOpen === "createUser"} onClose={() => setModalOpen(undefined)}>
|
<Modal isOpen={modalOpen === "createUser"} onClose={() => setModalOpen(undefined)}>
|
||||||
<UserCreator
|
<UserCreator
|
||||||
|
|||||||
Reference in New Issue
Block a user