All files / apps/web/src/backoffice/components BackofficeLayout.jsx

0% Statements 0/16
0% Branches 0/18
0% Functions 0/8
0% Lines 0/16

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161                                                                                                                                                                                                                                                                                                                                 
import { Outlet, Link, useLocation } from 'react-router-dom';
import {
  ChevronLeft,
  Shield,
  Crown,
  Settings,
  ExternalLink,
  User,
  Menu,
  X
} from 'lucide-react';
import { useState, useEffect } from 'react';
import { useAuth } from '../../contexts/AuthContext';
import Navigation from '../../components/Navigation';
import OrgSwitcher from '../../components/OrgSwitcher';
 
import { backofficeNavigation } from '../../config/backofficeNavigation';
import { useOrganizationStore } from '../../store/organizationStore';
 
export default function BackofficeLayout() {
  const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
  const [mobileOpen, setMobileOpen] = useState(false);
  const location = useLocation();
  const { user } = useAuth();
  const { currentOrganization } = useOrganizationStore();
  const organizationId = currentOrganization?.id;
 
  // Ferme le menu mobile à chaque navigation
  useEffect(() => {
    setMobileOpen(false);
  }, [location.pathname]);
 
  const renderSidebarContent = (collapsed) => (
    <>
      {/* Navigation */}
      <Navigation
        sections={backofficeNavigation}
        theme="dark"
        collapsible={true}
        collapsed={collapsed}
        className="p-2 overflow-y-auto flex-1"
        basePath={`/${organizationId}/backoffice`}
        userRole={user?.role}
        onItemClick={() => setMobileOpen(false)}
      />
 
      {/* Bottom section */}
      <div className="mt-auto p-4 border-t border-slate-800 space-y-3">
        {/* 0. Admin Link - only for admins */}
        {user?.role === 'admin' && (
          <Link
            to="/admin/users"
            className="flex items-center justify-center w-full px-3 py-2 text-sm text-purple-300 bg-purple-900/30 hover:bg-purple-900/50 rounded-lg transition-colors border border-purple-700"
          >
            <Crown className="w-4 h-4 mr-2" />
            {!collapsed && 'Administration'}
          </Link>
        )}
 
        {/* 1. Organization selector with gear */}
        <div className="flex items-center space-x-2">
          <div className="flex-1 bg-white rounded-lg">
            <OrgSwitcher />
          </div>
          <Link
            to={`/${organizationId}`}
            className="p-2 text-slate-400 hover:text-white hover:bg-slate-800 rounded-lg transition-colors"
            title="Retour à l'application"
          >
            <ExternalLink className="w-4 h-4" />
          </Link>
        </div>
 
        {/* 2. User with gear */}
        <Link
          to="/profile"
          className="flex items-center px-3 py-2 text-slate-300 hover:bg-slate-800 rounded-lg transition-colors border border-slate-700 hover:border-slate-600"
        >
          <div className="w-8 h-8 rounded-full bg-indigo-900 flex items-center justify-center flex-shrink-0 mr-3">
            <User className="w-4 h-4 text-indigo-400" />
          </div>
          <div className="flex-1 min-w-0">
            <p className="text-sm font-medium truncate">{user?.name}</p>
            <p className="text-xs text-slate-400 truncate">{user?.email}</p>
          </div>
          <Settings className="w-4 h-4 text-slate-400" />
        </Link>
      </div>
    </>
  );
 
  return (
    <div className="min-h-screen bg-gray-100">
      {/* Mobile sidebar (burger) */}
      <div className={`fixed inset-0 z-50 lg:hidden ${mobileOpen ? 'block' : 'hidden'}`}>
        <div className="fixed inset-0 bg-gray-900/50" onClick={() => setMobileOpen(false)} />
        <div className="fixed inset-y-0 left-0 w-72 bg-slate-900 text-white overflow-y-auto flex flex-col">
          <div className="flex items-center justify-between h-16 px-4 border-b border-slate-800">
            <div className="flex items-center space-x-2">
              <Shield className="w-6 h-6 text-indigo-400" />
              <span className="text-lg font-bold">Backoffice</span>
            </div>
            <button onClick={() => setMobileOpen(false)} className="p-1 rounded hover:bg-slate-800">
              <X className="w-5 h-5" />
            </button>
          </div>
          {renderSidebarContent(false)}
        </div>
      </div>
 
      {/* Desktop sidebar */}
      <aside
        className={`hidden lg:flex lg:fixed lg:inset-y-0 lg:left-0 lg:z-50 bg-slate-900 text-white transition-all duration-300 ${
          sidebarCollapsed ? 'lg:w-20' : 'lg:w-72'
        }`}
      >
        {/* Logo */}
        <div className="flex items-center justify-between h-16 px-4 border-b border-slate-800 flex-shrink-0">
          {!sidebarCollapsed && (
            <div className="flex items-center space-x-2">
              <Shield className="w-6 h-6 text-indigo-400" />
              <span className="text-lg font-bold">Backoffice</span>
            </div>
          )}
          {sidebarCollapsed && <Shield className="w-6 h-6 text-indigo-400 mx-auto" />}
          <button
            onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
            className={`p-1 rounded hover:bg-slate-800 ${sidebarCollapsed ? 'mx-auto' : ''}`}
          >
            <ChevronLeft className={`w-5 h-5 transition-transform ${sidebarCollapsed ? 'rotate-180' : ''}`} />
          </button>
        </div>
        {renderSidebarContent(sidebarCollapsed)}
      </aside>
 
      {/* Main content */}
      <div
        className={`transition-all duration-300 ${
          sidebarCollapsed ? 'lg:ml-20' : 'lg:ml-72'
        }`}
      >
        {/* Mobile header */}
        <div className="sticky top-0 z-40 flex items-center justify-between h-16 px-4 bg-slate-900 text-white border-b border-slate-800 lg:hidden">
          <button onClick={() => setMobileOpen(true)} className="p-2 -ml-2 rounded hover:bg-slate-800">
            <Menu className="w-6 h-6" />
          </button>
          <span className="flex items-center gap-2 text-lg font-bold">
            <Shield className="w-5 h-5 text-indigo-400" />
            Backoffice
          </span>
          <div className="w-8" />
        </div>
 
        {/* Page content */}
        <main className="p-3 sm:p-6">
          <Outlet />
        </main>
      </div>
    </div>
  );
}