Compare commits

...

1 Commits

Author SHA1 Message Date
José Lima
25aef3afdf Added new pages and nav with menu 2025-03-07 04:38:57 +00:00
3 changed files with 184 additions and 26 deletions

View File

@@ -12,6 +12,10 @@ import {
BsCurrencyDollar, BsCurrencyDollar,
BsClipboardData, BsClipboardData,
BsPeople, BsPeople,
BsChevronDown,
BsChevronUp,
BsChatText,
BsCardText,
} from "react-icons/bs"; } from "react-icons/bs";
import { GoWorkflow } from "react-icons/go"; import { GoWorkflow } from "react-icons/go";
import { CiDumbbell } from "react-icons/ci"; import { CiDumbbell } from "react-icons/ci";
@@ -31,7 +35,7 @@ import {
useAllowedEntities, useAllowedEntities,
useAllowedEntitiesSomePermissions, useAllowedEntitiesSomePermissions,
} from "@/hooks/useEntityPermissions"; } from "@/hooks/useEntityPermissions";
import { useMemo } from "react"; import { useMemo, useState } from "react";
import { PermissionType } from "../interfaces/permissions"; import { PermissionType } from "../interfaces/permissions";
interface Props { interface Props {
@@ -52,6 +56,7 @@ interface NavProps {
disabled?: boolean; disabled?: boolean;
isMinimized?: boolean; isMinimized?: boolean;
badge?: number; badge?: number;
children?: React.ReactNode;
} }
const Nav = ({ const Nav = ({
@@ -62,8 +67,16 @@ const Nav = ({
disabled = false, disabled = false,
isMinimized = false, isMinimized = false,
badge, badge,
children,
}: NavProps) => { }: NavProps) => {
const [open, setOpen] = useState(false);
return ( return (
<div
className={clsx(
"flex flex-col gap-2 transition-all duration-300 ease-in-out",
open && !isMinimized && "bg-white rounded-xl"
)}
>
<Link <Link
href={!disabled ? keyPath : ""} href={!disabled ? keyPath : ""}
className={clsx( className={clsx(
@@ -89,7 +102,36 @@ const Nav = ({
{badge} {badge}
</div> </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> </Link>
{open || isMinimized ? children : null}
</div>
); );
}; };
@@ -325,7 +367,24 @@ export default function Sidebar({
path={path} path={path}
keyPath="/training" keyPath="/training"
isMinimized={isMinimized} 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"] && ( {sidebarPermissions["viewPaymentRecords"] && (
<Nav <Nav
@@ -424,7 +483,24 @@ export default function Sidebar({
path={path} path={path}
keyPath="/training" keyPath="/training"
isMinimized 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"] && ( {sidebarPermissions["viewPaymentRecords"] && (
<Nav <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;