Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Sidebar Components to Adhere to DRY Principle #14

Open
ouhammmourachid opened this issue Feb 19, 2024 · 0 comments
Open

Refactor Sidebar Components to Adhere to DRY Principle #14

ouhammmourachid opened this issue Feb 19, 2024 · 0 comments

Comments

@ouhammmourachid
Copy link

ouhammmourachid commented Feb 19, 2024

The current implementation of Sidebar components in the repository appears to contain duplicated code segments, violating the DRY (Don't Repeat Yourself) principle. This redundancy can lead to maintenance issues, increased development time, and potential bugs.

To improve the codebase's maintainability and readability, it's crucial to refactor the Sidebar components to eliminate redundancy and adhere to the DRY principle. This involves identifying duplicated code segments and abstracting them into reusable functions or components.

here is a suggestion, but I am not sure about the name of class
SidebarGroup.tsx

"use client";
import SidebarLinkGroup from "./SidebarLinkGroup";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useState,useEffect } from "react";
import React from "react";
import Image from "next/image";

const SidebarGroup = ({...props}) => {
    const { name,iconPath,elements,path } = props;
    const pathname = usePathname();
    let extractedPath = path.split("/")[1];
    if (extractedPath === "") {
      extractedPath = "dashboard";
    }
    let storedSidebarExpanded = "true";
    const [sidebarExpanded, setSidebarExpanded] = useState(
        storedSidebarExpanded === null ? false : storedSidebarExpanded === "true",
      );
    
    useEffect(() => {
        localStorage.setItem("sidebar-expanded", sidebarExpanded.toString());
        if (sidebarExpanded) {
          document.querySelector("body")?.classList.add("sidebar-expanded");
        } else {
          document.querySelector("body")?.classList.remove("sidebar-expanded");
        }
      }, [sidebarExpanded]);
    
    return (
      <>
      {elements ?
      <SidebarLinkGroup
      activeCondition={
        pathname === path || pathname.includes(extractedPath)
      }
    >
      {(handleClick, open) => {
        return (
          <React.Fragment>
            <Link
              href="#"
              className={`group relative flex items-center gap-2.5 rounded-sm px-4 py-2 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${
                (pathname === path ||
                  pathname.includes(extractedPath)) &&
                "bg-graydark dark:bg-meta-4"
              }`}
              onClick={(e) => {
                e.preventDefault();
                sidebarExpanded
                  ? handleClick()
                  : setSidebarExpanded(true);
              }}
            >
              <Image
                  className="fill-current"
                  width={18}
                  height={18}
                  src={iconPath}
                  alt="Logo"
                  priority
              />
              {name}
              <Image
                className={`absolute right-4 top-1/2 -translate-y-1/2 fill-current ${
                  open && "rotate-180"
                }`}
                width={20}
                height={20}
                src="/images/icon/icon-arrow-up.svg"
                alt="Arrow"
                
              />
            </Link>
            {/* <!-- Dropdown Menu Start --> */}
            <div
              className={`translate transform overflow-hidden ${
                !open && "hidden"
              }`}
            >
              <ul className="mb-5.5 mt-4 flex flex-col gap-2.5 pl-6">
                {elements && elements.map((element: { href: string, name: string }, index: number) => {
                  return (
                    <li key={index}>
                      <Link
                        href={element.href}
                        className={`group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ${
                          pathname === element.href && "text-white"
                        }`}
                      >
                        {element.name}
                      </Link>
                    </li>
                  );
                })}
              </ul>
            </div>
            {/* <!-- Dropdown Menu End --> */}
          </React.Fragment>
        );
      }}
    </SidebarLinkGroup>
      :
      <li>
                <Link
                  href={path}
                  className={`group relative flex items-center gap-2.5 rounded-sm px-4 py-2 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${
                    pathname.includes("chart") && "bg-graydark dark:bg-meta-4"
                  }`}
                >
                  <Image
                    className="fill-current"
                    width={18}
                    height={18}
                    src={iconPath}
                    alt="Logo"
                    priority
                  />
                  {name}
                </Link>
      </li>
      }
      </>
    )   
};

export default SidebarGroup;

index.tsx

"use client";

import React, { useEffect, useRef, useState } from "react";
import { usePathname } from "next/navigation";
import Link from "next/link";
import Image from "next/image";
import SidebarGroup from "./SidebarGroup";

interface SidebarProps {
  sidebarOpen: boolean;
  setSidebarOpen: (arg: boolean) => void;
}

const Sidebar = ({ sidebarOpen, setSidebarOpen }: SidebarProps) => {
  const pathname = usePathname();

  const trigger = useRef<any>(null);
  const sidebar = useRef<any>(null);

  // dashboard elements
  const dashboardElements = [
    {
      name: "eCommerce",
      href: "/",
    }
  ];
  
  // ui elements
  const uiElements = [
    {
      name: "Alerts",
      href: "/ui/alerts",
    },
    {
      name: "Buttons",
      href: "/ui/buttons",
    },
  ];

  // authonetication elements
  const authElements = [
    {
      name: "Sign In",
      href: "/auth/signin",
    },
    {
      name: "Sign Up",
      href: "/auth/signup",
    },
  ];

  // close on click outside
  useEffect(() => {
    const clickHandler = ({ target }: MouseEvent) => {
      if (!sidebar.current || !trigger.current) return;
      if (
        !sidebarOpen ||
        sidebar.current.contains(target) ||
        trigger.current.contains(target)
      )
        return;
      setSidebarOpen(false);
    };
    document.addEventListener("click", clickHandler);
    return () => document.removeEventListener("click", clickHandler);
  });

  // close if the esc key is pressed
  useEffect(() => {
    const keyHandler = ({ key }: KeyboardEvent) => {
      if (!sidebarOpen || key !== "Escape") return;
      setSidebarOpen(false);
    };
    document.addEventListener("keydown", keyHandler);
    return () => document.removeEventListener("keydown", keyHandler);
  });

  return (
    <aside
      ref={sidebar}
      className={`absolute left-0 top-0 z-9999 flex h-screen w-72.5 flex-col overflow-y-hidden bg-black duration-300 ease-linear dark:bg-boxdark lg:static lg:translate-x-0 ${
        sidebarOpen ? "translate-x-0" : "-translate-x-full"
      }`}
    >
      {/* <!-- SIDEBAR HEADER --> */}
      <div className="flex items-center justify-between gap-2 px-6 py-5.5 lg:py-6.5">
        <Link href="/">
          <Image
            width={180}
            height={32}
            src={"/images/logo/logo.svg"}
            alt="Logo"
            priority
          />
        </Link>

        <button
          ref={trigger}
          onClick={() => setSidebarOpen(!sidebarOpen)}
          aria-controls="sidebar"
          aria-expanded={sidebarOpen}
          className="block lg:hidden"
        >
          <Image
            width={20}
            height={18}
            src="/images/icon/icon-close.svg"
            alt="Close"
            priority
          />
        </button>
      </div>
      {/* <!-- SIDEBAR HEADER --> */}

      <div className="no-scrollbar flex flex-col overflow-y-auto duration-300 ease-linear">
        {/* <!-- Sidebar Menu --> */}
        <nav className="mt-5 px-4 py-4 lg:mt-9 lg:px-6">
          {/* <!-- Menu Group --> */}
          <div>
            <h3 className="mb-4 ml-4 text-sm font-semibold text-bodydark2">
              MENU
            </h3>

            <ul className="mb-6 flex flex-col gap-1.5">
              {/* <!-- Element of MENU --> */}
              
              <SidebarGroup 
                name="Dashboard" 
                iconPath="/images/icon/icon-dashboard.svg"
                elements={dashboardElements}
                path="/"/>

            </ul>
          </div>

          {/* <!-- Others Group --> */}
          <div>
            <h3 className="mb-4 ml-4 text-sm font-semibold text-bodydark2">
              OTHERS
            </h3>

            <ul className="mb-6 flex flex-col gap-1.5">
              {/* <!-- Elements of OTHERS --> */}
              <SidebarGroup
                name="Chart"
                iconPath="/images/icon/icon-chart.svg"
                path="/chart"
                />

              <SidebarGroup
                name="UI Elements"
                iconPath="/images/icon/icon-ui.svg"
                elements={uiElements}
                path="/ui"
                />

              <SidebarGroup
                name="Authentication"
                iconPath="/images/icon/icon-auth.svg"
                elements={authElements}
                path="/auth"
                />
              {/* <!-- Menu Item Auth Pages --> */}
            </ul>
          </div>
        </nav>
        {/* <!-- Sidebar Menu --> */}
      </div>
    </aside>
  );
};

export default Sidebar;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant