Added the total of assignments to the Master Corporate
This commit is contained in:
@@ -17,7 +17,6 @@
|
|||||||
"@headlessui/react": "^2.1.2",
|
"@headlessui/react": "^2.1.2",
|
||||||
"@mdi/js": "^7.1.96",
|
"@mdi/js": "^7.1.96",
|
||||||
"@mdi/react": "^1.6.1",
|
"@mdi/react": "^1.6.1",
|
||||||
"@next/font": "13.1.6",
|
|
||||||
"@paypal/paypal-js": "^7.1.0",
|
"@paypal/paypal-js": "^7.1.0",
|
||||||
"@paypal/react-paypal-js": "^8.1.3",
|
"@paypal/react-paypal-js": "^8.1.3",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ import {useAssignmentArchive} from "@/hooks/useAssignmentArchive";
|
|||||||
import {uniqBy} from "lodash";
|
import {uniqBy} from "lodash";
|
||||||
import {useAssignmentUnarchive} from "@/hooks/useAssignmentUnarchive";
|
import {useAssignmentUnarchive} from "@/hooks/useAssignmentUnarchive";
|
||||||
import {getUserName} from "@/utils/users";
|
import {getUserName} from "@/utils/users";
|
||||||
|
import {User} from "@/interfaces/user";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
users: User[];
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
allowDownload?: boolean;
|
allowDownload?: boolean;
|
||||||
reload?: Function;
|
reload?: Function;
|
||||||
@@ -35,9 +37,8 @@ export default function AssignmentCard({
|
|||||||
reload,
|
reload,
|
||||||
allowArchive,
|
allowArchive,
|
||||||
allowUnarchive,
|
allowUnarchive,
|
||||||
|
users,
|
||||||
}: Assignment & Props) {
|
}: Assignment & Props) {
|
||||||
const {users} = useUsers();
|
|
||||||
|
|
||||||
const renderPdfIcon = usePDFDownload("assignments");
|
const renderPdfIcon = usePDFDownload("assignments");
|
||||||
const renderArchiveIcon = useAssignmentArchive(id, reload);
|
const renderArchiveIcon = useAssignmentArchive(id, reload);
|
||||||
const renderUnarchiveIcon = useAssignmentUnarchive(id, reload);
|
const renderUnarchiveIcon = useAssignmentUnarchive(id, reload);
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ export default function CorporateDashboard({user}: Props) {
|
|||||||
<h2 className="text-2xl font-semibold">Active Assignments ({assignments.filter(activeFilter).length})</h2>
|
<h2 className="text-2xl font-semibold">Active Assignments ({assignments.filter(activeFilter).length})</h2>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{assignments.filter(activeFilter).map((a) => (
|
{assignments.filter(activeFilter).map((a) => (
|
||||||
<AssignmentCard {...a} onClick={() => setSelectedAssignment(a)} key={a.id} />
|
<AssignmentCard {...a} users={users} onClick={() => setSelectedAssignment(a)} key={a.id} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -355,6 +355,7 @@ export default function CorporateDashboard({user}: Props) {
|
|||||||
{assignments.filter(futureFilter).map((a) => (
|
{assignments.filter(futureFilter).map((a) => (
|
||||||
<AssignmentCard
|
<AssignmentCard
|
||||||
{...a}
|
{...a}
|
||||||
|
users={users}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedAssignment(a);
|
setSelectedAssignment(a);
|
||||||
setIsCreatingAssignment(true);
|
setIsCreatingAssignment(true);
|
||||||
@@ -370,6 +371,7 @@ export default function CorporateDashboard({user}: Props) {
|
|||||||
{assignments.filter(pastFilter).map((a) => (
|
{assignments.filter(pastFilter).map((a) => (
|
||||||
<AssignmentCard
|
<AssignmentCard
|
||||||
{...a}
|
{...a}
|
||||||
|
users={users}
|
||||||
onClick={() => setSelectedAssignment(a)}
|
onClick={() => setSelectedAssignment(a)}
|
||||||
key={a.id}
|
key={a.id}
|
||||||
allowDownload
|
allowDownload
|
||||||
@@ -385,6 +387,7 @@ export default function CorporateDashboard({user}: Props) {
|
|||||||
{assignments.filter(archivedFilter).map((a) => (
|
{assignments.filter(archivedFilter).map((a) => (
|
||||||
<AssignmentCard
|
<AssignmentCard
|
||||||
{...a}
|
{...a}
|
||||||
|
users={users}
|
||||||
onClick={() => setSelectedAssignment(a)}
|
onClick={() => setSelectedAssignment(a)}
|
||||||
key={a.id}
|
key={a.id}
|
||||||
allowDownload
|
allowDownload
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import List from "@/components/List";
|
|||||||
import {getUserCorporate} from "@/utils/groups";
|
import {getUserCorporate} from "@/utils/groups";
|
||||||
import {getCorporateUser, getUserCompanyName} from "@/resources/user";
|
import {getCorporateUser, getUserCompanyName} from "@/resources/user";
|
||||||
import Checkbox from "@/components/Low/Checkbox";
|
import Checkbox from "@/components/Low/Checkbox";
|
||||||
import {uniq, uniqBy} from "lodash";
|
import {groupBy, uniq, uniqBy} from "lodash";
|
||||||
import Select from "@/components/Low/Select";
|
import Select from "@/components/Low/Select";
|
||||||
import {Menu, MenuButton, MenuItem, MenuItems} from "@headlessui/react";
|
import {Menu, MenuButton, MenuItem, MenuItems} from "@headlessui/react";
|
||||||
import {Popover, PopoverContent, PopoverTrigger} from "@/components/ui/popover";
|
import {Popover, PopoverContent, PopoverTrigger} from "@/components/ui/popover";
|
||||||
@@ -55,6 +55,12 @@ interface Props {
|
|||||||
user: MasterCorporateUser;
|
user: MasterCorporateUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeFilter = (a: Assignment) =>
|
||||||
|
moment(a.endDate).isAfter(moment()) && moment(a.startDate).isBefore(moment()) && a.assignees.length > a.results.length;
|
||||||
|
const pastFilter = (a: Assignment) => (moment(a.endDate).isBefore(moment()) || a.assignees.length === a.results.length) && !a.archived;
|
||||||
|
const archivedFilter = (a: Assignment) => a.archived;
|
||||||
|
const futureFilter = (a: Assignment) => moment(a.startDate).isAfter(moment());
|
||||||
|
|
||||||
type StudentPerformanceItem = User & {corporate?: CorporateUser; group?: Group};
|
type StudentPerformanceItem = User & {corporate?: CorporateUser; group?: Group};
|
||||||
const StudentPerformanceList = ({items, stats, users, groups}: {items: StudentPerformanceItem[]; stats: Stat[]; users: User[]; groups: Group[]}) => {
|
const StudentPerformanceList = ({items, stats, users, groups}: {items: StudentPerformanceItem[]; stats: Stat[]; users: User[]; groups: Group[]}) => {
|
||||||
const [isShowingAmount, setIsShowingAmount] = useState(false);
|
const [isShowingAmount, setIsShowingAmount] = useState(false);
|
||||||
@@ -294,6 +300,7 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
const [selectedAssignment, setSelectedAssignment] = useState<Assignment>();
|
const [selectedAssignment, setSelectedAssignment] = useState<Assignment>();
|
||||||
const [isCreatingAssignment, setIsCreatingAssignment] = useState(false);
|
const [isCreatingAssignment, setIsCreatingAssignment] = useState(false);
|
||||||
|
const [corporateAssignments, setCorporateAssignments] = useState<(Assignment & {corporate?: CorporateUser})[]>([]);
|
||||||
|
|
||||||
const {stats} = useStats();
|
const {stats} = useStats();
|
||||||
const {users, reload} = useUsers();
|
const {users, reload} = useUsers();
|
||||||
@@ -312,6 +319,17 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
setShowModal(!!selectedUser && page === "");
|
setShowModal(!!selectedUser && page === "");
|
||||||
}, [selectedUser, page]);
|
}, [selectedUser, page]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCorporateAssignments(
|
||||||
|
assignments.filter(activeFilter).map((a) => ({
|
||||||
|
...a,
|
||||||
|
corporate: !!users.find((x) => x.id === a.assigner)
|
||||||
|
? getCorporateUser(users.find((x) => x.id === a.assigner)!, users, groups)
|
||||||
|
: undefined,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}, [assignments, groups, users]);
|
||||||
|
|
||||||
const studentFilter = (user: User) => user.type === "student" && corporateUserGroups.includes(user.id);
|
const studentFilter = (user: User) => user.type === "student" && corporateUserGroups.includes(user.id);
|
||||||
const teacherFilter = (user: User) => user.type === "teacher" && corporateUserGroups.includes(user.id);
|
const teacherFilter = (user: User) => user.type === "teacher" && corporateUserGroups.includes(user.id);
|
||||||
|
|
||||||
@@ -447,12 +465,6 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const AssignmentsPage = () => {
|
const AssignmentsPage = () => {
|
||||||
const activeFilter = (a: Assignment) =>
|
|
||||||
moment(a.endDate).isAfter(moment()) && moment(a.startDate).isBefore(moment()) && a.assignees.length > a.results.length;
|
|
||||||
const pastFilter = (a: Assignment) => (moment(a.endDate).isBefore(moment()) || a.assignees.length === a.results.length) && !a.archived;
|
|
||||||
const archivedFilter = (a: Assignment) => a.archived;
|
|
||||||
const futureFilter = (a: Assignment) => moment(a.startDate).isAfter(moment());
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AssignmentView
|
<AssignmentView
|
||||||
@@ -499,11 +511,29 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
<BsArrowRepeat className={clsx("text-xl", isAssignmentsLoading && "animate-spin")} />
|
<BsArrowRepeat className={clsx("text-xl", isAssignmentsLoading && "animate-spin")} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<span className="text-lg font-bold">Active Assignments Status</span>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<span>
|
||||||
|
<b>Total:</b> {assignments.filter(activeFilter).reduce((acc, curr) => acc + curr.results.length, 0)}/
|
||||||
|
{assignments.filter(activeFilter).reduce((acc, curr) => curr.exams.length + acc, 0)}
|
||||||
|
</span>
|
||||||
|
{Object.keys(groupBy(corporateAssignments, (x) => x.corporate?.id)).map((x) => (
|
||||||
|
<div key={x}>
|
||||||
|
<span className="font-semibold">{getUserCompanyName(users.find((u) => u.id === x)!, users, groups)}: </span>
|
||||||
|
<span>
|
||||||
|
{groupBy(corporateAssignments, (x) => x.corporate?.id)[x].reduce((acc, curr) => curr.results.length + acc, 0)}/
|
||||||
|
{groupBy(corporateAssignments, (x) => x.corporate?.id)[x].reduce((acc, curr) => curr.exams.length + acc, 0)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<section className="flex flex-col gap-4">
|
<section className="flex flex-col gap-4">
|
||||||
<h2 className="text-2xl font-semibold">Active Assignments ({assignments.filter(activeFilter).length})</h2>
|
<h2 className="text-2xl font-semibold">Active Assignments ({assignments.filter(activeFilter).length})</h2>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{assignments.filter(activeFilter).map((a) => (
|
{assignments.filter(activeFilter).map((a) => (
|
||||||
<AssignmentCard {...a} onClick={() => setSelectedAssignment(a)} key={a.id} />
|
<AssignmentCard {...a} users={users} onClick={() => setSelectedAssignment(a)} key={a.id} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -519,6 +549,7 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
{assignments.filter(futureFilter).map((a) => (
|
{assignments.filter(futureFilter).map((a) => (
|
||||||
<AssignmentCard
|
<AssignmentCard
|
||||||
{...a}
|
{...a}
|
||||||
|
users={users}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedAssignment(a);
|
setSelectedAssignment(a);
|
||||||
setIsCreatingAssignment(true);
|
setIsCreatingAssignment(true);
|
||||||
@@ -534,6 +565,7 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
{assignments.filter(pastFilter).map((a) => (
|
{assignments.filter(pastFilter).map((a) => (
|
||||||
<AssignmentCard
|
<AssignmentCard
|
||||||
{...a}
|
{...a}
|
||||||
|
users={users}
|
||||||
onClick={() => setSelectedAssignment(a)}
|
onClick={() => setSelectedAssignment(a)}
|
||||||
key={a.id}
|
key={a.id}
|
||||||
allowDownload
|
allowDownload
|
||||||
@@ -549,6 +581,7 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
{assignments.filter(archivedFilter).map((a) => (
|
{assignments.filter(archivedFilter).map((a) => (
|
||||||
<AssignmentCard
|
<AssignmentCard
|
||||||
{...a}
|
{...a}
|
||||||
|
users={users}
|
||||||
onClick={() => setSelectedAssignment(a)}
|
onClick={() => setSelectedAssignment(a)}
|
||||||
key={a.id}
|
key={a.id}
|
||||||
allowDownload
|
allowDownload
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default function TeacherDashboard({user}: Props) {
|
|||||||
|
|
||||||
const {stats} = useStats();
|
const {stats} = useStats();
|
||||||
const {users, reload} = useUsers();
|
const {users, reload} = useUsers();
|
||||||
const {groups} = useGroups({admin: user.id});
|
const {groups} = useGroups({adminAdmins: user.id});
|
||||||
const {permissions} = usePermissions(user.id);
|
const {permissions} = usePermissions(user.id);
|
||||||
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({assigner: user.id});
|
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({assigner: user.id});
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ export default function TeacherDashboard({user}: Props) {
|
|||||||
? groups
|
? groups
|
||||||
.filter((g) => g.admin === selectedUser.id)
|
.filter((g) => g.admin === selectedUser.id)
|
||||||
.flatMap((g) => g.participants)
|
.flatMap((g) => g.participants)
|
||||||
.includes(x.id) || false
|
.includes(x.id)
|
||||||
: groups.flatMap((g) => g.participants).includes(x.id)),
|
: groups.flatMap((g) => g.participants).includes(x.id)),
|
||||||
)}
|
)}
|
||||||
assigner={user.id}
|
assigner={user.id}
|
||||||
@@ -222,7 +222,7 @@ export default function TeacherDashboard({user}: Props) {
|
|||||||
<h2 className="text-2xl font-semibold">Active Assignments ({assignments.filter(activeFilter).length})</h2>
|
<h2 className="text-2xl font-semibold">Active Assignments ({assignments.filter(activeFilter).length})</h2>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{assignments.filter(activeFilter).map((a) => (
|
{assignments.filter(activeFilter).map((a) => (
|
||||||
<AssignmentCard {...a} onClick={() => setSelectedAssignment(a)} key={a.id} />
|
<AssignmentCard {...a} users={users} onClick={() => setSelectedAssignment(a)} key={a.id} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -238,6 +238,7 @@ export default function TeacherDashboard({user}: Props) {
|
|||||||
{assignments.filter(futureFilter).map((a) => (
|
{assignments.filter(futureFilter).map((a) => (
|
||||||
<AssignmentCard
|
<AssignmentCard
|
||||||
{...a}
|
{...a}
|
||||||
|
users={users}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedAssignment(a);
|
setSelectedAssignment(a);
|
||||||
setIsCreatingAssignment(true);
|
setIsCreatingAssignment(true);
|
||||||
@@ -253,6 +254,7 @@ export default function TeacherDashboard({user}: Props) {
|
|||||||
{assignments.filter(pastFilter).map((a) => (
|
{assignments.filter(pastFilter).map((a) => (
|
||||||
<AssignmentCard
|
<AssignmentCard
|
||||||
{...a}
|
{...a}
|
||||||
|
users={users}
|
||||||
onClick={() => setSelectedAssignment(a)}
|
onClick={() => setSelectedAssignment(a)}
|
||||||
key={a.id}
|
key={a.id}
|
||||||
allowDownload
|
allowDownload
|
||||||
@@ -268,6 +270,7 @@ export default function TeacherDashboard({user}: Props) {
|
|||||||
{assignments.filter(archivedFilter).map((a) => (
|
{assignments.filter(archivedFilter).map((a) => (
|
||||||
<AssignmentCard
|
<AssignmentCard
|
||||||
{...a}
|
{...a}
|
||||||
|
users={users}
|
||||||
onClick={() => setSelectedAssignment(a)}
|
onClick={() => setSelectedAssignment(a)}
|
||||||
key={a.id}
|
key={a.id}
|
||||||
allowDownload
|
allowDownload
|
||||||
|
|||||||
@@ -36,5 +36,5 @@ async function GET(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
const assigners = await getAllAssignersByCorporate(id);
|
const assigners = await getAllAssignersByCorporate(id);
|
||||||
const assignments = await getAssignmentsByAssigners([...assigners, id]);
|
const assignments = await getAssignmentsByAssigners([...assigners, id]);
|
||||||
|
|
||||||
res.status(200).json(assignments);
|
res.status(200).json(uniqBy(assignments, "id"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -979,11 +979,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
glob "7.1.7"
|
glob "7.1.7"
|
||||||
|
|
||||||
"@next/font@13.1.6":
|
|
||||||
version "13.1.6"
|
|
||||||
resolved "https://registry.npmjs.org/@next/font/-/font-13.1.6.tgz"
|
|
||||||
integrity sha512-AITjmeb1RgX1HKMCiA39ztx2mxeAyxl4ljv2UoSBUGAbFFMg8MO7YAvjHCgFhD39hL7YTbFjol04e/BPBH5RzQ==
|
|
||||||
|
|
||||||
"@next/swc-darwin-arm64@14.2.5":
|
"@next/swc-darwin-arm64@14.2.5":
|
||||||
version "14.2.5"
|
version "14.2.5"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz#d0a160cf78c18731c51cc0bff131c706b3e9bb05"
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz#d0a160cf78c18731c51cc0bff131c706b3e9bb05"
|
||||||
|
|||||||
Reference in New Issue
Block a user