ENCOA-249

This commit is contained in:
Tiago Ribeiro
2024-12-02 10:00:38 +00:00
parent 1f1c2b4aaf
commit 24d613e9cd

View File

@@ -2,17 +2,17 @@ import clsx from "clsx";
import { IconType } from "react-icons"; import { IconType } from "react-icons";
import { MdSpaceDashboard } from "react-icons/md"; import { MdSpaceDashboard } from "react-icons/md";
import { import {
BsFileEarmarkText, BsFileEarmarkText,
BsClockHistory, BsClockHistory,
BsPencil, BsPencil,
BsGraphUp, BsGraphUp,
BsChevronBarRight, BsChevronBarRight,
BsChevronBarLeft, BsChevronBarLeft,
BsShieldFill, BsShieldFill,
BsCloudFill, BsCloudFill,
BsCurrencyDollar, BsCurrencyDollar,
BsClipboardData, BsClipboardData,
BsPeople, BsPeople,
} from "react-icons/bs"; } from "react-icons/bs";
import { CiDumbbell } from "react-icons/ci"; import { CiDumbbell } from "react-icons/ci";
import { RiLogoutBoxFill } from "react-icons/ri"; import { RiLogoutBoxFill } from "react-icons/ri";
@@ -31,50 +31,50 @@ import { useAllowedEntitiesSomePermissions } from "@/hooks/useEntityPermissions"
import { useMemo } from "react"; import { useMemo } from "react";
interface Props { interface Props {
path: string; path: string;
navDisabled?: boolean; navDisabled?: boolean;
focusMode?: boolean; focusMode?: boolean;
onFocusLayerMouseEnter?: () => void; onFocusLayerMouseEnter?: () => void;
className?: string; className?: string;
user: User; user: User;
entities?: EntityWithRoles[] entities?: EntityWithRoles[]
} }
interface NavProps { interface NavProps {
Icon: IconType; Icon: IconType;
label: string; label: string;
path: string; path: string;
keyPath: string; keyPath: string;
disabled?: boolean; disabled?: boolean;
isMinimized?: boolean; isMinimized?: boolean;
badge?: number; badge?: number;
} }
const Nav = ({ Icon, label, path, keyPath, disabled = false, isMinimized = false, badge }: NavProps) => { const Nav = ({ Icon, label, path, keyPath, disabled = false, isMinimized = false, badge }: NavProps) => {
return ( return (
<Link <Link
href={!disabled ? keyPath : ""} href={!disabled ? keyPath : ""}
className={clsx( className={clsx(
"flex items-center gap-4 rounded-full p-4 text-gray-500 hover:text-white", "flex items-center gap-4 rounded-full p-4 text-gray-500 hover:text-white",
"transition-all duration-300 ease-in-out relative", "transition-all duration-300 ease-in-out relative",
disabled ? "hover:bg-mti-gray-dim cursor-not-allowed" : "hover:bg-mti-purple-light cursor-pointer", disabled ? "hover:bg-mti-gray-dim cursor-not-allowed" : "hover:bg-mti-purple-light cursor-pointer",
path.startsWith(keyPath) && "bg-mti-purple-light text-white", path.startsWith(keyPath) && "bg-mti-purple-light text-white",
isMinimized ? "w-fit" : "w-full min-w-[200px] px-8 2xl:min-w-[220px]", isMinimized ? "w-fit" : "w-full min-w-[200px] px-8 2xl:min-w-[220px]",
)}> )}>
<Icon size={24} /> <Icon size={24} />
{!isMinimized && <span className="text-lg font-semibold">{label}</span>} {!isMinimized && <span className="text-lg font-semibold">{label}</span>}
{!!badge && badge > 0 && ( {!!badge && badge > 0 && (
<div <div
className={clsx( className={clsx(
"bg-mti-purple-light h-5 w-5 text-xs rounded-full flex items-center justify-center text-white", "bg-mti-purple-light h-5 w-5 text-xs rounded-full flex items-center justify-center text-white",
"transition ease-in-out duration-300", "transition ease-in-out duration-300",
isMinimized && "absolute right-0 top-0", isMinimized && "absolute right-0 top-0",
)}> )}>
{badge} {badge}
</div> </div>
)} )}
</Link> </Link>
); );
}; };
export default function Sidebar({ export default function Sidebar({
@@ -86,152 +86,152 @@ export default function Sidebar({
onFocusLayerMouseEnter, onFocusLayerMouseEnter,
className className
}: Props) { }: Props) {
const router = useRouter(); const router = useRouter();
const isAdmin = useMemo(() => ['developer', 'admin'].includes(user?.type), [user?.type]) const isAdmin = useMemo(() => ['developer', 'admin'].includes(user?.type), [user?.type])
const [isMinimized, toggleMinimize] = usePreferencesStore((state) => [state.isSidebarMinimized, state.toggleSidebarMinimized]); const [isMinimized, toggleMinimize] = usePreferencesStore((state) => [state.isSidebarMinimized, state.toggleSidebarMinimized]);
const { totalAssignedTickets } = useTicketsListener(user.id); const { totalAssignedTickets } = useTicketsListener(user.id);
const { permissions } = usePermissions(user.id); const { permissions } = usePermissions(user.id);
const entitiesAllowGeneration = useAllowedEntitiesSomePermissions(user, entities, [ const entitiesAllowGeneration = useAllowedEntitiesSomePermissions(user, entities, [
"generate_reading", "generate_listening", "generate_writing", "generate_speaking", "generate_level" "generate_reading", "generate_listening", "generate_writing", "generate_speaking", "generate_level"
]) ])
const logout = async () => { const logout = async () => {
axios.post("/api/logout").finally(() => { axios.post("/api/logout").finally(() => {
setTimeout(() => router.reload(), 500); setTimeout(() => router.reload(), 500);
}); });
}; };
const disableNavigation = preventNavigation(navDisabled, focusMode); const disableNavigation = preventNavigation(navDisabled, focusMode);
return ( return (
<section <section
className={clsx( className={clsx(
"relative flex h-full flex-col justify-between bg-transparent px-4 py-4 pb-8", "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", isMinimized ? "w-fit" : "-xl:w-fit w-1/6",
className, className,
)}> )}>
<div className="-xl:hidden flex-col gap-3 xl:flex"> <div className="-xl:hidden flex-col gap-3 xl:flex">
<Nav disabled={disableNavigation} Icon={MdSpaceDashboard} label="Dashboard" path={path} keyPath="/dashboard" isMinimized={isMinimized} /> <Nav disabled={disableNavigation} Icon={MdSpaceDashboard} label="Dashboard" path={path} keyPath="/dashboard" isMinimized={isMinimized} />
{checkAccess(user, ["student", "teacher", "developer"], permissions, "viewExams") && ( {checkAccess(user, ["student", "teacher", "developer"], permissions, "viewExams") && (
<Nav disabled={disableNavigation} Icon={BsFileEarmarkText} label="Exams" path={path} keyPath="/exam" isMinimized={isMinimized} /> <Nav disabled={disableNavigation} Icon={BsFileEarmarkText} label="Practice" path={path} keyPath="/exam" isMinimized={isMinimized} />
)} )}
{checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewStats") && ( {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewStats") && (
<Nav disabled={disableNavigation} Icon={BsGraphUp} label="Stats" path={path} keyPath="/stats" isMinimized={isMinimized} /> <Nav disabled={disableNavigation} Icon={BsGraphUp} label="Stats" path={path} keyPath="/stats" isMinimized={isMinimized} />
)} )}
{checkAccess(user, ["developer", "admin", "mastercorporate", "corporate", "teacher", "student"], permissions) && ( {checkAccess(user, ["developer", "admin", "mastercorporate", "corporate", "teacher", "student"], permissions) && (
<Nav <Nav
disabled={disableNavigation} disabled={disableNavigation}
Icon={BsPeople} Icon={BsPeople}
label="Classrooms" label="Classrooms"
path={path} path={path}
keyPath="/classrooms" keyPath="/classrooms"
isMinimized={isMinimized} isMinimized={isMinimized}
/> />
)} )}
{checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && ( {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
<Nav disabled={disableNavigation} Icon={BsClockHistory} label="Record" path={path} keyPath="/record" isMinimized={isMinimized} /> <Nav disabled={disableNavigation} Icon={BsClockHistory} label="Record" path={path} keyPath="/record" isMinimized={isMinimized} />
)} )}
{checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && ( {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
<Nav disabled={disableNavigation} Icon={CiDumbbell} label="Training" path={path} keyPath="/training" isMinimized={isMinimized} /> <Nav disabled={disableNavigation} Icon={CiDumbbell} label="Training" path={path} keyPath="/training" isMinimized={isMinimized} />
)} )}
{checkAccess(user, ["admin", "developer", "agent", "corporate", "mastercorporate"], permissions, "viewPaymentRecords") && ( {checkAccess(user, ["admin", "developer", "agent", "corporate", "mastercorporate"], permissions, "viewPaymentRecords") && (
<Nav <Nav
disabled={disableNavigation} disabled={disableNavigation}
Icon={BsCurrencyDollar} Icon={BsCurrencyDollar}
label="Payment Record" label="Payment Record"
path={path} path={path}
keyPath="/payment-record" keyPath="/payment-record"
isMinimized={isMinimized} isMinimized={isMinimized}
/> />
)} )}
{checkAccess(user, ["admin", "developer", "corporate", "teacher", "mastercorporate"]) && ( {checkAccess(user, ["admin", "developer", "corporate", "teacher", "mastercorporate"]) && (
<Nav <Nav
disabled={disableNavigation} disabled={disableNavigation}
Icon={BsShieldFill} Icon={BsShieldFill}
label="Settings" label="Settings"
path={path} path={path}
keyPath="/settings" keyPath="/settings"
isMinimized={isMinimized} isMinimized={isMinimized}
/> />
)} )}
{checkAccess(user, ["admin", "developer", "agent"], permissions, "viewTickets") && ( {checkAccess(user, ["admin", "developer", "agent"], permissions, "viewTickets") && (
<Nav <Nav
disabled={disableNavigation} disabled={disableNavigation}
Icon={BsClipboardData} Icon={BsClipboardData}
label="Tickets" label="Tickets"
path={path} path={path}
keyPath="/tickets" keyPath="/tickets"
isMinimized={isMinimized} isMinimized={isMinimized}
badge={totalAssignedTickets} badge={totalAssignedTickets}
/> />
)} )}
{(entitiesAllowGeneration.length > 0 || isAdmin) && ( {(entitiesAllowGeneration.length > 0 || isAdmin) && (
<Nav <Nav
disabled={disableNavigation} disabled={disableNavigation}
Icon={BsCloudFill} Icon={BsCloudFill}
label="Generation" label="Generation"
path={path} path={path}
keyPath="/generation" keyPath="/generation"
isMinimized={isMinimized} isMinimized={isMinimized}
/> />
)} )}
</div> </div>
<div className="-xl:flex flex-col gap-3 xl:hidden"> <div className="-xl:flex flex-col gap-3 xl:hidden">
<Nav disabled={disableNavigation} Icon={MdSpaceDashboard} label="Dashboard" path={path} keyPath="/" isMinimized /> <Nav disabled={disableNavigation} Icon={MdSpaceDashboard} label="Dashboard" path={path} keyPath="/" isMinimized />
<Nav disabled={disableNavigation} Icon={BsFileEarmarkText} label="Exams" path={path} keyPath="/exam" isMinimized /> <Nav disabled={disableNavigation} Icon={BsFileEarmarkText} label="Exams" path={path} keyPath="/exam" isMinimized />
{checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewStats") && ( {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewStats") && (
<Nav disabled={disableNavigation} Icon={BsGraphUp} label="Stats" path={path} keyPath="/stats" isMinimized /> <Nav disabled={disableNavigation} Icon={BsGraphUp} label="Stats" path={path} keyPath="/stats" isMinimized />
)} )}
{checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && ( {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
<Nav disabled={disableNavigation} Icon={BsClockHistory} label="Record" path={path} keyPath="/record" isMinimized /> <Nav disabled={disableNavigation} Icon={BsClockHistory} label="Record" path={path} keyPath="/record" isMinimized />
)} )}
{checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && ( {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
<Nav disabled={disableNavigation} Icon={CiDumbbell} label="Training" path={path} keyPath="/training" isMinimized /> <Nav disabled={disableNavigation} Icon={CiDumbbell} label="Training" path={path} keyPath="/training" isMinimized />
)} )}
{checkAccess(user, getTypesOfUser(["student"])) && ( {checkAccess(user, getTypesOfUser(["student"])) && (
<Nav disabled={disableNavigation} Icon={BsShieldFill} label="Settings" path={path} keyPath="/settings" isMinimized /> <Nav disabled={disableNavigation} Icon={BsShieldFill} label="Settings" path={path} keyPath="/settings" isMinimized />
)} )}
{entitiesAllowGeneration.length > 0 && ( {entitiesAllowGeneration.length > 0 && (
<Nav <Nav
disabled={disableNavigation} disabled={disableNavigation}
Icon={BsCloudFill} Icon={BsCloudFill}
label="Generation" label="Generation"
path={path} path={path}
keyPath="/generation" keyPath="/generation"
isMinimized isMinimized
/> />
)} )}
</div> </div>
<div className="2xl:fixed bottom-12 flex flex-col gap-0 -2xl:mt-8"> <div className="2xl:fixed bottom-12 flex flex-col gap-0 -2xl:mt-8">
<div <div
role="button" role="button"
tabIndex={1} tabIndex={1}
onClick={toggleMinimize} onClick={toggleMinimize}
className={clsx( 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", "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 ? "w-fit" : "w-full min-w-[250px] px-8",
)}> )}>
{isMinimized ? <BsChevronBarRight size={24} /> : <BsChevronBarLeft size={24} />} {isMinimized ? <BsChevronBarRight size={24} /> : <BsChevronBarLeft size={24} />}
{!isMinimized && <span className="text-lg font-medium">Minimize</span>} {!isMinimized && <span className="text-lg font-medium">Minimize</span>}
</div> </div>
<div <div
role="button" role="button"
tabIndex={1} tabIndex={1}
onClick={focusMode ? () => { } : logout} onClick={focusMode ? () => { } : logout}
className={clsx( 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", "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", isMinimized ? "w-fit" : "w-full min-w-[250px] px-8",
)}> )}>
<RiLogoutBoxFill size={24} /> <RiLogoutBoxFill size={24} />
{!isMinimized && <span className="-xl:hidden text-lg font-medium">Log Out</span>} {!isMinimized && <span className="-xl:hidden text-lg font-medium">Log Out</span>}
</div> </div>
</div> </div>
{focusMode && <FocusLayer onFocusLayerMouseEnter={onFocusLayerMouseEnter} />} {focusMode && <FocusLayer onFocusLayerMouseEnter={onFocusLayerMouseEnter} />}
</section> </section>
); );
} }