Updated the backend so the users list only returns the correct ones
This commit is contained in:
@@ -207,64 +207,6 @@ export default function CorporateDashboard({user, linkedCorporate}: Props) {
|
||||
</div>
|
||||
);
|
||||
|
||||
const StudentsList = () => {
|
||||
const filter = (x: User) =>
|
||||
x.type === "student" &&
|
||||
(!!selectedUser
|
||||
? groups
|
||||
.filter((g) => g.admin === selectedUser.id)
|
||||
.flatMap((g) => g.participants)
|
||||
.includes(x.id) || false
|
||||
: groups.flatMap((g) => g.participants).includes(x.id));
|
||||
|
||||
return (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[filter]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Students ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const TeachersList = () => {
|
||||
const filter = (x: User) =>
|
||||
x.type === "teacher" &&
|
||||
(!!selectedUser
|
||||
? groups
|
||||
.filter((g) => g.admin === selectedUser.id)
|
||||
.flatMap((g) => g.participants)
|
||||
.includes(x.id) || false
|
||||
: groups.flatMap((g) => g.participants).includes(x.id));
|
||||
|
||||
return (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[filter]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Teachers ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const GroupsList = () => {
|
||||
const filter = (x: Group) => x.admin === user.id || x.participants.includes(user.id);
|
||||
|
||||
@@ -518,8 +460,40 @@ export default function CorporateDashboard({user, linkedCorporate}: Props) {
|
||||
)}
|
||||
</>
|
||||
</Modal>
|
||||
{router.asPath === "/#students" && <StudentsList />}
|
||||
{router.asPath === "/#teachers" && <TeachersList />}
|
||||
{router.asPath === "/#students" && (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[(x) => x.type === "student"]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Students ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{router.asPath === "/#teachers" && (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[(x) => x.type === "teacher"]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Teachers ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{router.asPath === "/#groups" && <GroupsList />}
|
||||
{router.asPath === "/#assignments" && (
|
||||
<AssignmentsPage
|
||||
|
||||
@@ -349,8 +349,8 @@ export default function MasterCorporateDashboard({user}: Props) {
|
||||
);
|
||||
}, [assignments, groups, users]);
|
||||
|
||||
const studentFilter = (user: User) => user.type === "student" && corporateUserGroups.includes(user.id);
|
||||
const teacherFilter = (user: User) => user.type === "teacher" && corporateUserGroups.includes(user.id);
|
||||
const studentFilter = (user: User) => user.type === "student";
|
||||
const teacherFilter = (user: User) => user.type === "teacher";
|
||||
const getStatsByStudent = (user: User) => stats.filter((s) => s.user === user.id);
|
||||
|
||||
const UserDisplay = (displayUser: User) => (
|
||||
@@ -365,74 +365,7 @@ export default function MasterCorporateDashboard({user}: Props) {
|
||||
</div>
|
||||
);
|
||||
|
||||
const StudentsList = () => {
|
||||
const filter = (x: User) =>
|
||||
x.type === "student" && (!!selectedUser ? corporateUserGroups.includes(x.id) || false : corporateUserGroups.includes(x.id));
|
||||
|
||||
return (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[filter]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Students ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const TeachersList = () => {
|
||||
const filter = (x: User) =>
|
||||
x.type === "teacher" && (!!selectedUser ? corporateUserGroups.includes(x.id) || false : corporateUserGroups.includes(x.id));
|
||||
|
||||
return (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[filter]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Teachers ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const corporateUserFilter = (x: User) =>
|
||||
x.type === "corporate" && (!!selectedUser ? masterCorporateUserGroups.includes(x.id) || false : masterCorporateUserGroups.includes(x.id));
|
||||
|
||||
const CorporateList = () => {
|
||||
return (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[corporateUserFilter]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Corporates ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const corporateUserFilter = (x: User) => x.type === "corporate";
|
||||
|
||||
const GroupsList = () => {
|
||||
return (
|
||||
@@ -704,10 +637,58 @@ export default function MasterCorporateDashboard({user}: Props) {
|
||||
)}
|
||||
</>
|
||||
</Modal>
|
||||
{router.asPath === "/#students" && <StudentsList />}
|
||||
{router.asPath === "/#teachers" && <TeachersList />}
|
||||
{router.asPath === "/#students" && (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[(x) => x.type === "student"]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Students ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{router.asPath === "/#teachers" && (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[(x) => x.type === "teacher"]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Teachers ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{router.asPath === "/#groups" && <GroupsList />}
|
||||
{router.asPath === "/#corporate" && <CorporateList />}
|
||||
{router.asPath === "/#corporate" && (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[(x) => x.type === "corporate"]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Corporate ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{router.asPath === "/#assignments" && (
|
||||
<AssignmentsPage
|
||||
assignments={assignments}
|
||||
|
||||
@@ -51,6 +51,7 @@ import usePermissions from "@/hooks/usePermissions";
|
||||
import {futureAssignmentFilter, pastAssignmentFilter, archivedAssignmentFilter, activeAssignmentFilter} from "@/utils/assignments";
|
||||
import AssignmentsPage from "./views/AssignmentsPage";
|
||||
import {useRouter} from "next/router";
|
||||
import useFilterStore from "@/stores/listFilterStore";
|
||||
|
||||
interface Props {
|
||||
user: User;
|
||||
@@ -67,6 +68,7 @@ export default function TeacherDashboard({user, linkedCorporate}: Props) {
|
||||
const {permissions} = usePermissions(user.id);
|
||||
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({assigner: user.id});
|
||||
|
||||
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
|
||||
const router = useRouter();
|
||||
|
||||
const assignmentsGroups = useMemo(() => groups.filter((x) => x.admin === user.id || x.participants.includes(user.id)), [groups, user.id]);
|
||||
@@ -90,7 +92,7 @@ export default function TeacherDashboard({user, linkedCorporate}: Props) {
|
||||
setShowModal(!!selectedUser && router.asPath === "/#");
|
||||
}, [selectedUser, router.asPath]);
|
||||
|
||||
const studentFilter = (user: User) => user.type === "student" && groups.flatMap((g) => g.participants).includes(user.id);
|
||||
const studentFilter = (user: User) => user.type === "student";
|
||||
|
||||
const getStatsByStudent = (user: User) => stats.filter((s) => s.user === user.id);
|
||||
|
||||
@@ -106,35 +108,6 @@ export default function TeacherDashboard({user, linkedCorporate}: Props) {
|
||||
</div>
|
||||
);
|
||||
|
||||
const StudentsList = () => {
|
||||
const filter = (x: User) =>
|
||||
x.type === "student" &&
|
||||
(!!selectedUser
|
||||
? groups
|
||||
.filter((g) => g.admin === selectedUser.id)
|
||||
.flatMap((g) => g.participants)
|
||||
.includes(x.id) || false
|
||||
: groups.flatMap((g) => g.participants).includes(x.id));
|
||||
|
||||
return (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[filter]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Students ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const GroupsList = () => {
|
||||
const filter = (x: Group) => x.admin === user.id;
|
||||
|
||||
@@ -285,16 +258,68 @@ export default function TeacherDashboard({user, linkedCorporate}: Props) {
|
||||
if (shouldReload) reload();
|
||||
}}
|
||||
onViewStudents={
|
||||
selectedUser.type === "corporate" || selectedUser.type === "teacher" ? () => router.push("/#students") : undefined
|
||||
selectedUser.type === "corporate" || selectedUser.type === "teacher"
|
||||
? () => {
|
||||
appendUserFilters({
|
||||
id: "view-students",
|
||||
filter: (x: User) => x.type === "student",
|
||||
});
|
||||
appendUserFilters({
|
||||
id: "belongs-to-admin",
|
||||
filter: (x: User) =>
|
||||
groups
|
||||
.filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id))
|
||||
.flatMap((g) => g.participants)
|
||||
.includes(x.id),
|
||||
});
|
||||
|
||||
router.push("/list/users");
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onViewTeachers={
|
||||
selectedUser.type === "corporate" || selectedUser.type === "student"
|
||||
? () => {
|
||||
appendUserFilters({
|
||||
id: "view-teachers",
|
||||
filter: (x: User) => x.type === "teacher",
|
||||
});
|
||||
appendUserFilters({
|
||||
id: "belongs-to-admin",
|
||||
filter: (x: User) =>
|
||||
groups
|
||||
.filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id))
|
||||
.flatMap((g) => g.participants)
|
||||
.includes(x.id),
|
||||
});
|
||||
|
||||
router.push("/list/users");
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onViewTeachers={selectedUser.type === "corporate" ? () => router.push("/#teachers") : undefined}
|
||||
user={selectedUser}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</Modal>
|
||||
{router.asPath === "/#students" && <StudentsList />}
|
||||
{router.asPath === "/#students" && (
|
||||
<UserList
|
||||
user={user}
|
||||
filters={[(x) => x.type === "student"]}
|
||||
renderHeader={(total) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
onClick={() => router.push("/")}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<BsArrowLeft className="text-xl" />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold">Students ({total})</h2>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{router.asPath === "/#groups" && <GroupsList />}
|
||||
{router.asPath === "/#assignments" && (
|
||||
<AssignmentsPage
|
||||
|
||||
@@ -80,19 +80,15 @@ export default function UserList({
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (user && users) {
|
||||
const filterUsers = ["corporate", "teacher", "mastercorporate"].includes(user.type)
|
||||
? users.filter((u) => groups.flatMap((g) => g.participants).includes(u.id))
|
||||
: users;
|
||||
|
||||
const filteredUsers = filters.reduce((d, f) => d.filter(f), filterUsers);
|
||||
if (users) {
|
||||
const filteredUsers = filters.reduce((d, f) => d.filter(f), users);
|
||||
const sortedUsers = await asyncSorter<User>(filteredUsers, sortFunction);
|
||||
|
||||
setDisplayUsers([...sortedUsers]);
|
||||
}
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [user, users, sorter, groups]);
|
||||
}, [users, sorter]);
|
||||
|
||||
const deleteAccount = (user: User) => {
|
||||
if (!confirm(`Are you sure you want to delete ${user.name}'s account?`)) return;
|
||||
|
||||
@@ -4,6 +4,8 @@ import {app} from "@/firebase";
|
||||
import {getFirestore, collection, getDocs} from "firebase/firestore";
|
||||
import {withIronSessionApiRoute} from "iron-session/next";
|
||||
import {sessionOptions} from "@/lib/session";
|
||||
import {getGroupsForUser} from "@/utils/groups.be";
|
||||
import {uniq} from "lodash";
|
||||
|
||||
const db = getFirestore(app);
|
||||
|
||||
@@ -16,11 +18,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
}
|
||||
|
||||
const snapshot = await getDocs(collection(db, "users"));
|
||||
|
||||
res.status(200).json(
|
||||
snapshot.docs.map((doc) => ({
|
||||
const users = snapshot.docs.map((doc) => ({
|
||||
id: doc.id,
|
||||
...doc.data(),
|
||||
})),
|
||||
);
|
||||
}));
|
||||
|
||||
if (!req.session.user) return res.status(200).json(users);
|
||||
if (req.session.user.type === "admin" || req.session.user.type === "developer") return res.status(200).json(users);
|
||||
|
||||
const adminGroups = await getGroupsForUser(req.session.user.id);
|
||||
const groups = await Promise.all(adminGroups.flatMap((x) => x.participants).map(async (x) => await getGroupsForUser(x)));
|
||||
const participants = uniq([...adminGroups.flatMap((x) => x.participants), ...groups.flat().flatMap((x) => x.participants)]);
|
||||
|
||||
res.status(200).json(users.filter((x) => participants.includes(x.id)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user