ENCOA-283 or ENCOA-282, I don't know someone deleted the issue
This commit is contained in:
@@ -195,6 +195,13 @@ const ClassroomImportSummary: React.FC<{state: ClassroomTransferState}> = ({ sta
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
{state.alreadyInClass && state.alreadyInClass.length > 0 && (
|
||||
<li>
|
||||
<div className="text-gray-700">
|
||||
<span className="font-medium">{state.alreadyInClass.length}</span> users that are already assigned to the classroom
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
{errorCount > 0 && (
|
||||
<li>
|
||||
<div className="text-gray-700">
|
||||
@@ -221,7 +228,7 @@ const ClassroomImportSummary: React.FC<{state: ClassroomTransferState}> = ({ sta
|
||||
</>
|
||||
</Modal>
|
||||
|
||||
<Modal isOpen={showNotFoundModal} onClose={() => setShowNotFoundModal(false)}>
|
||||
<Modal isOpen={showNotFoundModal} onClose={() => setShowNotFoundModal(false)} maxWidth="max-w-[85%]">
|
||||
<>
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<FaTimesCircle className="w-5 h-5 text-red-500" />
|
||||
@@ -231,7 +238,7 @@ const ClassroomImportSummary: React.FC<{state: ClassroomTransferState}> = ({ sta
|
||||
</>
|
||||
</Modal>
|
||||
|
||||
<Modal isOpen={showOtherEntityModal} onClose={() => setShowOtherEntityModal(false)}>
|
||||
<Modal isOpen={showOtherEntityModal} onClose={() => setShowOtherEntityModal(false)} maxWidth="max-w-[85%]">
|
||||
<>
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<FaExclamationCircle className="w-5 h-5 text-yellow-500" />
|
||||
@@ -241,7 +248,7 @@ const ClassroomImportSummary: React.FC<{state: ClassroomTransferState}> = ({ sta
|
||||
</>
|
||||
</Modal>
|
||||
|
||||
<Modal isOpen={showAlreadyInClassModal} onClose={() => setShowAlreadyInClassModal(false)}>
|
||||
<Modal isOpen={showAlreadyInClassModal} onClose={() => setShowAlreadyInClassModal(false)} maxWidth="max-w-[85%]">
|
||||
<>
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<FaUsers className="w-5 h-5 text-blue-500" />
|
||||
|
||||
@@ -161,10 +161,9 @@ const StudentClassroomTransfer: React.FC<{ user: User; entities?: EntityWithRole
|
||||
readXlsxFile(
|
||||
file.content, { schema, ignoreEmptyRows: false })
|
||||
.then((data) => {
|
||||
setClassroomTransferState((prev) => ({...prev, parsedExcel: data}))
|
||||
console.log(data);
|
||||
setClassroomTransferState((prev) => ({ ...prev, parsedExcel: data }))
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [filesContent]);
|
||||
@@ -236,7 +235,6 @@ const StudentClassroomTransfer: React.FC<{ user: User; entities?: EntityWithRole
|
||||
} as UserImport;
|
||||
})
|
||||
.filter((item): item is UserImport => item !== undefined);
|
||||
console.log(infos);
|
||||
|
||||
// On import reset state except excel parsing
|
||||
setClassroomTransferState((prev) => ({
|
||||
@@ -260,26 +258,26 @@ const StudentClassroomTransfer: React.FC<{ user: User; entities?: EntityWithRole
|
||||
const emails = classroomTransferState.imports.map((i) => i.email);
|
||||
const crossRefUsers = async () => {
|
||||
try {
|
||||
console.log(user.entities);
|
||||
const { data: nonExistantUsers } = await axios.post("/api/users/controller?op=dontExist", { emails });
|
||||
const { data: nonEntityUsers } = await axios.post("/api/users/controller?op=entityCheck", { entities: user.entities, emails });
|
||||
const { data: alreadyPlaced } = await axios.post("/api/users/controller?op=crossRefClassrooms", {
|
||||
sets: classroomTransferState.imports.map((info) => ({
|
||||
email: info.email,
|
||||
classroom: info.groupName
|
||||
}))
|
||||
})),
|
||||
entity
|
||||
});
|
||||
|
||||
const excludeEmails = new Set([
|
||||
...nonExistantUsers,
|
||||
...nonEntityUsers,
|
||||
...nonEntityUsers.map((o: any) => o.email),
|
||||
...alreadyPlaced
|
||||
]);
|
||||
|
||||
const filteredImports = classroomTransferState.imports.filter(i => !excludeEmails.has(i.email));
|
||||
|
||||
const nonExistantEmails = new Set(nonExistantUsers);
|
||||
const nonEntityEmails = new Set(nonEntityUsers);
|
||||
const nonEntityEmails = new Set(nonEntityUsers.map((o: any) => o.email));
|
||||
const alreadyPlacedEmails = new Set(alreadyPlaced);
|
||||
|
||||
setClassroomTransferState((prev) => ({
|
||||
@@ -287,7 +285,14 @@ const StudentClassroomTransfer: React.FC<{ user: User; entities?: EntityWithRole
|
||||
stage: 2,
|
||||
imports: filteredImports,
|
||||
notFoundUsers: classroomTransferState.imports.filter(i => nonExistantEmails.has(i.email)),
|
||||
otherEntityUsers: classroomTransferState.imports.filter(i => nonEntityEmails.has(i.email)),
|
||||
otherEntityUsers: classroomTransferState.imports.filter(i => nonEntityEmails.has(i.email))
|
||||
.map(user => {
|
||||
const nonEntityUser = nonEntityUsers.find((o: any) => o.email === user.email);
|
||||
return {
|
||||
...user,
|
||||
entityLabels: nonEntityUser?.names || []
|
||||
};
|
||||
}),
|
||||
alreadyInClass: classroomTransferState.imports.filter(i => alreadyPlacedEmails.has(i.email)),
|
||||
}));
|
||||
} catch (error) {
|
||||
@@ -299,14 +304,14 @@ const StudentClassroomTransfer: React.FC<{ user: User; entities?: EntityWithRole
|
||||
crossRefUsers();
|
||||
}
|
||||
|
||||
}, [classroomTransferState.imports, user.entities, classroomTransferState.stage])
|
||||
}, [classroomTransferState.imports, user.entities, classroomTransferState.stage, entity])
|
||||
|
||||
// Stage 3 - Classroom Filter
|
||||
// - See if there are classrooms with same name but different admin
|
||||
// - Find which new classrooms need to be created
|
||||
useEffect(() => {
|
||||
const crossRefClassrooms = async () => {
|
||||
const classrooms = new Set(classroomTransferState.imports.map((i) => i.groupName));
|
||||
const classrooms = Array.from(new Set(classroomTransferState.imports.map((i) => i.groupName)));
|
||||
|
||||
try {
|
||||
const { data: notOwnedClassroomsSameName } = await axios.post("/api/groups/controller?op=crossRefOwnership", {
|
||||
@@ -362,7 +367,42 @@ const StudentClassroomTransfer: React.FC<{ user: User; entities?: EntityWithRole
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const groupedUsers = classroomTransferState.imports.reduce((acc, user) => {
|
||||
|
||||
const getIds = async () => {
|
||||
try {
|
||||
const { data: emailIdMap } = await axios.post("/api/users/controller?op=getIds", {
|
||||
emails: classroomTransferState.imports.map((u) => u.email)
|
||||
});
|
||||
|
||||
return classroomTransferState.imports.map(importUser => {
|
||||
const matchingUser = emailIdMap.find((mapping: any) => mapping.email === importUser.email);
|
||||
return {
|
||||
...importUser,
|
||||
id: matchingUser?.id || undefined
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
toast.error("Something went wrong, please try again later!");
|
||||
}
|
||||
};
|
||||
|
||||
const imports = await getIds();
|
||||
|
||||
if (!imports) return;
|
||||
|
||||
|
||||
await axios.post("/api/groups/controller?op=deletePriorEntitiesGroups", {
|
||||
ids: imports.map((u) => u.id),
|
||||
entity
|
||||
});
|
||||
|
||||
await axios.post("/api/users/controller?op=assignToEntity", {
|
||||
ids: imports.map((u) => u.id),
|
||||
entity
|
||||
});
|
||||
|
||||
|
||||
const groupedUsers = imports.reduce((acc, user) => {
|
||||
if (!acc[user.groupName]) {
|
||||
acc[user.groupName] = [];
|
||||
}
|
||||
@@ -385,33 +425,38 @@ const StudentClassroomTransfer: React.FC<{ user: User; entities?: EntityWithRole
|
||||
return axios.post('/api/groups', groupData);
|
||||
});
|
||||
|
||||
|
||||
const existingGroupUsers = Object.fromEntries(
|
||||
Object.entries(groupedUsers)
|
||||
.filter(([groupName]) => !classroomTransferState.classroomsToCreate.includes(groupName))
|
||||
);
|
||||
|
||||
const { groups: groupNameToId, users: userEmailToId } = await axios.post('/api/groups?op=getIds', {
|
||||
names: Object.keys(existingGroupUsers),
|
||||
userEmails: Object.values(existingGroupUsers).flat().map(user => user.email)
|
||||
}).then(response => response.data);
|
||||
let updatePromises: Promise<any>[] = [];
|
||||
|
||||
const existingGroupsWithIds = Object.entries(existingGroupUsers).reduce((acc, [groupName, users]) => {
|
||||
acc[groupNameToId[groupName]] = users;
|
||||
return acc;
|
||||
}, {} as Record<string, any[]>);
|
||||
if (Object.keys(existingGroupUsers).length > 0) {
|
||||
const { groups: groupNameToId, users: userEmailToId } = await axios.post('/api/groups?op=getIds', {
|
||||
names: Object.keys(existingGroupUsers),
|
||||
userEmails: Object.values(existingGroupUsers).flat().map(user => user.email)
|
||||
}).then(response => response.data);
|
||||
|
||||
const updatePromises = Object.entries(existingGroupsWithIds).map(([groupId, users]) => {
|
||||
const userIds = users.map(user => userEmailToId[user.email]);
|
||||
return axios.patch(`/api/groups/${groupId}`, {
|
||||
participants: userIds
|
||||
const existingGroupsWithIds = Object.entries(existingGroupUsers).reduce((acc, [groupName, users]) => {
|
||||
acc[groupNameToId[groupName]] = users;
|
||||
return acc;
|
||||
}, {} as Record<string, any[]>);
|
||||
|
||||
updatePromises = Object.entries(existingGroupsWithIds).map(([groupId, users]) => {
|
||||
const userIds = users.map(user => userEmailToId[user.email]);
|
||||
return axios.patch(`/api/groups/${groupId}`, {
|
||||
participants: userIds
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
...createGroupPromises,
|
||||
...updatePromises
|
||||
]);
|
||||
|
||||
|
||||
toast.success(`Successfully assigned all ${classroomTransferState.imports.length} user(s)!`);
|
||||
onFinish();
|
||||
} catch (error) {
|
||||
@@ -537,14 +582,14 @@ const StudentClassroomTransfer: React.FC<{ user: User; entities?: EntityWithRole
|
||||
</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>
|
||||
<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>
|
||||
{classroomTransferState.parsedExcel?.rows !== undefined && (
|
||||
<ClassroomImportSummary state={classroomTransferState} />
|
||||
|
||||
@@ -51,12 +51,28 @@ const columns = [
|
||||
];
|
||||
|
||||
|
||||
const institutionsColumn = columnHelper.accessor('entityLabels', {
|
||||
cell: (info): string => {
|
||||
const value = info.getValue();
|
||||
if (!value || value.length === 0) {
|
||||
return 'None';
|
||||
}
|
||||
return value.join(', ');
|
||||
},
|
||||
header: () => 'Institutions',
|
||||
}) as unknown as typeof columns[0];
|
||||
|
||||
const UserTable: React.FC<{ users: UserImport[] }> = ({ users }) => {
|
||||
const [globalFilter, setGlobalFilter] = useState('');
|
||||
|
||||
const tableColumns = [...columns];
|
||||
if (users.some(user => 'entityLabels' in user)) {
|
||||
tableColumns.push(institutionsColumn);
|
||||
}
|
||||
|
||||
const table = useReactTable({
|
||||
data: users,
|
||||
columns,
|
||||
columns: tableColumns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
|
||||
Reference in New Issue
Block a user