308 lines
8.3 KiB
TypeScript
308 lines
8.3 KiB
TypeScript
import clsx from "clsx";
|
|
import { IconType } from "react-icons";
|
|
import { MdSpaceDashboard } from "react-icons/md";
|
|
import {
|
|
BsFileEarmarkText,
|
|
BsClockHistory,
|
|
BsPencil,
|
|
BsGraphUp,
|
|
BsChevronBarRight,
|
|
BsChevronBarLeft,
|
|
BsShieldFill,
|
|
BsCloudFill,
|
|
BsCurrencyDollar,
|
|
BsClipboardData,
|
|
} from "react-icons/bs";
|
|
import { RiLogoutBoxFill } from "react-icons/ri";
|
|
import { SlPencil } from "react-icons/sl";
|
|
import { FaAward } from "react-icons/fa";
|
|
import Link from "next/link";
|
|
import { useRouter } from "next/router";
|
|
import axios from "axios";
|
|
import FocusLayer from "@/components/FocusLayer";
|
|
import { preventNavigation } from "@/utils/navigation.disabled";
|
|
import { useState } from "react";
|
|
import usePreferencesStore from "@/stores/preferencesStore";
|
|
import { Type } from "@/interfaces/user";
|
|
import useTicketsListener from '@/hooks/useTicketsListener';
|
|
interface Props {
|
|
path: string;
|
|
navDisabled?: boolean;
|
|
focusMode?: boolean;
|
|
onFocusLayerMouseEnter?: () => void;
|
|
className?: string;
|
|
userType?: Type;
|
|
userId?: string;
|
|
}
|
|
|
|
interface NavProps {
|
|
Icon: IconType;
|
|
label: string;
|
|
path: string;
|
|
keyPath: string;
|
|
disabled?: boolean;
|
|
isMinimized?: boolean;
|
|
badge?: number;
|
|
}
|
|
|
|
const Nav = ({
|
|
Icon,
|
|
label,
|
|
path,
|
|
keyPath,
|
|
disabled = false,
|
|
isMinimized = false,
|
|
badge,
|
|
}: NavProps) => {
|
|
const displayBadge = badge && badge > 0 ? true : false;
|
|
return (
|
|
<Link
|
|
href={!disabled ? keyPath : ""}
|
|
className={clsx(
|
|
"flex items-center gap-4 rounded-full p-4 text-gray-500 hover:text-white",
|
|
"transition-all duration-300 ease-in-out",
|
|
disabled
|
|
? "hover:bg-mti-gray-dim cursor-not-allowed"
|
|
: "hover:bg-mti-purple-light cursor-pointer",
|
|
path === keyPath && "bg-mti-purple-light text-white",
|
|
isMinimized ? "w-fit" : "w-full min-w-[200px] px-8 2xl:min-w-[220px]",
|
|
)}
|
|
>
|
|
<Icon size={24} />
|
|
{!isMinimized && <span className="text-lg font-semibold">{label}</span>}
|
|
{displayBadge && <div className="badge badge-primary badge-sm">{badge}</div>}
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
export default function Sidebar({
|
|
path,
|
|
navDisabled = false,
|
|
focusMode = false,
|
|
userType,
|
|
onFocusLayerMouseEnter,
|
|
className,
|
|
userId,
|
|
}: Props) {
|
|
const router = useRouter();
|
|
|
|
const [isMinimized, toggleMinimize] = usePreferencesStore((state) => [
|
|
state.isSidebarMinimized,
|
|
state.toggleSidebarMinimized,
|
|
]);
|
|
|
|
const {totalAssignedTickets } = useTicketsListener(userId);
|
|
|
|
const logout = async () => {
|
|
axios.post("/api/logout").finally(() => {
|
|
setTimeout(() => router.reload(), 500);
|
|
});
|
|
};
|
|
|
|
const disableNavigation = preventNavigation(navDisabled, focusMode);
|
|
|
|
return (
|
|
<section
|
|
className={clsx(
|
|
"relative flex h-full flex-col justify-between bg-transparent px-4 py-4 pb-8",
|
|
isMinimized ? "w-fit" : "-xl:w-fit w-1/6",
|
|
className,
|
|
)}
|
|
>
|
|
<div className="-xl:hidden flex-col gap-3 xl:flex">
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={MdSpaceDashboard}
|
|
label="Dashboard"
|
|
path={path}
|
|
keyPath="/"
|
|
isMinimized={isMinimized}
|
|
/>
|
|
{(userType === "student" ||
|
|
userType === "teacher" ||
|
|
userType === "developer") && (
|
|
<>
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsFileEarmarkText}
|
|
label="Exams"
|
|
path={path}
|
|
keyPath="/exam"
|
|
isMinimized={isMinimized}
|
|
/>
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsPencil}
|
|
label="Exercises"
|
|
path={path}
|
|
keyPath="/exercises"
|
|
isMinimized={isMinimized}
|
|
/>
|
|
</>
|
|
)}
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsGraphUp}
|
|
label="Stats"
|
|
path={path}
|
|
keyPath="/stats"
|
|
isMinimized={isMinimized}
|
|
/>
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsClockHistory}
|
|
label="Record"
|
|
path={path}
|
|
keyPath="/record"
|
|
isMinimized={isMinimized}
|
|
/>
|
|
{["admin", "developer", "agent", "corporate"].includes(
|
|
userType || "",
|
|
) && (
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsCurrencyDollar}
|
|
label="Payment Record"
|
|
path={path}
|
|
keyPath="/payment-record"
|
|
isMinimized={isMinimized}
|
|
/>
|
|
)}
|
|
{["admin", "developer", "corporate", "teacher"].includes(
|
|
userType || "",
|
|
) && (
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsShieldFill}
|
|
label="Settings"
|
|
path={path}
|
|
keyPath="/settings"
|
|
isMinimized={isMinimized}
|
|
/>
|
|
)}
|
|
{["admin", "developer", "agent"].includes(userType || "") && (
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsClipboardData}
|
|
label="Tickets"
|
|
path={path}
|
|
keyPath="/tickets"
|
|
isMinimized={isMinimized}
|
|
badge={totalAssignedTickets}
|
|
/>
|
|
)}
|
|
{userType === "developer" && (
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsCloudFill}
|
|
label="Generation"
|
|
path={path}
|
|
keyPath="/generation"
|
|
isMinimized={isMinimized}
|
|
/>
|
|
)}
|
|
</div>
|
|
<div className="-xl:flex flex-col gap-3 xl:hidden">
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={MdSpaceDashboard}
|
|
label="Dashboard"
|
|
path={path}
|
|
keyPath="/"
|
|
isMinimized={true}
|
|
/>
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsFileEarmarkText}
|
|
label="Exams"
|
|
path={path}
|
|
keyPath="/exam"
|
|
isMinimized={true}
|
|
/>
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsPencil}
|
|
label="Exercises"
|
|
path={path}
|
|
keyPath="/exercises"
|
|
isMinimized={true}
|
|
/>
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsGraphUp}
|
|
label="Stats"
|
|
path={path}
|
|
keyPath="/stats"
|
|
isMinimized={true}
|
|
/>
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsClockHistory}
|
|
label="Record"
|
|
path={path}
|
|
keyPath="/record"
|
|
isMinimized={true}
|
|
/>
|
|
{userType !== "student" && (
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsShieldFill}
|
|
label="Settings"
|
|
path={path}
|
|
keyPath="/settings"
|
|
isMinimized={true}
|
|
/>
|
|
)}
|
|
{userType === "developer" && (
|
|
<Nav
|
|
disabled={disableNavigation}
|
|
Icon={BsCloudFill}
|
|
label="Generation"
|
|
path={path}
|
|
keyPath="/generation"
|
|
isMinimized={true}
|
|
/>
|
|
)}
|
|
</div>
|
|
|
|
<div className="fixed bottom-12 flex flex-col gap-0">
|
|
<div
|
|
role="button"
|
|
tabIndex={1}
|
|
onClick={toggleMinimize}
|
|
className={clsx(
|
|
"hover:text-mti-rose -xl:hidden flex cursor-pointer items-center gap-4 rounded-full p-4 text-black transition duration-300 ease-in-out",
|
|
isMinimized ? "w-fit" : "w-full min-w-[250px] px-8",
|
|
)}
|
|
>
|
|
{isMinimized ? (
|
|
<BsChevronBarRight size={24} />
|
|
) : (
|
|
<BsChevronBarLeft size={24} />
|
|
)}
|
|
{!isMinimized && (
|
|
<span className="text-lg font-medium">Minimize</span>
|
|
)}
|
|
</div>
|
|
<div
|
|
role="button"
|
|
tabIndex={1}
|
|
onClick={focusMode ? () => {} : logout}
|
|
className={clsx(
|
|
"hover:text-mti-rose flex cursor-pointer items-center gap-4 rounded-full p-4 text-black transition duration-300 ease-in-out",
|
|
isMinimized ? "w-fit" : "w-full min-w-[250px] px-8",
|
|
)}
|
|
>
|
|
<RiLogoutBoxFill size={24} />
|
|
{!isMinimized && (
|
|
<span className="-xl:hidden text-lg font-medium">Log Out</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
{focusMode && (
|
|
<FocusLayer onFocusLayerMouseEnter={onFocusLayerMouseEnter} />
|
|
)}
|
|
</section>
|
|
);
|
|
}
|