Added a new card for the Corporate to show their user balance
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
import useStats from "@/hooks/useStats";
|
import useStats from "@/hooks/useStats";
|
||||||
import useUsers from "@/hooks/useUsers";
|
import useUsers from "@/hooks/useUsers";
|
||||||
import {Group, Stat, User} from "@/interfaces/user";
|
import {CorporateUser, Group, Stat, User} from "@/interfaces/user";
|
||||||
import UserList from "@/pages/(admin)/Lists/UserList";
|
import UserList from "@/pages/(admin)/Lists/UserList";
|
||||||
import {dateSorter} from "@/utils";
|
import {dateSorter} from "@/utils";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
@@ -20,6 +20,9 @@ import {
|
|||||||
BsPersonFillGear,
|
BsPersonFillGear,
|
||||||
BsPersonGear,
|
BsPersonGear,
|
||||||
BsPencilSquare,
|
BsPencilSquare,
|
||||||
|
BsPersonBadge,
|
||||||
|
BsPersonCheck,
|
||||||
|
BsPeople,
|
||||||
} from "react-icons/bs";
|
} from "react-icons/bs";
|
||||||
import UserCard from "@/components/UserCard";
|
import UserCard from "@/components/UserCard";
|
||||||
import useGroups from "@/hooks/useGroups";
|
import useGroups from "@/hooks/useGroups";
|
||||||
@@ -31,9 +34,10 @@ import IconCard from "./IconCard";
|
|||||||
import GroupList from "@/pages/(admin)/Lists/GroupList";
|
import GroupList from "@/pages/(admin)/Lists/GroupList";
|
||||||
import useFilterStore from "@/stores/listFilterStore";
|
import useFilterStore from "@/stores/listFilterStore";
|
||||||
import {useRouter} from "next/router";
|
import {useRouter} from "next/router";
|
||||||
|
import useCodes from "@/hooks/useCodes";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: User;
|
user: CorporateUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CorporateDashboard({user}: Props) {
|
export default function CorporateDashboard({user}: Props) {
|
||||||
@@ -43,6 +47,7 @@ export default function CorporateDashboard({user}: Props) {
|
|||||||
|
|
||||||
const {stats} = useStats();
|
const {stats} = useStats();
|
||||||
const {users, reload} = useUsers();
|
const {users, reload} = useUsers();
|
||||||
|
const {codes} = useCodes(user.id);
|
||||||
const {groups} = useGroups(user.id);
|
const {groups} = useGroups(user.id);
|
||||||
|
|
||||||
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
|
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
|
||||||
@@ -187,7 +192,13 @@ export default function CorporateDashboard({user}: Props) {
|
|||||||
value={averageLevelCalculator(stats.filter((s) => groups.flatMap((g) => g.participants).includes(s.user))).toFixed(1)}
|
value={averageLevelCalculator(stats.filter((s) => groups.flatMap((g) => g.participants).includes(s.user))).toFixed(1)}
|
||||||
color="purple"
|
color="purple"
|
||||||
/>
|
/>
|
||||||
<IconCard onClick={() => setPage("groups")} Icon={BsPersonAdd} label="Groups" value={groups.length} color="purple" />
|
<IconCard onClick={() => setPage("groups")} Icon={BsPeople} label="Groups" value={groups.length} color="purple" />
|
||||||
|
<IconCard
|
||||||
|
Icon={BsPersonCheck}
|
||||||
|
label="User Balance"
|
||||||
|
value={`${codes.length}/${user.corporateInformation?.companyInformation?.userAmount || 0}`}
|
||||||
|
color="purple"
|
||||||
|
/>
|
||||||
<IconCard
|
<IconCard
|
||||||
Icon={BsClock}
|
Icon={BsClock}
|
||||||
label="Expiration Date"
|
label="Expiration Date"
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ interface Props {
|
|||||||
label: string;
|
label: string;
|
||||||
value: string | number;
|
value: string | number;
|
||||||
color: "purple" | "rose" | "red";
|
color: "purple" | "rose" | "red";
|
||||||
|
tooltip?: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function IconCard({Icon, label, value, color, onClick}: Props) {
|
export default function IconCard({Icon, label, value, color, tooltip, onClick}: Props) {
|
||||||
const colorClasses: {[key in typeof color]: string} = {
|
const colorClasses: {[key in typeof color]: string} = {
|
||||||
purple: "text-mti-purple-light",
|
purple: "text-mti-purple-light",
|
||||||
red: "text-mti-red-light",
|
red: "text-mti-red-light",
|
||||||
@@ -19,7 +20,11 @@ export default function IconCard({Icon, label, value, color, onClick}: Props) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className="bg-white rounded-xl shadow p-4 flex flex-col gap-4 items-center text-center w-52 h-52 justify-center cursor-pointer hover:shadow-xl transition ease-in-out duration-300">
|
className={clsx(
|
||||||
|
"bg-white rounded-xl shadow p-4 flex flex-col gap-4 items-center text-center w-52 h-52 justify-center cursor-pointer hover:shadow-xl transition ease-in-out duration-300",
|
||||||
|
tooltip && "tooltip tooltip-bottom",
|
||||||
|
)}
|
||||||
|
data-tip={tooltip}>
|
||||||
<Icon className={clsx("text-6xl", colorClasses[color])} />
|
<Icon className={clsx("text-6xl", colorClasses[color])} />
|
||||||
<span className="flex flex-col gap-1 items-center text-xl">
|
<span className="flex flex-col gap-1 items-center text-xl">
|
||||||
<span className="text-lg">{label}</span>
|
<span className="text-lg">{label}</span>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
BsEnvelopePaper,
|
BsEnvelopePaper,
|
||||||
BsGlobeCentralSouthAsia,
|
BsGlobeCentralSouthAsia,
|
||||||
BsPaperclip,
|
BsPaperclip,
|
||||||
|
BsPeople,
|
||||||
BsPerson,
|
BsPerson,
|
||||||
BsPersonAdd,
|
BsPersonAdd,
|
||||||
BsPersonFill,
|
BsPersonFill,
|
||||||
@@ -271,7 +272,7 @@ export default function TeacherDashboard({user}: Props) {
|
|||||||
value={averageLevelCalculator(stats.filter((s) => groups.flatMap((g) => g.participants).includes(s.user))).toFixed(1)}
|
value={averageLevelCalculator(stats.filter((s) => groups.flatMap((g) => g.participants).includes(s.user))).toFixed(1)}
|
||||||
color="purple"
|
color="purple"
|
||||||
/>
|
/>
|
||||||
<IconCard Icon={BsPersonAdd} label="Groups" value={groups.length} color="purple" onClick={() => setPage("groups")} />
|
<IconCard Icon={BsPeople} label="Groups" value={groups.length} color="purple" onClick={() => setPage("groups")} />
|
||||||
<div
|
<div
|
||||||
onClick={() => setPage("assignments")}
|
onClick={() => setPage("assignments")}
|
||||||
className="bg-white rounded-xl shadow p-4 flex flex-col gap-4 items-center w-96 h-52 justify-center cursor-pointer hover:shadow-xl transition ease-in-out duration-300">
|
className="bg-white rounded-xl shadow p-4 flex flex-col gap-4 items-center w-96 h-52 justify-center cursor-pointer hover:shadow-xl transition ease-in-out duration-300">
|
||||||
|
|||||||
21
src/hooks/useCodes.tsx
Normal file
21
src/hooks/useCodes.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import {Code, Group, User} from "@/interfaces/user";
|
||||||
|
import axios from "axios";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
|
||||||
|
export default function useCodes(creator?: string) {
|
||||||
|
const [codes, setCodes] = useState<Code[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [isError, setIsError] = useState(false);
|
||||||
|
|
||||||
|
const getData = () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
axios
|
||||||
|
.get<Code[]>(`/api/code${creator ? `?creator=${creator}` : ""}`)
|
||||||
|
.then((response) => setCodes(response.data))
|
||||||
|
.finally(() => setIsLoading(false));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(getData, [creator]);
|
||||||
|
|
||||||
|
return {codes, isLoading, isError, reload: getData};
|
||||||
|
}
|
||||||
@@ -127,5 +127,16 @@ export interface Group {
|
|||||||
disableEditing?: boolean;
|
disableEditing?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Code {
|
||||||
|
code: string;
|
||||||
|
creator: string;
|
||||||
|
expiryDate: Date;
|
||||||
|
type: Type;
|
||||||
|
userId?: string;
|
||||||
|
email?: string;
|
||||||
|
name?: string;
|
||||||
|
passport_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type Type = "student" | "teacher" | "corporate" | "admin" | "developer" | "agent";
|
export type Type = "student" | "teacher" | "corporate" | "admin" | "developer" | "agent";
|
||||||
export const userTypes: Type[] = ["student", "teacher", "corporate", "admin", "developer", "agent"];
|
export const userTypes: Type[] = ["student", "teacher", "corporate", "admin", "developer", "agent"];
|
||||||
|
|||||||
@@ -14,6 +14,26 @@ const db = getFirestore(app);
|
|||||||
export default withIronSessionApiRoute(handler, sessionOptions);
|
export default withIronSessionApiRoute(handler, sessionOptions);
|
||||||
|
|
||||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
if (req.method === "GET") return get(req, res);
|
||||||
|
if (req.method === "POST") return post(req, res);
|
||||||
|
|
||||||
|
return res.status(404).json({ok: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
if (!req.session.user) {
|
||||||
|
res.status(401).json({ok: false, reason: "You must be logged in to generate a code!"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {creator} = req.query as {creator?: string};
|
||||||
|
const q = query(collection(db, "codes"), where("creator", "==", creator));
|
||||||
|
const snapshot = await getDocs(creator ? q : collection(db, "codes"));
|
||||||
|
|
||||||
|
res.status(200).json(snapshot.docs.map((doc) => doc.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
res.status(401).json({ok: false, reason: "You must be logged in to generate a code!"});
|
res.status(401).json({ok: false, reason: "You must be logged in to generate a code!"});
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user