Merge with develop

This commit is contained in:
Carlos-Mesquita
2024-11-06 10:59:26 +00:00
135 changed files with 9517 additions and 3617 deletions

View File

@@ -17,6 +17,8 @@ import countryCodes from "country-codes-list";
import { User, Type as UserType } from "@/interfaces/user";
import { Type, UserImport } from "./IUserImport";
import UserTable from "./UserTable";
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]+)*$/);
@@ -62,10 +64,11 @@ const USER_TYPE_PERMISSIONS: {
interface Props {
user: User;
permissions: PermissionType[];
entities: EntityWithRoles[]
onFinish: () => void;
}
export default function BatchCreateUser({ user, permissions, onFinish }: Props) {
export default function BatchCreateUser({ user, entities, permissions, onFinish }: Props) {
const [infos, setInfos] = useState<UserImport[]>([]);
const [duplicatedUsers, setDuplicatedUsers] = useState<UserImport[]>([]);
@@ -78,6 +81,7 @@ export default function BatchCreateUser({ user, permissions, onFinish }: Props)
const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true);
const [type, setType] = useState<Type>("student");
const [showHelp, setShowHelp] = useState(false);
const [entity, setEntity] = useState((entities || [])[0]?.id || undefined)
const { openFilePicker, filesContent, clear } = useFilePicker({
accept: ".xlsx",
@@ -97,7 +101,7 @@ export default function BatchCreateUser({ user, permissions, onFinish }: Props)
const information = uniqBy(
rows
.map((row) => {
const [firstName, lastName, studentID, passport_id, email, phone, corporate, group, country] = row as string[];
const [firstName, lastName, studentID, passport_id, email, phone, group, country] = row as string[];
const countryItem =
countryCodes.findOne("countryCode" as any, country.toUpperCase()) ||
countryCodes.all().find((x) => x.countryNameEn.toLowerCase() === country.toLowerCase());
@@ -109,8 +113,8 @@ export default function BatchCreateUser({ user, permissions, onFinish }: Props)
type: type,
passport_id: passport_id?.toString().trim() || undefined,
groupName: group,
corporate,
studentID,
entity,
demographicInformation: {
country: countryItem?.countryCode,
passport_id: passport_id?.toString().trim() || undefined,
@@ -131,7 +135,9 @@ export default function BatchCreateUser({ user, permissions, onFinish }: Props)
}
setInfos(information);
} catch {
} catch(e) {
console.log(e)
toast.error(
"Please upload an Excel file containing user information, one per line! All already registered e-mails have also been ignored!",
);
@@ -170,22 +176,37 @@ export default function BatchCreateUser({ user, permissions, onFinish }: Props)
}, [infos]);
const makeUsers = async () => {
if (!confirm(`You are about to add ${newUsers.length} user${newUsers.length !== 1 ? 's' : ''}, are you sure you want to continue?`)) return;
const newUsersSentence = newUsers.length > 0 ? `create ${newUsers.length} user(s)` : undefined;
const existingUsersSentence = duplicatedUsers.length > 0 ? `invite ${duplicatedUsers.length} registered student(s)` : undefined;
if (!confirm(`You are about to ${[newUsersSentence, existingUsersSentence].filter((x) => !!x).join(" and ")}, are you sure you want to continue?`))
return;
/*Promise.all(duplicatedUsers.map(async (u) => await axios.post(`/api/invites`, {to: u.id, entity, from: user.id})))
.then(() => toast.success(`Successfully invited ${duplicatedUsers.length} registered student(s)!`))
.finally(() => {
if (newUsers.length === 0) setIsLoading(false);
});
*/
if (newUsers.length > 0) {
setIsLoading(true);
try {
//await axios.post("/api/batch_users", {users: newUsers.map((user) => ({...user, type, expiryDate}))});
await axios.post("/api/batch_users", {users: newUsers.map((user) => ({...user, type, expiryDate}))});
toast.success(`Successfully added ${newUsers.length} user(s)!`);
onFinish();
} catch {
} catch(e) {
console.error(e)
toast.error("Something went wrong, please try again later!");
} finally {
setIsLoading(false);
setInfos([]);
clear();
}
} else {
setIsLoading(false);
setInfos([]);
clear();
}
};
@@ -203,8 +224,7 @@ export default function BatchCreateUser({ user, permissions, onFinish }: Props)
<th className="border border-neutral-200 px-2 py-1">Passport/National ID</th>
<th className="border border-neutral-200 px-2 py-1">E-mail</th>
<th className="border border-neutral-200 px-2 py-1">Phone Number</th>
{user?.type !== "corporate" && <th className="border border-neutral-200 px-2 py-1">Corporate (e-mail)</th>}
<th className="border border-neutral-200 px-2 py-1">Group Name</th>
<th className="border border-neutral-200 px-2 py-1">Classroom Name</th>
<th className="border border-neutral-200 px-2 py-1">Country</th>
</tr>
</thead>
@@ -221,15 +241,28 @@ export default function BatchCreateUser({ user, permissions, onFinish }: Props)
</div>
</Modal>
<div className="border-mti-gray-platinum flex flex-col gap-4 rounded-xl border p-4">
<div className="flex items-end justify-between">
<label className="text-mti-gray-dim text-base font-normal">Choose an Excel file</label>
<div className="tooltip cursor-pointer" data-tip="Excel File Format" onClick={() => setShowHelp(true)}>
<BsQuestionCircleFill />
<div className="grid grid-cols-2 -md:grid-cols-1 gap-4">
<div className="flex flex-col gap-4">
<div className="flex items-end justify-between">
<label className="text-mti-gray-dim text-base font-normal">Choose an Excel file</label>
<div className="tooltip cursor-pointer" data-tip="Excel File Format" onClick={() => setShowHelp(true)}>
<BsQuestionCircleFill />
</div>
</div>
<Button onClick={openFilePicker} isLoading={isLoading} disabled={isLoading}>
{filesContent.length > 0 ? filesContent[0].name : "Choose a file"}
</Button>
</div>
<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>
<Button onClick={openFilePicker} isLoading={isLoading} disabled={isLoading}>
{filesContent.length > 0 ? filesContent[0].name : "Choose a file"}
</Button>
{user && 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">