diff --git a/frontend/index.html b/frontend/index.html index 483865a..c71f45c 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,7 +3,7 @@ - + TV Dashboard diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 64a604f..061a8b7 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -53,7 +53,7 @@ function App() { const connected = !topology.error && !tasks.error && !calendar.error; return ( -
+
diff --git a/frontend/src/components/Calendar/CalendarView.tsx b/frontend/src/components/Calendar/CalendarView.tsx index a99629f..68a720e 100644 --- a/frontend/src/components/Calendar/CalendarView.tsx +++ b/frontend/src/components/Calendar/CalendarView.tsx @@ -28,73 +28,57 @@ export function CalendarView({ events }: CalendarViewProps) { return (
- {/* Summary bar */} -
- - {events.length} eventos esta - semana +
+ + {events.length} eventos esta semana
-
+
{!hasEvents ? ( -
- πŸ“… -

+

+ πŸ“… +

Sin eventos programados

-

+

Los prΓ³ximos eventos del calendario de Odoo aparecerΓ‘n aquΓ­

) : ( -
- {/* Today */} -
-

Hoy

-

{formatDate(today)}

+
+
+

Hoy

+

{formatDate(today)}

{todayEvents.length === 0 ? ( -

Sin eventos

- ) : ( -
- {todayEvents.map((e) => ( - - ))} -
- )} -
- - {/* Tomorrow */} -
-

MaΓ±ana

-

- {formatDate(tomorrow)} -

- {tomorrowEvents.length === 0 ? ( -

Sin eventos

- ) : ( -
- {tomorrowEvents.map((e) => ( - - ))} -
- )} -
- - {/* This week */} -
-

Esta semana

-

PrΓ³ximos dΓ­as

- {laterEvents.length === 0 ? ( -

Sin eventos

+

Sin eventos

) : (
+ {todayEvents.map((e) => )} +
+ )} +
+
+

MaΓ±ana

+

{formatDate(tomorrow)}

+ {tomorrowEvents.length === 0 ? ( +

Sin eventos

+ ) : ( +
+ {tomorrowEvents.map((e) => )} +
+ )} +
+
+

Esta semana

+

PrΓ³ximos dΓ­as

+ {laterEvents.length === 0 ? ( +

Sin eventos

+ ) : ( +
{laterEvents.map((e) => ( -
- +
+ {formatDate(e.start)} {e.name} diff --git a/frontend/src/components/Layout/Header.tsx b/frontend/src/components/Layout/Header.tsx index 2537411..9293092 100644 --- a/frontend/src/components/Layout/Header.tsx +++ b/frontend/src/components/Layout/Header.tsx @@ -27,22 +27,22 @@ export function Header({ viewName, connected }: HeaderProps) { }); return ( -
-
-

+
+
+

Consultoria AS

- | - {viewName} + | + {viewName}
-
- {dateStr} - {timeStr} +
+ {dateStr} + {timeStr}
- + {connected ? "Conectado" : "Sin conexiΓ³n"}
diff --git a/frontend/src/components/Tasks/KanbanBoard.tsx b/frontend/src/components/Tasks/KanbanBoard.tsx index c02db23..862e988 100644 --- a/frontend/src/components/Tasks/KanbanBoard.tsx +++ b/frontend/src/components/Tasks/KanbanBoard.tsx @@ -22,45 +22,41 @@ function ProjectRow({ project }: { project: Project }) { if (totalTasks === 0) return null; return ( -
- {/* Project header */} -
-

+
+
+

{project.name}

- - {totalTasks} tareas + + {totalTasks}
- {/* Progress bar */} -
+
{stages.map(([stageName, tasks], i) => { const pct = (tasks.length / totalTasks) * 100; if (pct < 1) return null; return (
); })}
- {/* Stage chips */} -
+
{stages.map(([stageName, tasks], i) => ( - {stageName} - {tasks.length} + {stageName} + {tasks.length} ))}
@@ -81,21 +77,18 @@ export function KanbanBoard({ projects }: KanbanBoardProps) { return (
- {/* Summary bar */} -
- +
+ {activeProjects.length} proyectos - activos - | - - {totalTasks} tareas totales + | + + {totalTasks} tareas
- {/* Projects grid */} -
-
+
+
{activeProjects.map((project) => ( ))} diff --git a/frontend/src/components/Topology/NetworkGraph.tsx b/frontend/src/components/Topology/NetworkGraph.tsx index 9054d33..ad75435 100644 --- a/frontend/src/components/Topology/NetworkGraph.tsx +++ b/frontend/src/components/Topology/NetworkGraph.tsx @@ -17,16 +17,15 @@ function VmPill({ node }: { node: NetworkNode }) { const dotColor = isUp ? "bg-success" : "bg-danger"; return ( -
- - {node.name} - {node.ip.split(".").pop()} +
+ + {node.name}
); } -/* ── Full node card (infrastructure + proxmox) ───────────── */ -function InfraCard({ node, isCenter }: { node: NetworkNode; isCenter?: boolean }) { +/* ── Infrastructure card ─────────────────────────────────── */ +function InfraCard({ node }: { node: NetworkNode }) { const [showPass, setShowPass] = useState(false); const isUp = node.status === "up"; const borderColor = isUp ? "border-success/40" : "border-danger/40"; @@ -35,56 +34,42 @@ function InfraCard({ node, isCenter }: { node: NetworkNode; isCenter?: boolean } return (
setShowPass((p) => !p)} > -
- {ICON_MAP[node.icon] || "πŸ“¦"} +
+ {ICON_MAP[node.icon] || "πŸ“¦"}
-

{node.name}

-

{node.ip}

+

{node.name}

+

{node.ip}

- +
- {node.username && ( -

+ {showPass && node.username && ( +

{node.username} - {node.password && ( - - {showPass ? ` / ${node.password}` : " / β€’β€’β€’β€’β€’β€’"} - - )} + {node.password && / {node.password}}

)} - {node.public_url && ( -

{node.public_url}

+ {showPass && node.public_url && ( +

{node.public_url}

)}
); } -/* ── Proxmox server with its VMs ─────────────────────────── */ +/* ── Proxmox server column ───────────────────────────────── */ function ProxmoxColumn({ server, vms }: { server: NetworkNode; vms: NetworkNode[] }) { - const vmCount = vms.length; const upCount = vms.filter((v) => v.status === "up").length; return ( -
- {/* Server card */} +
- - {/* Vertical connector */} -
- - {/* VM count badge */} -
- - {upCount}/{vmCount} VMs - -
- - {/* VM grid */} -
+
+ + {upCount}/{vms.length} activos + +
{vms.map((vm) => ( ))} @@ -93,14 +78,13 @@ function ProxmoxColumn({ server, vms }: { server: NetworkNode; vms: NetworkNode[ ); } -/* ── Vertical connector line ─────────────────────────────── */ +/* ── Vertical line ───────────────────────────────────────── */ function VLine() { - return
; + return
; } -/* ── Main topology graph ─────────────────────────────────── */ +/* ── Main topology ───────────────────────────────────────── */ export function NetworkGraph({ nodes }: NetworkGraphProps) { - // Find nodes by role const findByName = (name: string) => nodes.find((n) => n.name === name); const modem = findByName("Router Telmex"); const firewall = findByName("Firewall OPNsense"); @@ -115,7 +99,6 @@ export function NetworkGraph({ nodes }: NetworkGraphProps) { n.name !== "Switch Cisco" ); - // Group VMs by parent const vmsByParent = new Map(); for (const node of nodes) { if ((node.type === "vm" || node.type === "ct") && node.parent) { @@ -131,23 +114,22 @@ export function NetworkGraph({ nodes }: NetworkGraphProps) { return (
{/* Summary bar */} -
-
- - +
+
+ + {onlineCount} online
-
- - +
+ + {total - onlineCount} offline
- | - - {proxmoxServers.length} Proxmox - Β· + | + + {proxmoxServers.length} Proxmox Β·{" "} {nodes.filter((n) => n.type === "vm" || n.type === "ct").length} {" "} @@ -155,65 +137,48 @@ export function NetworkGraph({ nodes }: NetworkGraphProps) {
- {/* Diagram area */} -
-
+ {/* Diagram */} +
+ {/* Top chain: Modem β†’ Firewall β†’ Switch */} +
+ {modem && } + β†’ + {firewall && } + β†’ + {switchNode && } +
- {/* ── Level 1: Modem ── */} - {modem && } - + - {/* ── Level 2: Firewall ── */} - {firewall && } - + {/* Horizontal branch line */} +
- {/* ── Level 3: Switch ── */} - {switchNode && } - - - {/* ── Branch lines to Proxmox servers ── */} -
- {/* Horizontal bar spanning all columns */} -
- {/* Horizontal line */} -
- - {/* Proxmox columns */} -
- {proxmoxServers.map((server) => { - const vms = vmsByParent.get(server.name) || []; - return ( -
- {/* Vertical connector from horizontal bar */} -
- -
- ); - })} + {/* Proxmox columns */} +
+ {proxmoxServers.map((server) => { + const vms = vmsByParent.get(server.name) || []; + return ( +
+
+
+ ); + })} +
+ + {/* Other devices */} + {otherDevices.length > 0 && ( +
+

+ Otros dispositivos +

+
+ {otherDevices.map((node) => ( + + ))}
- - {/* ── Other devices ── */} - {otherDevices.length > 0 && ( -
-

- Otros dispositivos conectados al switch -

-
- {otherDevices.map((node) => ( - - ))} -
-
- )} -
+ )}
); diff --git a/frontend/src/index.css b/frontend/src/index.css index 50f4045..4c66f66 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -21,7 +21,7 @@ } html { - font-size: 24px; + font-size: clamp(14px, 1.15vw, 24px); } body { @@ -30,11 +30,10 @@ body { font-family: "Inter", system-ui, -apple-system, sans-serif; margin: 0; overflow: hidden; - width: 3840px; - height: 2160px; + width: 100vw; + height: 100vh; } -/* Smooth scrollbar for overflow areas */ ::-webkit-scrollbar { width: 6px; }