Added a capability to minimize the sidebar
This commit is contained in:
@@ -37,7 +37,7 @@ export default function Layout({user, children, className, navDisabled = false,
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"w-full lg:w-5/6 min-h-full h-fit lg:mr-8 bg-white shadow-md rounded-2xl p-12 pb-8 flex flex-col gap-12 relative overflow-hidden mt-2",
|
"w-full min-h-full h-fit lg:mr-8 bg-white shadow-md rounded-2xl p-12 pb-8 flex flex-col gap-12 relative overflow-hidden mt-2",
|
||||||
className,
|
className,
|
||||||
)}>
|
)}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import clsx from "clsx";
|
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 {BsFileEarmarkText, BsClockHistory, BsPencil, BsGraphUp} from "react-icons/bs";
|
import {BsFileEarmarkText, BsClockHistory, BsPencil, BsGraphUp, BsChevronBarRight, BsChevronBarLeft} from "react-icons/bs";
|
||||||
import {RiLogoutBoxFill} from "react-icons/ri";
|
import {RiLogoutBoxFill} from "react-icons/ri";
|
||||||
import {SlPencil} from "react-icons/sl";
|
import {SlPencil} from "react-icons/sl";
|
||||||
import {FaAward} from "react-icons/fa";
|
import {FaAward} from "react-icons/fa";
|
||||||
@@ -10,6 +10,8 @@ import {useRouter} from "next/router";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import FocusLayer from "@/components/FocusLayer";
|
import FocusLayer from "@/components/FocusLayer";
|
||||||
import {preventNavigation} from "@/utils/navigation.disabled";
|
import {preventNavigation} from "@/utils/navigation.disabled";
|
||||||
|
import {useState} from "react";
|
||||||
|
import usePreferencesStore from "@/stores/preferencesStore";
|
||||||
interface Props {
|
interface Props {
|
||||||
path: string;
|
path: string;
|
||||||
navDisabled?: boolean;
|
navDisabled?: boolean;
|
||||||
@@ -24,23 +26,28 @@ interface NavProps {
|
|||||||
path: string;
|
path: string;
|
||||||
keyPath: string;
|
keyPath: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
isMinimized?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Nav = ({Icon, label, path, keyPath, disabled = false}: NavProps) => (
|
const Nav = ({Icon, label, path, keyPath, disabled = false, isMinimized = false}: NavProps) => (
|
||||||
<Link
|
<Link
|
||||||
href={!disabled ? keyPath : ""}
|
href={!disabled ? keyPath : ""}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"p-4 px-8 rounded-full flex gap-4 items-center cursor-pointer text-gray-500 hover:bg-mti-purple-light hover:text-white transition duration-300 ease-in-out",
|
"p-4 rounded-full flex gap-4 items-center cursor-pointer text-gray-500 hover:bg-mti-purple-light hover:text-white",
|
||||||
|
"transition-all duration-300 ease-in-out",
|
||||||
path === keyPath && "bg-mti-purple-light text-white",
|
path === keyPath && "bg-mti-purple-light text-white",
|
||||||
|
isMinimized ? "w-fit" : "w-full min-w-[250px] px-8",
|
||||||
)}>
|
)}>
|
||||||
<Icon size={20} />
|
<Icon size={24} />
|
||||||
<span className="text-lg font-semibold">{label}</span>
|
{!isMinimized && <span className="text-lg font-semibold">{label}</span>}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default function Sidebar({path, navDisabled = false, focusMode = false, onFocusLayerMouseEnter, className}: Props) {
|
export default function Sidebar({path, navDisabled = false, focusMode = false, onFocusLayerMouseEnter, className}: Props) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const [isMinimized, toggleMinimize] = usePreferencesStore((state) => [state.isSidebarMinimized, state.toggleSidebarMinimized]);
|
||||||
|
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
axios.post("/api/logout").finally(() => {
|
axios.post("/api/logout").finally(() => {
|
||||||
setTimeout(() => router.reload(), 500);
|
setTimeout(() => router.reload(), 500);
|
||||||
@@ -50,25 +57,43 @@ export default function Sidebar({path, navDisabled = false, focusMode = false, o
|
|||||||
const disableNavigation = preventNavigation(navDisabled, focusMode);
|
const disableNavigation = preventNavigation(navDisabled, focusMode);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={clsx("h-full flex bg-transparent flex-col justify-between w-1/6 px-4 py-4 pb-8 relative", className)}>
|
<section
|
||||||
|
className={clsx(
|
||||||
|
"h-full flex bg-transparent flex-col justify-between px-4 py-4 pb-8 relative",
|
||||||
|
isMinimized ? "w-fit" : "w-1/6",
|
||||||
|
className,
|
||||||
|
)}>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<Nav disabled={disableNavigation} Icon={MdSpaceDashboard} label="Dashboard" path={path} keyPath="/" />
|
<Nav disabled={disableNavigation} Icon={MdSpaceDashboard} label="Dashboard" path={path} keyPath="/" isMinimized={isMinimized} />
|
||||||
<Nav disabled={disableNavigation} Icon={BsFileEarmarkText} label="Exams" path={path} keyPath="/exam" />
|
<Nav disabled={disableNavigation} Icon={BsFileEarmarkText} label="Exams" path={path} keyPath="/exam" isMinimized={isMinimized} />
|
||||||
<Nav disabled={disableNavigation} Icon={BsPencil} label="Exercises" path={path} keyPath="/exercises" />
|
<Nav disabled={disableNavigation} Icon={BsPencil} label="Exercises" path={path} keyPath="/exercises" isMinimized={isMinimized} />
|
||||||
<Nav disabled={disableNavigation} Icon={BsGraphUp} label="Stats" path={path} keyPath="/stats" />
|
<Nav disabled={disableNavigation} Icon={BsGraphUp} label="Stats" path={path} keyPath="/stats" isMinimized={isMinimized} />
|
||||||
<Nav disabled={disableNavigation} Icon={BsClockHistory} label="Record" path={path} keyPath="/record" />
|
<Nav disabled={disableNavigation} Icon={BsClockHistory} label="Record" path={path} keyPath="/record" isMinimized={isMinimized} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-0 absolute bottom-12">
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={1}
|
||||||
|
onClick={toggleMinimize}
|
||||||
|
className={clsx(
|
||||||
|
"p-4 rounded-full flex gap-4 items-center cursor-pointer text-black hover:text-mti-rose 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
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
onClick={focusMode ? () => {} : logout}
|
onClick={focusMode ? () => {} : logout}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"p-4 px-8 rounded-full flex gap-4 items-center cursor-pointer text-black hover:text-mti-rose transition duration-300 ease-in-out",
|
"p-4 rounded-full flex gap-4 items-center cursor-pointer text-black hover:text-mti-rose transition duration-300 ease-in-out",
|
||||||
"absolute bottom-8",
|
isMinimized ? "w-fit" : "w-full min-w-[250px] px-8",
|
||||||
)}>
|
)}>
|
||||||
<RiLogoutBoxFill size={20} />
|
<RiLogoutBoxFill size={24} />
|
||||||
<span className="text-lg font-medium">Log Out</span>
|
{!isMinimized && <span className="text-lg font-medium">Log Out</span>}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{focusMode && <FocusLayer onFocusLayerMouseEnter={onFocusLayerMouseEnter} />}
|
{focusMode && <FocusLayer onFocusLayerMouseEnter={onFocusLayerMouseEnter} />}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
id: docSnap.id,
|
id: docSnap.id,
|
||||||
...docSnap.data(),
|
...docSnap.data(),
|
||||||
|
module,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(404).json(undefined);
|
res.status(404).json(undefined);
|
||||||
|
|||||||
20
src/stores/preferencesStore.ts
Normal file
20
src/stores/preferencesStore.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import {Module} from "@/interfaces";
|
||||||
|
import {Exam, UserSolution} from "@/interfaces/exam";
|
||||||
|
import {create} from "zustand";
|
||||||
|
|
||||||
|
export interface PreferencesState {
|
||||||
|
isSidebarMinimized: boolean;
|
||||||
|
toggleSidebarMinimized: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initialState = {
|
||||||
|
isSidebarMinimized: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const usePreferencesStore = create<PreferencesState>((set) => ({
|
||||||
|
...initialState,
|
||||||
|
toggleSidebarMinimized: () => set((state) => ({isSidebarMinimized: !state.isSidebarMinimized})),
|
||||||
|
reset: () => set(() => initialState),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default usePreferencesStore;
|
||||||
Reference in New Issue
Block a user