diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 39dd71c..2709117 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,7 +1,184 @@ +import { useState } from 'react'; import { InventoryPage } from './pages/InventoryPage'; +import './app-shell.css'; + +type TabKey = 'overview' | 'inventory' | 'discovery' | 'topology' | 'wip'; + +const tabs: { key: TabKey; label: string; description: string }[] = [ + { key: 'overview', label: 'Overview', description: 'Stato generale del workspace NetMapper.' }, + { key: 'inventory', label: 'Inventory', description: 'Tree fisico/logico e navigazione tra oggetti.' }, + { key: 'discovery', label: 'Discovery', description: 'Pipeline e job di raccolta dati.' }, + { key: 'topology', label: 'Topology', description: 'Vista futura della mappa multilivello.' }, + { key: 'wip', label: 'WIP', description: 'Blocchi provvisori, note e parti in corso.' }, +]; + +function ShellCard({ title, children, tone = 'default' }: { title: string; children: React.ReactNode; tone?: 'default' | 'accent' | 'warning' }) { + return ( +
+
+

{title}

+
+
{children}
+
+ ); +} + +function OverviewTab() { + return ( +
+ + + + + + + + + +
+ ); +} + +function DiscoveryTab() { + return ( +
+ + + + + + +
+ ); +} + +function TopologyTab() { + return ( +
+ + + + + + +
+ ); +} + +function WipTab() { + return ( +
+ + + + + + +
+ ); +} function App() { - return ; + const [activeTab, setActiveTab] = useState('inventory'); + const currentTab = tabs.find((tab) => tab.key === activeTab)!; + + return ( +
+ + +
+
+
+

{currentTab.label}

+

{currentTab.description}

+
+
+ LXC ready + Docker-ready base +
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ ); } export default App; diff --git a/frontend/src/app-shell.css b/frontend/src/app-shell.css new file mode 100644 index 0000000..d1e6e43 --- /dev/null +++ b/frontend/src/app-shell.css @@ -0,0 +1,170 @@ +:root { + color-scheme: dark; + font-family: Inter, system-ui, sans-serif; + background: #0b1020; +} + +body { + margin: 0; + background: linear-gradient(180deg, #0b1020 0%, #111827 100%); + color: #e5e7eb; +} + +#root { + min-height: 100vh; +} + +.app-shell { + display: grid; + grid-template-columns: 280px minmax(0, 1fr); + min-height: 100vh; +} + +.app-sidebar { + border-right: 1px solid rgba(255,255,255,0.08); + background: rgba(2, 6, 23, 0.75); + backdrop-filter: blur(14px); + padding: 20px; + display: flex; + flex-direction: column; + gap: 20px; +} + +.app-brand { + display: flex; + align-items: center; + gap: 12px; +} + +.app-brand-mark { + width: 42px; + height: 42px; + border-radius: 12px; + display: grid; + place-items: center; + background: linear-gradient(135deg, #1d4ed8, #2563eb); + color: white; + font-weight: 700; +} + +.app-brand h1, +.app-header h2, +.shell-card h3 { + margin: 0; +} + +.app-brand p, +.app-header p { + margin: 4px 0 0; + color: #94a3b8; +} + +.app-nav { + display: flex; + flex-direction: column; + gap: 10px; +} + +.app-nav-item { + border: 1px solid rgba(255,255,255,0.08); + background: rgba(255,255,255,0.03); + border-radius: 12px; + padding: 12px 14px; + color: #e5e7eb; + text-align: left; + cursor: pointer; + display: flex; + flex-direction: column; + gap: 4px; +} + +.app-nav-item.active { + background: rgba(37, 99, 235, 0.16); + border-color: rgba(96, 165, 250, 0.35); +} + +.app-nav-item small { + color: #94a3b8; +} + +.app-sidebar-footer, +.app-header-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.app-main { + padding: 20px; +} + +.app-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 16px; + margin-bottom: 20px; +} + +.shell-pill { + display: inline-flex; + align-items: center; + padding: 6px 10px; + border-radius: 999px; + background: rgba(255,255,255,0.08); + color: #cbd5e1; + font-size: 12px; +} + +.shell-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; +} + +.shell-grid.two-columns { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.shell-card { + background: rgba(17, 24, 39, 0.88); + border: 1px solid rgba(255,255,255,0.08); + border-radius: 16px; + padding: 16px; +} + +.shell-card.accent { + border-color: rgba(96, 165, 250, 0.35); +} + +.shell-card.warning { + border-color: rgba(251, 191, 36, 0.35); +} + +.shell-card-body { + margin-top: 12px; +} + +.shell-list { + margin: 0; + padding-left: 18px; + display: grid; + gap: 8px; + color: #cbd5e1; +} + +@media (max-width: 1100px) { + .app-shell { + grid-template-columns: 1fr; + } + + .app-sidebar { + border-right: 0; + border-bottom: 1px solid rgba(255,255,255,0.08); + } + + .shell-grid, + .shell-grid.two-columns { + grid-template-columns: 1fr; + } +}