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 (
+
+
+
+ - Frontend inventory collegato al backend.
+ - Tree gerarchico e context panel attivi.
+ - Navigazione tra parent, children e relazioni disponibile.
+
+
+
+
+ - Reintegro layout persistente.
+ - Tab dedicate per viste future.
+ - Preparazione vista topology multilivello.
+
+
+
+
+ - La discovery avanzata è ancora in fase iniziale.
+ - I badge e il contesto nodo sono MVP, non finali.
+ - Serve reintegro progressivo delle sezioni storiche.
+
+
+
+ );
+}
+
+function DiscoveryTab() {
+ return (
+
+
+
+ - Seed scan subnet.
+ - Classificazione host e object binding.
+ - Correlazione con relazioni inventory.
+
+
+
+
+ - SNMP collector — WIP.
+ - LLDP ingestion — WIP.
+ - Port mapping inference — WIP.
+
+
+
+ );
+}
+
+function TopologyTab() {
+ return (
+
+
+
+ - Vista fisica dispositivi.
+ - Vista logica host / VM / container / app.
+ - Layer networking e dipendenze.
+
+
+
+
+ - Drag & drop dei contenitori.
+ - Link fisici e logici cliccabili.
+ - Focus node e highlight dei vicini.
+
+
+
+ );
+}
+
+function WipTab() {
+ return (
+
+
+
+ - Placeholder per pannelli legacy reintegrati.
+ - Spazio riservato ai blocchi non ancora consolidati.
+ - Promemoria tecnico per le parti temporanee.
+
+
+
+
+ - Non rimuovere più la shell quando si aggiungono nuove viste.
+ - Le nuove sezioni devono entrare come tab del workspace.
+ - Persistenza dello stato UI da migliorare nei prossimi step.
+
+
+
+ );
+}
function App() {
- return ;
+ const [activeTab, setActiveTab] = useState('inventory');
+ const currentTab = tabs.find((tab) => tab.key === activeTab)!;
+
+ return (
+
+ );
}
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;
+ }
+}