Files
mek-tech-consulting/rack-infra-report.ejs
T

150 lines
8.5 KiB
Plaintext

<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Infrastructuur Rapport — <%= rack.name %> — Mek-Tech</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/neon.css">
<link rel="stylesheet" href="/css/dynamic.css">
<script src="https://unpkg.com/lucide@latest"></script>
<style>
.report-wrap{max-width:1000px;margin:0 auto;padding:2rem 1.5rem 3rem}
.report-header{text-align:center;padding:1.25rem 0 1.5rem;border-bottom:2px solid var(--accent);margin-bottom:1.5rem}
.report-header h1{font-size:1.5rem;margin:0 0 .25rem}
.report-header .sub{color:var(--text-muted);font-size:.85rem}
.section{margin-bottom:2rem}
.section h2{font-size:1rem;font-weight:700;border-bottom:1px solid var(--border);padding-bottom:.4rem;margin-bottom:.75rem;display:flex;align-items:center;gap:.4rem}
.summary-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:.5rem;margin-bottom:1rem}
.summary-card{background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:.65rem;text-align:center}
.summary-card .num{font-size:1.4rem;font-weight:800;color:var(--accent)}
.summary-card .lbl{font-size:.62rem;color:var(--text-muted);text-transform:uppercase}
.rack-table,.flow-table{width:100%;border-collapse:collapse;font-size:.75rem}
.rack-table th,.flow-table th{text-align:left;padding:.45rem .55rem;font-size:.62rem;color:var(--text-muted);text-transform:uppercase;border-bottom:1px solid var(--border);background:var(--bg-card)}
.rack-table td,.flow-table td{padding:.4rem .55rem;border-bottom:1px solid var(--border-light)}
.rack-visual{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:.4rem;margin-bottom:1rem}
.ru-card{background:var(--bg-card);border:1px solid var(--border);border-radius:6px;padding:.45rem .55rem;font-size:.68rem;border-left:3px solid var(--accent)}
.ru-card strong{display:block;font-size:.72rem}
.diagram-block{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:1rem;margin-bottom:1rem}
.diagram-block h3{font-size:.85rem;margin:0 0 .35rem}
.diagram-meta{font-size:.65rem;color:var(--text-muted);margin-bottom:.65rem}
.tag{display:inline-block;padding:.1rem .4rem;border-radius:999px;font-size:.58rem;font-weight:600;margin-right:.25rem}
.tag.flow{background:var(--cyan-bg);color:var(--cyan)}
.tag.landscape{background:var(--purple-bg);color:var(--purple)}
.tag.data{background:var(--green-bg);color:var(--green)}
.mini-flow{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.5rem}
.mini-node{padding:.25rem .45rem;border-radius:5px;font-size:.62rem;background:var(--bg);border:1px solid var(--border-light)}
.mini-arrow{color:var(--accent);font-size:.7rem}
.action-bar{display:flex;gap:.5rem;justify-content:center;margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--border);flex-wrap:wrap}
@media print{.navbar,.action-bar{display:none!important}}
</style>
</head>
<body>
<nav class="navbar"><div class="nav-inner"><a href="/" class="nav-brand"><img src="/img/logo.svg" class="nav-logo" alt="Mek-Tech"></a><div style="display:flex;gap:.4rem"><a href="/racks/<%= rack.id %>" class="nav-icon-btn">Rack</a><a href="/auth/logout" class="nav-icon-btn" style="color:var(--red)"><i data-lucide="log-out" style="width:15px;height:15px"></i></a></div></div></nav>
<div class="report-wrap">
<header class="report-header">
<h1>Gecombineerd Infrastructuur Rapport</h1>
<div class="sub"><%= rack.name %> · <%= rack.client_name %></div>
<div class="sub" style="margin-top:.2rem"><%= rack.location || '' %> <%= rack.datacenter ? '· ' + rack.datacenter : '' %> · <%= new Date().toLocaleDateString('nl-NL',{weekday:'long',year:'numeric',month:'long',day:'numeric'}) %></div>
</header>
<section class="section">
<h2><i data-lucide="server" style="width:16px;height:16px;color:var(--cyan)"></i> Rack Overzicht</h2>
<div class="summary-grid">
<div class="summary-card"><div class="num"><%= devices.length %></div><div class="lbl">Devices</div></div>
<div class="summary-card"><div class="num"><%= usedU %>/<%= rack.total_units || 42 %></div><div class="lbl">Rack Units</div></div>
<div class="summary-card"><div class="num"><%= Math.round(usedU/(rack.total_units||42)*100) %>%</div><div class="lbl">Bezetting</div></div>
<div class="summary-card"><div class="num"><%= diagrams.length %></div><div class="lbl">Diagrammen</div></div>
</div>
<div class="rack-visual">
<% devices.forEach(function(d){ %>
<div class="ru-card">
<strong>U<%= d.position_u %> — <%= d.name %></strong>
<%= d.manufacturer || '' %> <%= d.model || '' %> · <%= d.device_type %> · <%= d.height_u %>U
<% if (d.mgmt_ip) { %><br><span style="color:var(--accent);font-family:monospace;font-size:.62rem"><%= d.mgmt_ip %></span><% } %>
</div>
<% }) %>
<% if (devices.length === 0) { %><p style="color:var(--text-muted);font-size:.75rem">Geen devices in dit rack.</p><% } %>
</div>
<table class="rack-table">
<thead><tr><th>Pos</th><th>Naam</th><th>Type</th><th>Vendor / Model</th><th>IP</th><th>Status</th></tr></thead>
<tbody>
<% devices.forEach(function(d){ %>
<tr><td>U<%= d.position_u %></td><td><strong><%= d.name %></strong></td><td><%= d.device_type %></td><td><%= d.manufacturer || '—' %> <%= d.model || '' %></td><td><%= d.mgmt_ip || '—' %></td><td><%= d.status || 'active' %></td></tr>
<% }) %>
</tbody>
</table>
</section>
<% if (diagrams.length > 0) { %>
<section class="section">
<h2><i data-lucide="git-branch" style="width:16px;height:16px;color:var(--purple)"></i> Applicatie Flows & Landschap</h2>
<% diagrams.forEach(function(d){ %>
<div class="diagram-block">
<h3><%= d.name %></h3>
<div class="diagram-meta">
<span class="tag <%= d.diagram_type === 'application_flow' ? 'flow' : d.diagram_type === 'application_landscape' ? 'landscape' : 'data' %>"><%= d.typeLabel %></span>
<%= d.nodeCount %> componenten · <%= d.edgeCount %> flows · bijgewerkt <%= new Date(d.updated_at).toLocaleDateString('nl-NL') %>
</div>
<% if (Object.keys(d.layers || {}).length) { %>
<div style="font-size:.68rem;color:var(--text-muted);margin-bottom:.5rem">
<% Object.entries(d.layers).forEach(function(e){ %><span style="margin-right:.5rem"><strong><%= e[0] %>:</strong> <%= e[1] %></span><% }) %>
</div>
<% } %>
<% if (d.nodes && d.nodes.length) { %>
<div class="mini-flow">
<% d.nodes.slice(0,12).forEach(function(n,i){ %>
<% if (i > 0) { %><span class="mini-arrow">→</span><% } %>
<span class="mini-node" title="<%= n.type || '' %>"><%= n.label || n.type %><% if (n.rack_device_id) { %> <em style="color:var(--green)">●rack</em><% } %></span>
<% }) %>
<% if (d.nodes.length > 12) { %><span class="mini-node">+<%= d.nodes.length - 12 %> meer</span><% } %>
</div>
<% } %>
<% if (d.edges && d.edges.length) { %>
<table class="flow-table" style="margin-top:.75rem">
<thead><tr><th>#</th><th>Van</th><th>Naar</th><th>Protocol</th><th>Animatie</th></tr></thead>
<tbody>
<% d.edges.forEach(function(e,i){
var from = (d.nodes || []).find(function(n){ return n.id == e.from; });
var to = (d.nodes || []).find(function(n){ return n.id == e.to; });
%>
<tr>
<td><%= i+1 %></td>
<td><%= from ? from.label : e.from %></td>
<td>→ <%= to ? to.label : e.to %></td>
<td><%= e.protocol || '—' %></td>
<td><%= e.flow || 'off' %></td>
</tr>
<% }) %>
</tbody>
</table>
<% } %>
<a href="/architecture/<%= d.id %>" class="btn btn-secondary btn-small" style="margin-top:.65rem;display:inline-flex">Open in designer</a>
</div>
<% }) %>
</section>
<% } else { %>
<section class="section">
<h2><i data-lucide="git-branch" style="width:16px;height:16px;color:var(--purple)"></i> Applicatie Flows & Landschap</h2>
<p style="color:var(--text-muted);font-size:.78rem">Nog geen flows of landschap gekoppeld aan dit rack.</p>
<div style="display:flex;gap:.4rem;margin-top:.5rem;flex-wrap:wrap">
<a href="/racks/<%= rack.id %>/flow/new" class="btn btn-accent btn-small">+ Applicatie Flow</a>
<a href="/racks/<%= rack.id %>/landscape/new" class="btn btn-secondary btn-small">+ Applicatie Landschap</a>
</div>
</section>
<% } %>
<div class="action-bar">
<button class="btn btn-accent" onclick="window.print()">PDF / Printen</button>
<a href="/racks/<%= rack.id %>" class="btn btn-secondary">Terug naar Rack</a>
<a href="/racks/<%= rack.id %>/flow/new" class="btn btn-secondary">Flow toevoegen</a>
<a href="/racks/<%= rack.id %>/landscape/new" class="btn btn-secondary">Landschap toevoegen</a>
</div>
</div>
<script>lucide.createIcons();</script>
<script src="/js/theme.js"></script>
</body>
</html>