CoolFace
Apppublic

DataScope26/WEB

sourceHugging Faceupdated 7d agoView on Hugging Face
0likes
Sidebar.jsx192 linesDownload Raw Back to components
1import { useState, useEffect, useRef } from "react";2import { NavLink, useNavigate, Link } from "react-router-dom";3import { useAuth } from "../context/AuthContext.jsx";4import {5  LayoutGrid,6  ShieldCheck,7  Building2,8  Settings2,9  Settings,10  Briefcase,11  HelpCircle,12  LogOut,13  Mail,14  Users,15} from "lucide-react";16import "./Sidebar.css";17import ButtonPrimary from "./ButtonPrimary.jsx";18 19const NAV_ITEMS = [20  { key: "dashboard", label: "General dashboard", icon: LayoutGrid, path: "/overview" },21  { key: "company", label: "My companies", icon: Building2, path: "/overview/mycompanies" },22  { key: "invitations", label: "Invitaciones", icon: Mail, path: "/overview/invitaciones" },23  { key: "waitlist", label: "Waitlist", icon: Users, path: "/overview/waitlist", developerOnly: true },24  //{ key: "control", label: "Control panel", icon: ShieldCheck, path: "/overview/control" },25  { key: "user", label: "User settings", icon: Settings, path: "/overview/perfil" },26  { key: "companysettings", label: "Company settings", icon: Briefcase, path: "/overview/companysettings" },27];28 29export default function Sidebar() {30 31  const [expanded, setExpanded] = useState(false);32  const [hasSelectedCompany, setHasSelectedCompany] = useState(false);33  const [companyMenuOpen, setCompanyMenuOpen] = useState(false);34  35 36  const sidebarRef = useRef(null);37  const companyRef = useRef(null);38  const navigate = useNavigate();39  const { logout, selectedCompany, user, setSelectedCompany } = useAuth();40 41  // Determine whether the current user has any companies — hide company settings when none42  useEffect(() => {43    setHasSelectedCompany(!!(user && user.companies && user.companies.length > 0));44  }, [user]);45 46  useEffect(() => {47    const handleScroll = () => {48      const footer = document.querySelector('.footer');49      if (footer && sidebarRef.current) {50        const footerRect = footer.getBoundingClientRect();51        const visibleFooterHeight = Math.max(0, window.innerHeight - footerRect.top);52        sidebarRef.current.style.setProperty('--footer-offset', `${visibleFooterHeight}px`);53      }54    };55 56    window.addEventListener('scroll', handleScroll, { passive: true });57    window.addEventListener('resize', handleScroll);58 59    handleScroll();60    const timer = setTimeout(handleScroll, 300);61 62    return () => {63      window.removeEventListener('scroll', handleScroll);64      window.removeEventListener('resize', handleScroll);65      clearTimeout(timer);66    };67  }, []);68 69  // Close company menu and collapse sidebar when clicking outside the sidebar70  useEffect(() => {71    const handleOutside = (e) => {72      if (sidebarRef.current && !sidebarRef.current.contains(e.target)) {73        setCompanyMenuOpen(false);74        setExpanded(false);75      }76    };77    document.addEventListener('mousedown', handleOutside);78    return () => document.removeEventListener('mousedown', handleOutside);79  }, []);80 81  const handleLogout = () => {82    logout();83    navigate('/');84  };85 86  const handleSelectCompany = (company) => {87    setSelectedCompany && setSelectedCompany(company);88    try { localStorage.setItem('selectedCompanyId', company.id.toString()); } catch (e) {}89    setCompanyMenuOpen(false);90    navigate('/overview', { state: { company } });91  };92 93  const currentCompany = selectedCompany || (user && user.companies && user.companies[0]) || null;94  const otherCompanies = (user && user.companies ? user.companies.filter(c => !currentCompany || c.id !== currentCompany.id) : []);95  const companyInitial = currentCompany && currentCompany.name ? currentCompany.name.charAt(0).toUpperCase() : '';96  const canToggleMenu = otherCompanies.length > 0; // only show menu when there are other companies97 98  const renderCompanyAvatar = (company, size = 'default') => {99    const initials = company && company.name ? company.name.charAt(0).toUpperCase() : '';100    const isSmall = size === 'small';101 102    return (103      <span className={`company-avatar ${isSmall ? 'company-avatar--small' : ''}`}>104        {company && company.logo ? (105          <img src={company.logo} alt={company.name} className="company-logo-img" />106        ) : (107          <span className="company-avatar__placeholder" aria-label={company ? company.name : 'Company'}>{initials}</span>108        )}109      </span>110    );111  };112 113  return (114    <aside115      ref={sidebarRef}116      className={`sidebar ${expanded ? "sidebar--expanded" : ""}`}117      onMouseEnter={() => setExpanded(true)}118      onMouseLeave={() => { if (!companyMenuOpen) setExpanded(false); }}119    >120      <nav className="sidebar-nav">121        {/* Company row with toggleable list of other companies */}122        {hasSelectedCompany && user && (123          <div className="sidebar-company" ref={companyRef}>124            <button125              type="button"126              className="sidebar-link sidebar-company-btn"127              onClick={() => { if (canToggleMenu) setCompanyMenuOpen(s => !s); }}128              aria-expanded={companyMenuOpen}129              disabled={!canToggleMenu}130            >131              <span className="sidebar-icon sidebar-company-icon">132                {renderCompanyAvatar(currentCompany)}133              </span>134              <span className="sidebar-label sidebar-company-label">{currentCompany ? currentCompany.name : 'Sin empresa'}</span>135            </button>136            {companyMenuOpen && canToggleMenu && (137              <div className={`sidebar-company-list sidebar-company-list--inline`} role="menu">138                {otherCompanies.map((c) => (139                  <button140                    key={c.id}141                    type="button"142                    className={`sidebar-company-item`}143                    onClick={() => handleSelectCompany(c)}144                  >145                    <span className="sidebar-company-item-icon">146                      {renderCompanyAvatar(c, 'small')}147                    </span>148                    <span className="sidebar-company-item-label">{c.name}</span>149                  </button>150                ))}151              </div>152            )}153          </div>154        )}155        <ul className="sidebar-list">156          {NAV_ITEMS157            .filter(item => {158              if (item.key === 'companysettings' && !hasSelectedCompany) return false;159              if ((item.key === 'companysettings' || item.key === 'company' || item.key === 'invitations') && Number(user?.rolefk) === 3) return false;160              if (item.developerOnly && Number(user?.rolefk) !== 4) return false;161              return true;162            })163            .map(({ key, label, icon: Icon, path }) => (164            <li key={key} className="sidebar-item">165              <NavLink166                to={path}167                end={path === "/overview"}168                className={({ isActive }) =>169                  `sidebar-link ${isActive ? "sidebar-link--active" : ""}`170                }171              >172                <span className="sidebar-icon">173                  <Icon size={20} strokeWidth={2} />174                </span>175                <span className="sidebar-label">{label}</span>176              </NavLink>177            </li>178          ))}179        </ul>180      </nav>181 182      <div className="sidebar-footer">183        <ButtonPrimary type="button" onClick={handleLogout} className="sidebar-link sidebar-link--button">184          <span className="sidebar-icon">185            <LogOut size={20} strokeWidth={2} />186          </span>187          <span className="sidebar-label">Logout</span>188        </ButtonPrimary>189      </div>190    </aside>191  );192}