Added new pages and nav with menu

This commit is contained in:
José Lima
2025-03-07 04:38:57 +00:00
parent df84aaadf4
commit 25aef3afdf
3 changed files with 184 additions and 26 deletions

View File

@@ -12,6 +12,10 @@ import {
BsCurrencyDollar,
BsClipboardData,
BsPeople,
BsChevronDown,
BsChevronUp,
BsChatText,
BsCardText,
} from "react-icons/bs";
import { GoWorkflow } from "react-icons/go";
import { CiDumbbell } from "react-icons/ci";
@@ -31,7 +35,7 @@ import {
useAllowedEntities,
useAllowedEntitiesSomePermissions,
} from "@/hooks/useEntityPermissions";
import { useMemo } from "react";
import { useMemo, useState } from "react";
import { PermissionType } from "../interfaces/permissions";
interface Props {
@@ -52,6 +56,7 @@ interface NavProps {
disabled?: boolean;
isMinimized?: boolean;
badge?: number;
children?: React.ReactNode;
}
const Nav = ({
@@ -62,34 +67,71 @@ const Nav = ({
disabled = false,
isMinimized = false,
badge,
children,
}: NavProps) => {
const [open, setOpen] = useState(false);
return (
<Link
href={!disabled ? keyPath : ""}
<div
className={clsx(
"flex items-center gap-4 rounded-full p-4 text-gray-500 hover:text-white",
"transition-all duration-300 ease-in-out relative",
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",
isMinimized ? "w-fit" : "w-full min-w-[200px] px-8 2xl:min-w-[220px]"
"flex flex-col gap-2 transition-all duration-300 ease-in-out",
open && !isMinimized && "bg-white rounded-xl"
)}
>
<Icon size={24} />
{!isMinimized && <span className="text-lg font-semibold">{label}</span>}
{!!badge && badge > 0 && (
<div
className={clsx(
"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",
isMinimized && "absolute right-0 top-0"
)}
>
{badge}
</div>
)}
</Link>
<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 relative",
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",
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>}
{!!badge && badge > 0 && (
<div
className={clsx(
"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",
isMinimized && "absolute right-0 top-0"
)}
>
{badge}
</div>
)}
{children && (
<button
className="flex items-center gap-4 rounded-full p-4 absolute right-0"
onClick={(e) => {
setOpen((prev) => !prev);
e.preventDefault();
}}
>
{open ? (
<BsChevronUp
size={24}
className={clsx(
isMinimized && "hidden",
"transition ease-in-out duration-300"
)}
/>
) : (
<BsChevronDown
size={24}
className={clsx(
isMinimized && "hidden",
"transition ease-in-out duration-300"
)}
/>
)}
</button>
)}
</Link>
{open || isMinimized ? children : null}
</div>
);
};
@@ -325,7 +367,24 @@ export default function Sidebar({
path={path}
keyPath="/training"
isMinimized={isMinimized}
/>
>
<Nav
disabled={disableNavigation}
Icon={BsChatText}
label="Vocabulary"
path={path}
keyPath="/training/vocabulary"
isMinimized={isMinimized}
/>
<Nav
disabled={disableNavigation}
Icon={BsCardText}
label="Grammar"
path={path}
keyPath="/training/grammar"
isMinimized={isMinimized}
/>
</Nav>
)}
{sidebarPermissions["viewPaymentRecords"] && (
<Nav
@@ -424,7 +483,24 @@ export default function Sidebar({
path={path}
keyPath="/training"
isMinimized
/>
>
<Nav
disabled={disableNavigation}
Icon={BsChatText}
label="Vocabulary"
path={path}
keyPath="/training/vocabulary"
isMinimized
/>
<Nav
disabled={disableNavigation}
Icon={BsCardText}
label="Grammar"
path={path}
keyPath="/training/grammar"
isMinimized
/>
</Nav>
)}
{sidebarPermissions["viewPaymentRecords"] && (
<Nav

View File

@@ -0,0 +1,41 @@
/* eslint-disable @next/next/no-img-element */
import Head from "next/head";
import { withIronSessionSsr } from "iron-session/next";
import { sessionOptions } from "@/lib/session";
import { User } from "@/interfaces/user";
import { ToastContainer } from "react-toastify";
import { shouldRedirectHome } from "@/utils/navigation.disabled";
import { redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res);
if (!user) return redirect("/login");
if (shouldRedirectHome(user)) return redirect("/");
return {
props: serialize({ user }),
};
}, sessionOptions);
const Grammar: React.FC<{
user: User;
}> = ({ user }) => {
return (
<>
<Head>
<title>Training | EnCoach</title>
<meta
name="description"
content="A training platform for the IELTS exam provided by the Muscat Training Institute and developed by eCrop."
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<ToastContainer />
</>
);
};
export default Grammar;

View File

@@ -0,0 +1,41 @@
/* eslint-disable @next/next/no-img-element */
import Head from "next/head";
import { withIronSessionSsr } from "iron-session/next";
import { sessionOptions } from "@/lib/session";
import { User } from "@/interfaces/user";
import { ToastContainer } from "react-toastify";
import { shouldRedirectHome } from "@/utils/navigation.disabled";
import { redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res);
if (!user) return redirect("/login");
if (shouldRedirectHome(user)) return redirect("/");
return {
props: serialize({ user }),
};
}, sessionOptions);
const Vocabulary: React.FC<{
user: User;
}> = ({ user }) => {
return (
<>
<Head>
<title>Training | EnCoach</title>
<meta
name="description"
content="A training platform for the IELTS exam provided by the Muscat Training Institute and developed by eCrop."
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<ToastContainer />
</>
);
};
export default Vocabulary;