Fixed infinite loop on the dashboards

This commit is contained in:
Joao Ramos
2024-09-06 23:48:18 +01:00
parent f6550e6a36
commit 58448a391f
13 changed files with 102 additions and 49 deletions

View File

@@ -1,8 +1,8 @@
/* eslint-disable @next/next/no-img-element */
import Modal from "@/components/Modal";
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
import useUsers from "@/hooks/useUsers";
import {CorporateUser, Group, MasterCorporateUser, Stat, User} from "@/interfaces/user";
import useUsers, { userHashStudent, userHashTeacher, userHashCorporate} from "@/hooks/useUsers";
import {CorporateUser, Group, MasterCorporateUser, Stat, User } from "@/interfaces/user";
import UserList from "@/pages/(admin)/Lists/UserList";
import {dateSorter} from "@/utils";
import moment from "moment";
@@ -156,6 +156,7 @@ const StudentPerformanceList = ({items, stats, users}: {items: StudentPerformanc
);
};
export default function CorporateDashboard({user, linkedCorporate}: Props) {
const [selectedUser, setSelectedUser] = useState<User>();
const [showModal, setShowModal] = useState(false);
@@ -165,8 +166,8 @@ export default function CorporateDashboard({user, linkedCorporate}: Props) {
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({corporate: user.id});
const {balance} = useUserBalance();
const {users: students, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers({type: "student"});
const {users: teachers, reload: reloadTeachers, isLoading: isTeachersLoading} = useUsers({type: "teacher"});
const {users: students, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers(userHashStudent);
const {users: teachers, reload: reloadTeachers, isLoading: isTeachersLoading} = useUsers(userHashTeacher);
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
const router = useRouter();

View File

@@ -1,5 +1,5 @@
import React, {useMemo} from "react";
import useUsers from "@/hooks/useUsers";
import useUsers, { userHashStudent, userHashTeacher, userHashCorporate } from "@/hooks/useUsers";
import useGroups from "@/hooks/useGroups";
import {User} from "@/interfaces/user";
import Select from "@/components/Low/Select";
@@ -63,8 +63,8 @@ const Card = ({user}: {user: User}) => {
const CorporateStudentsLevels = () => {
const [corporateId, setCorporateId] = React.useState<string>("");
const {users: students} = useUsers({type: "student"});
const {users: corporates} = useUsers({type: "corporate"});
const {users: students} = useUsers(userHashStudent);
const {users: corporates} = useUsers(userHashCorporate);
const corporate = useMemo(() => corporates.find((u) => u.id === corporateId) || corporates[0], [corporates, corporateId]);

View File

@@ -1,7 +1,7 @@
/* eslint-disable @next/next/no-img-element */
import Modal from "@/components/Modal";
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
import useUsers from "@/hooks/useUsers";
import useUsers, { userHashStudent, userHashTeacher, userHashCorporate } from "@/hooks/useUsers";
import {CorporateUser, Group, MasterCorporateUser, Stat, User} from "@/interfaces/user";
import UserList from "@/pages/(admin)/Lists/UserList";
import {dateSorter} from "@/utils";
@@ -302,9 +302,9 @@ export default function MasterCorporateDashboard({user}: Props) {
const {data: stats} = useFilterRecordsByUser<Stat[]>();
const {users: students, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers({type: "student"});
const {users: teachers, reload: reloadTeachers, isLoading: isTeachersLoading} = useUsers({type: "teacher"});
const {users: corporates, reload: reloadCorporates, isLoading: isCorporatesLoading} = useUsers({type: "corporate"});
const {users: students, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers(userHashStudent);
const {users: teachers, reload: reloadTeachers, isLoading: isTeachersLoading} = useUsers(userHashTeacher);
const {users: corporates, reload: reloadCorporates, isLoading: isCorporatesLoading} = useUsers(userHashCorporate);
const {groups} = useGroups({admin: user.id, userType: user.type});
const {balance} = useUserBalance();

View File

@@ -6,7 +6,7 @@ import useAssignments from "@/hooks/useAssignments";
import useGradingSystem from "@/hooks/useGrading";
import useInvites from "@/hooks/useInvites";
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
import useUsers from "@/hooks/useUsers";
import useUsers, { userHashStudent, userHashTeacher, userHashCorporate} from "@/hooks/useUsers";
import {Invite} from "@/interfaces/invite";
import {Assignment} from "@/interfaces/results";
import {CorporateUser, MasterCorporateUser, Stat, User} from "@/interfaces/user";
@@ -43,8 +43,8 @@ export default function StudentDashboard({user, linkedCorporate}: Props) {
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({assignees: user?.id});
const {invites, isLoading: isInvitesLoading, reload: reloadInvites} = useInvites({to: user.id});
const {users: teachers} = useUsers({type: "teacher"});
const {users: corporates} = useUsers({type: "corporate"});
const {users: teachers} = useUsers(userHashTeacher);
const {users: corporates} = useUsers(userHashCorporate);
const users = useMemo(() => [...teachers, ...corporates], [teachers, corporates]);
const router = useRouter();

View File

@@ -1,7 +1,7 @@
/* eslint-disable @next/next/no-img-element */
import Modal from "@/components/Modal";
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
import useUsers from "@/hooks/useUsers";
import useUsers, { userHashStudent, userHashTeacher, userHashCorporate } from "@/hooks/useUsers";
import {CorporateUser, Group, MasterCorporateUser, Stat, User} from "@/interfaces/user";
import UserList from "@/pages/(admin)/Lists/UserList";
import {dateSorter} from "@/utils";
@@ -67,7 +67,7 @@ export default function TeacherDashboard({user, linkedCorporate}: Props) {
const {permissions} = usePermissions(user.id);
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({assigner: user.id});
const {users: students, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers({type: "student"});
const {users: students, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers(userHashStudent);
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
const router = useRouter();

View File

@@ -2,10 +2,13 @@ import {Type, User} from "@/interfaces/user";
import Axios from "axios";
import {useEffect, useState} from "react";
import {setupCache} from "axios-cache-interceptor";
const instance = Axios.create();
const axios = setupCache(instance);
export const userHashStudent = { type: "student" } as { type: Type };
export const userHashTeacher = { type: "teacher" } as { type: Type };
export const userHashCorporate = { type: "corporate" } as { type: Type };
export default function useUsers(props?: {type?: Type; page?: number; size?: number}) {
const [users, setUsers] = useState<User[]>([]);
const [total, setTotal] = useState(0);

View File

@@ -164,4 +164,4 @@ export interface Code {
}
export type Type = "student" | "teacher" | "corporate" | "admin" | "developer" | "agent" | "mastercorporate";
export const userTypes: Type[] = ["student", "teacher", "corporate", "admin", "developer", "agent", "mastercorporate"];
export const userTypes: Type[] = ["student", "teacher", "corporate", "admin", "developer", "agent", "mastercorporate"];

View File

@@ -9,7 +9,7 @@ import axios from "axios";
import clsx from "clsx";
import {capitalize, reverse} from "lodash";
import moment from "moment";
import {Fragment, useEffect, useState} from "react";
import {Fragment, useEffect, useState, useMemo} from "react";
import {BsArrowDown, BsArrowDownUp, BsArrowUp, BsCheck, BsCheckCircle, BsEye, BsFillExclamationOctagonFill, BsPerson, BsTrash} from "react-icons/bs";
import {toast} from "react-toastify";
import {countries, TCountries} from "countries-list";
@@ -59,7 +59,12 @@ export default function UserList({
const [displayUsers, setDisplayUsers] = useState<User[]>([]);
const [selectedUser, setSelectedUser] = useState<User>();
const {users, page, total, reload, next, previous} = useUsers({type, size: 25});
const userHash = useMemo(() => ({
type,
size: 25,
}), [type])
const {users, page, total, reload, next, previous} = useUsers(userHash);
const {permissions} = usePermissions(user?.id || "");
const {balance} = useUserBalance();
const {groups} = useGroups({

View File

@@ -1,37 +1,48 @@
import {CorporateUser, Group, User, Type} from "@/interfaces/user";
import { CorporateUser, Group, User, Type } from "@/interfaces/user";
import axios from "axios";
export const isUserFromCorporate = async (userID: string) => {
const groups = (await axios.get<Group[]>(`/api/groups?participant=${userID}`)).data;
const users = (await axios.get<User[]>("/api/users/list")).data;
const groups = (await axios.get<Group[]>(`/api/groups?participant=${userID}`))
.data;
const usersData = (await axios.get<{users: User[], total: number}>("/api/users/list")).data;
const adminTypes = groups.map((g) => users.find((u) => u.id === g.admin)?.type);
return adminTypes.includes("corporate");
const adminTypes = groups.reduce((accm: Type[], g) => {
const user = usersData.users.find((u) => u.id === g.admin);
if (user) {
return [...accm, user.type];
}
return accm;
}, []);
return adminTypes.includes("corporate");
};
const getAdminForGroup = async (userID: string, role: Type) => {
const groups = (await axios.get<Group[]>(`/api/groups?participant=${userID}`)).data;
const groups = (await axios.get<Group[]>(`/api/groups?participant=${userID}`))
.data;
const adminRequests = await Promise.all(
groups.map(async (g) => {
const userRequest = await axios.get<User>(`/api/users/${g.admin}`);
if (userRequest.status === 200) return userRequest.data;
return undefined;
}),
);
const adminRequests = await Promise.all(
groups.map(async (g) => {
const userRequest = await axios.get<User>(`/api/users/${g.admin}`);
if (userRequest.status === 200) return userRequest.data;
return undefined;
})
);
const admins = adminRequests.filter((x) => x?.type === role);
return admins.length > 0 ? (admins[0] as CorporateUser) : undefined;
const admins = adminRequests.filter((x) => x?.type === role);
return admins.length > 0 ? (admins[0] as CorporateUser) : undefined;
};
export const getUserCorporate = async (userID: string): Promise<CorporateUser | undefined> => {
const userRequest = await axios.get<User>(`/api/users/${userID}`);
if (userRequest.status === 200) {
const user = userRequest.data;
if (user.type === "corporate") {
return getAdminForGroup(userID, "mastercorporate");
}
}
export const getUserCorporate = async (
userID: string
): Promise<CorporateUser | undefined> => {
const userRequest = await axios.get<User>(`/api/users/${userID}`);
if (userRequest.status === 200) {
const user = userRequest.data;
if (user.type === "corporate") {
return getAdminForGroup(userID, "mastercorporate");
}
}
return getAdminForGroup(userID, "corporate");
return getAdminForGroup(userID, "corporate");
};

View File

@@ -86,6 +86,9 @@ export async function getLinkedUsers(userID?: string, userType?: Type, type?: Ty
...(userType === "teacher" ? belongingGroups.flatMap((x) => x.participants) : []),
]);
// [FirebaseError: Invalid Query. A non-empty array is required for 'in' filters.] {
if(participants.length === 0) return {users: [], total: 0};
const snapshot = await getDocs(query(collection(db, "users"), ...[where(documentId(), "in", participants), ...q]));
const users = snapshot.docs.map((doc) => ({
id: doc.id,