/ dashboard-v2 / frontend / src / components / Layout.jsx
Layout.jsx
 1  import { NavLink, Outlet, useLocation } from 'react-router-dom';
 2  import { useQueryClient } from '@tanstack/react-query';
 3  import { useState } from 'react';
 4  import { refreshCache } from '../api';
 5  
 6  const NAV = [
 7    { path: '/overview', label: 'Overview', icon: '📊' },
 8    { path: '/outreach', label: 'Outreach', icon: '📤' },
 9    { path: '/conversations', label: 'Conversations', icon: '💬' },
10    { path: '/operations', label: 'Operations', icon: '⚙️' },
11    { path: '/quality', label: 'Quality', icon: '🔬' },
12    { path: '/compliance', label: 'Compliance', icon: '✅' },
13    { path: '/review', label: 'Review', icon: '🤝' },
14  ];
15  
16  export default function Layout() {
17    const qc = useQueryClient();
18    const [refreshing, setRefreshing] = useState(false);
19    const location = useLocation();
20    const pageTitle = NAV.find(n => location.pathname.startsWith(n.path))?.label ?? 'Audit&Fix';
21  
22    async function handleRefresh() {
23      setRefreshing(true);
24      try {
25        await refreshCache();
26        qc.invalidateQueries();
27      } catch (e) {
28        console.error(e);
29      } finally {
30        setRefreshing(false);
31      }
32    }
33  
34    return (
35      <div className="flex h-screen overflow-hidden bg-slate-900">
36        {/* Sidebar */}
37        <aside className="flex w-52 flex-shrink-0 flex-col border-r border-slate-700 bg-slate-900">
38          {/* Logo */}
39          <div className="flex h-14 items-center px-4 border-b border-slate-700">
40            <img src="/assets/img/logo.svg" alt="Audit&Fix" className="h-9" />
41          </div>
42  
43          {/* Nav links */}
44          <nav className="flex-1 overflow-y-auto py-3 px-2 space-y-0.5">
45            {NAV.map(({ path, label, icon }) => (
46              <NavLink
47                key={path}
48                to={path}
49                className={({ isActive }) =>
50                  `flex items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium transition-colors ${
51                    isActive
52                      ? 'bg-brand-orange/15 text-brand-orange'
53                      : 'text-slate-400 hover:bg-slate-800 hover:text-slate-200'
54                  }`
55                }
56              >
57                <span>{icon}</span>
58                {label}
59              </NavLink>
60            ))}
61          </nav>
62  
63          {/* Refresh button */}
64          <div className="border-t border-slate-700 p-3">
65            <button
66              onClick={handleRefresh}
67              disabled={refreshing}
68              className="w-full rounded-md bg-slate-800 px-3 py-2 text-xs text-slate-400
69                         hover:bg-slate-700 hover:text-slate-200 disabled:opacity-50 transition-colors"
70            >
71              {refreshing ? '⏳ Refreshing…' : '🔄 Refresh Cache'}
72            </button>
73          </div>
74        </aside>
75  
76        {/* Main content */}
77        <div className="flex flex-1 flex-col overflow-hidden">
78          {/* Top bar */}
79          <header className="flex h-14 items-center border-b border-slate-700 px-6">
80            <h1 className="text-base font-semibold text-slate-100">{pageTitle}</h1>
81          </header>
82  
83          {/* Page content */}
84          <main className="flex-1 overflow-y-auto p-6">
85            <Outlet />
86          </main>
87        </div>
88      </div>
89    );
90  }