initial commit - code moved to separate repo

This commit is contained in:
Dobromir Popov
2024-02-22 04:19:38 +02:00
commit 560d503219
240 changed files with 105125 additions and 0 deletions

209
components/sidebar.tsx Normal file
View File

@ -0,0 +1,209 @@
import { signIn, signOut, useSession } from "next-auth/react";
import styles from "../styles/header.module.css";
import React, { useState, useEffect, useRef } 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";
//get package version from package.json
const packageVersion = require('../package.json').version;
function SidebarMenuItem({ item, session, isSubmenu }) {
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-4 py-1" : ""} mt-1 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="mx-4 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 = 256; // Simplify by using a constant
const sidebarRef = useRef(null);
//const [locations, setLocations] = useState([]);
useEffect(() => {
const fetchLocations = async () => {
try {
const response = await axiosInstance.get('/api/data/locations'); // Adjust the API endpoint as needed
const locationsData = response.data
.filter(location => location.isactive === true)
.map(location => ({
text: location.name,
url: `/cart/locations/${location.id}`,
}));
// 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 = locationsData;
}
//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-0 left-0 z-40 m-4 text-xl bg-white border border-gray-200 p-2 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 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) => (
<SidebarMenuItem key={index} item={item} session={session} />
))}
<hr className="my-6 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 />
</div>
</nav>
</div>
</aside>
</>
);
}
function UserSection({ session }) {
return (
<div className="sidemenu-item flex items-center">
{!session ? <SignInButton /> : <UserDetails session={session} />}
</div>
);
}
function SignInButton() {
return (
<div className="items-center py-6" onClick={() => signIn()}>
<a href="/api/auth/signin">Впишете се</a>
</div>
);
}
function UserDetails({ session }) {
return (
<>
<hr className="m-0" />
<div className="flex items-center py-4 -mx-2">
{session.user.image && (
<img className="object-cover mx-2 rounded-full h-9 w-9" src={session.user.image} alt="avatar" />
)}
<div className="ml-3 overflow-hidden">
<p className="mx-2 mt-2 text-sm font-medium text-gray-800 dark:text-gray-200">{session.user.name}</p>
<p className="mx-2 mt-1 text-sm font-medium text-gray-600 dark:text-gray-400">{session.user.role}</p>
<a href="/api/auth/signout" className={styles.button} onClick={(e) => { e.preventDefault(); signOut(); }}>Излезте</a>
</div>
</div>
</>
);
}
function FooterSection() {
const router = useRouter();
const navigateTo = (url) => {
router.push(url);
};
return (
footerMenu.map((item, index) => (
<div
key={index}
className="px-4 py-2 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>
))
);
}