Files
mwitnessing/components/sidebar.tsx
2024-12-01 22:26:41 +02:00

266 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { signIn, signOut, useSession } from "next-auth/react";
import styles from "../styles/header.module.css";
import React, { useState, useEffect, useRef, use } from "react";
import { useRouter } from 'next/router';
import sidemenu, { footerMenu } from './sidemenuData.js'; // Move sidemenu data to a separate file
import axiosInstance from "src/axiosSecure";
import common from "src/helpers/common";
import LanguageSwitcher from "./languageSwitcher";
import PwaManagerNotifications from "./PwaManagerNotifications";
import { useTranslations } from 'next-intl';
import { getTranslations } from 'next-intl/server';
import ProtectedPage from "pages/examples/protected";
import ProtectedRoute from "./protectedRoute";
import { UserRole } from "@prisma/client";
//get package version from package.json
const packageVersion = require('../package.json').version;
function SidebarMenuItem({ item, session, isSubmenu }) {
// const tMenu = useTranslations('menu');
// const [t, locale] = useState(useTranslations('menu'));
// useEffect(() => {
// console.log("SidebarMenuItem locale: ", locale);
// locale(useTranslations('common'));
// }, [locale]);
const router = useRouter();
const isActive = router.pathname.includes(item.url);
const collapsable = item.collapsable === undefined ? true : item.collapsable;
// is open is always true if not collapsable; isOpen is true if not collapsable
//const [isOpen, setIsOpen] = useState(false && collapsable);
// Initialize isOpen to true for non-collapsible items, ensuring they are always "open" // xOR
const baseClass = `sidemenu-item flex items-center ${isSubmenu ? "px-3 py-1" : ""} mt-1 pr-0 transition-colors duration-3000 transform rounded-md`;
const activeClass = isActive ? "sidemenu-item-active text-blue-600 bg-gray-100 dark:text-blue-400 dark:bg-blue-900" : "text-gray-700 dark:text-gray-300";
const hoverClasses = "hover:bg-gray-100 dark:hover:bg-gray-800 dark:hover:text-gray-200 hover:text-gray-700";
const initialState = common.getLocalStorage(`sidebar-openState-${item.id}`, isActive);
const [isOpen, setIsOpen] = useState(() => common.getLocalStorage(`sidebar-openState-${item.id}`, isActive));
useEffect(() => {
// Only run this effect on the client-side and if it's a submenu item
if (typeof window !== 'undefined' && isSubmenu) {
common.setLocalStorage(`sidebar-openState-${item.id}`, isOpen);
}
}, [isOpen, item.id, isSubmenu]);
useEffect(() => {
// This effect should also check for window to ensure it's client-side
if (typeof window !== 'undefined' && isSubmenu) {
const isAnyChildActive = item.children?.some(child => router.pathname.includes(child.url));
if (isActive || isAnyChildActive) {
setIsOpen(true);
}
}
}, [router.pathname, isActive, item.children, isSubmenu]);
if (!session || (item.roles && !item.roles.includes(session?.user?.role))) {
return null;
}
const handleClick = () => {
//console.log("clicked", item);
if (item.children && collapsable) { // Toggle isOpen only if item is collapsable and has children
setIsOpen(!isOpen);
} else if (item.url) {
router.push(item.url);
}
};
const clickableClass = item.url || item.children ? "cursor-pointer" : "";
return (
<>
<div className={`${baseClass} ${activeClass} ${hoverClasses} ${clickableClass}`}
onClick={handleClick}>
{item.svgData && <svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d={item.svgData} stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>}
<span className="ml-3 mr-1 font-medium">{item.text}</span>
{item.children && <DropDownIcon isOpen={isOpen} />}
</div>
{isOpen && item.children && (
// <ul className="relative accordion-collapse show">
<ul className="pl-2 mt-1">
{item.children.map((child, index) => (
<SidebarMenuItem key={index} item={child} session={session} isSubmenu={true} />
))}
</ul>
)}
</>
);
}
function DropDownIcon({ isOpen }) {
return (
<svg aria-hidden="false" focusable="false" className="w-3 h-3 ml-auto" viewBox="0 0 448 512">
{/* svg content */}
</svg>
);
}
export default function Sidebar({ isSidebarOpen, toggleSidebar }) {
const { data: session, status } = useSession();
const sidebarWidth = 226; // Simplify by using a constant
const sidebarRef = useRef(null);
const t = useTranslations('menu');
//const [locations, setLocations] = useState([]);
useEffect(() => {
const fetchLocations = async () => {
try {
// if (session)
{ // Don't fetch locations if the user is not authenticated
const response = await axiosInstance.get('/api/data/locations'); // Adjust the API endpoint as needed
const subMenuLocations = response.data
.filter(location => location.isActive === true && location.isOnMainMenu === false)
.map(location => ({
text: location.name,
url: `/cart/locations/${location.id}`,
isOnMainMenu: location.isOnMainMenu,
}));
const mainMenuLocations = response.data.filter(l => l.isOnMainMenu === true)
.map(location => ({
key: location.name,
text: t('location.' + location.name + '.menu') || location.name,
url: location.url,
}));
console.log("locationsData: ", response.data);
console.log("subMenuLocations: ", subMenuLocations);
console.log("mainMenuLocations: ", mainMenuLocations);
// Find the "Locations" menu item and populate its children with locationsData
const menuIndex = sidemenu.findIndex(item => item.id === "locations");
if (menuIndex !== -1) {
sidemenu[menuIndex].children = subMenuLocations;
}
// insert main menu items after the locations (sidemenu[menuIndex+1])
sidemenu.splice(menuIndex + 1, 0, ...mainMenuLocations);
//setLocations(locationsData); // Optional, if you need to use locations elsewhere
}
} catch (error) {
console.error("Error fetching locations:", error);
}
};
fetchLocations();
}, []);
if (status === "loading") {
return <div>Loading...</div>;
}
return (
<>
<button onClick={toggleSidebar}
className="fixed top-1 left-5 z-40 m- text-xl bg-white border border-gray-200 px-3 py-2.5 rounded-full shadow-lg focus:outline-none"
style={{ transform: isSidebarOpen ? `translateX(${sidebarWidth - 64}px)` : 'translateX(-20px)' }}></button>
<aside id="sidenav" ref={sidebarRef}
className="px-2 fixed top-0 left-0 z-30 h-screen overflow-y-auto bg-white border-r dark:bg-gray-900 dark:border-gray-700 transition-all duration-300 sm:translate-x-0 w-64"
style={{ width: `${sidebarWidth}px`, transform: isSidebarOpen ? 'translateX(0)' : `translateX(-${sidebarWidth - 16}px)` }}>
<h2 className="text-2xl font-semibold text-gray-800 dark:text-white pt-2 pl-4 pb-4"
title={`v.${packageVersion} ${process.env.GIT_COMMIT_ID}`} >Специално Свидетелстване София</h2>
<div className="flex flex-col justify-between pb-4">
<nav>
{sidemenu.map((item, index) => (
item.text = t(item.id) || item.text, // Use the translation if available
<SidebarMenuItem key={index} item={item} session={session} />
))}
<hr className="my-3 border-gray-200 dark:border-gray-600" />
{/* User section */}
<UserSection session={session} />
{/* Footer section: smaller lighter text */}
<div className="mt-auto">
<hr className="border-gray-200 dark:border-gray-600 text-align-bottom" />
<FooterSection />
<ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage="">
<LanguageSwitcher />
</ProtectedRoute >
</div>
</nav>
</div>
</aside>
</>
);
}
function UserSection({ session }) {
return (
<div className="sidemenu-item flex items-center">
{!session ? <SignInButton /> : <UserDetails session={session} />}
</div>
);
}
function SignInButton() {
// const t = useTranslations('common');
return (
<div className="items-center py-2 font-bold" onClick={() => signIn()}>
<button>вход</button>
{/* <button>{t('login')}</button> */}
</div>
);
}
function UserDetails({ session }) {
// const t = useTranslations('common');
return (
<>
<hr className="m-0" />
<div className="items-center">
{session.user.image && (
<img className="object-cover mx-2 rounded-full h-9 w-9" src={session.user.image} alt="avatar" />
)}
<div className="overflow-hidden">
<p className="mx-1 mt-1 text-sm font-medium text-gray-800 dark:text-gray-200">{session.user.name}</p>
<p className="mx-1 mt-1 text-sm font-medium text-gray-600 dark:text-gray-400">{session.user.role}</p>
<PwaManagerNotifications />
<a href="/api/auth/signout" className={styles.button} onClick={(e) => { e.preventDefault(); signOut(); }}>
{/* {t('logout')} */}
изход
</a>
</div>
</div>
</>
);
}
function FooterSection() {
const router = useRouter();
const navigateTo = (url) => {
router.push(url);
};
return (
footerMenu.map((item, index) => (
item.roles ? (
<ProtectedRoute key={index} allowedRoles={item.roles} deniedMessage="">
<div
className="px-4 py-1 mt-2 cursor-pointer hover:underline hover:text-blue-600 dark:hover:text-blue-400"
onClick={() => navigateTo(item.url)}
>
<span className="text-gray-700 dark:text-gray-300 font-medium">{item.text}</span>
</div>
</ProtectedRoute>
) : (
<div
key={index}
className="px-4 py-1 mt-2 cursor-pointer hover:underline hover:text-blue-600 dark:hover:text-blue-400"
onClick={() => navigateTo(item.url)}
>
<span className="text-gray-700 dark:text-gray-300 font-medium">{item.text}</span>
</div>
)
))
);
}