| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- @page "/"
- @using RackPeek.Domain.Graph.Serialisers
- @using RackPeek.Domain.Graph.UseCases
- @using RackPeek.Domain.Resources
- @using RackPeek.Domain.Resources.Hardware
- @using RackPeek.Domain.Resources.Services.UseCases
- @using RackPeek.Domain.Resources.SystemResources.UseCases
- @using Shared.Rcl.Components
- @using Shared.Rcl.Components.Graphs
- @inject GetSystemSummaryUseCase SystemSummaryUseCase
- @inject GetServiceSummaryUseCase ServiceSummaryUseCase
- @inject GetHardwareUseCaseSummary HardwareSummaryUseCase
- @inject BuildPhysicalTopologyUseCase TopologyUseCase
- @inject BuildLogicalGraphUseCase LogicalUseCase
- <PageTitle>Home</PageTitle>
- <div class="min-h-screen bg-zinc-950 text-zinc-200 font-mono p-6">
- @if (_loading)
- {
- <div class="text-zinc-500">loading summary…</div>
- }
- else
- {
- <!--
- Uniform-height card grid. Every card is `h-80` regardless of content;
- taller content scrolls inside. `grid-flow-dense` packs the wider
- diagram cards without leaving holes.
- -->
- <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 auto-rows-[20rem] grid-flow-dense gap-4">
- <!-- Totals -->
- <div class="@_cardClass">
- <div class="@_cardHeaderClass">Totals</div>
- <div class="@_cardBodyClass">
- <div class="grid grid-cols-2 gap-y-2 text-sm">
- <div class="hover:text-emerald-300">
- <NavLink href="hardware/tree">→ Hardware</NavLink>
- </div>
- <div class="text-right">@_hardware!.TotalHardware</div>
- <div class="hover:text-emerald-300">
- <NavLink href="systems/list">→ Systems</NavLink>
- </div>
- <div class="text-right">@_system!.TotalSystems</div>
- <div class="hover:text-emerald-300">
- <NavLink href="services/list">→ Services</NavLink>
- </div>
- <div class="text-right">@_service!.TotalServices</div>
- </div>
- </div>
- </div>
- <!-- Tools -->
- <div class="@_cardClass">
- <div class="@_cardHeaderClass">Tools</div>
- <div class="@_cardBodyClass">
- <ul class="space-y-2 text-sm">
- <li><NavLink href="visualise" class="block hover:text-emerald-300" data-testid="home-tool-visualise">→ Visualise</NavLink></li>
- <li><NavLink href="subnets" class="block hover:text-emerald-300" data-testid="home-tool-subnets">→ Subnet Browser</NavLink></li>
- <li><NavLink href="cli" class="block hover:text-emerald-300" data-testid="home-tool-cli">→ CLI Emulator</NavLink></li>
- <li><NavLink href="yaml" class="block hover:text-emerald-300" data-testid="home-tool-yaml">→ YAML Editor</NavLink></li>
- <li><NavLink href="ansible/inventory" class="block hover:text-emerald-300" data-testid="home-tool-ansible">→ Ansible Inventory Generator</NavLink></li>
- <li><NavLink href="ssh/export" class="block hover:text-emerald-300" data-testid="home-tool-ssh">→ SSH Config Export</NavLink></li>
- <li><NavLink href="hosts/export" class="block hover:text-emerald-300" data-testid="home-tool-hosts">→ Hosts File Export</NavLink></li>
- <li><NavLink href="docs" class="block hover:text-emerald-300" data-testid="home-tool-docs">→ Documentation</NavLink></li>
- </ul>
- </div>
- </div>
- <!-- Physical / Hardware topology -->
- <div class="@_cardClass sm:col-span-2 sm:row-span-2">
- <div class="flex items-center justify-between mb-3 flex-shrink-0">
- <div class="text-xs text-zinc-500 uppercase tracking-wider">Physical Topology</div>
- <NavLink href="visualise/topology"
- class="text-xs text-zinc-400 hover:text-emerald-400"
- data-testid="home-topology-open">
- open →
- </NavLink>
- </div>
- <div class="flex-1 min-h-0 overflow-hidden">
- <GraphView Source="@_topologySource" TestId="home-topology-preview"/>
- </div>
- </div>
- <!-- Logical / Services & Systems -->
- <div class="@_cardClass sm:col-span-2 sm:row-span-2">
- <div class="flex items-center justify-between mb-3 flex-shrink-0">
- <div class="text-xs text-zinc-500 uppercase tracking-wider">Logical View</div>
- <NavLink href="visualise/logical"
- class="text-xs text-zinc-400 hover:text-emerald-400"
- data-testid="home-logical-open">
- open →
- </NavLink>
- </div>
- <div class="flex-1 min-h-0 overflow-hidden">
- <GraphView Source="@_logicalSource" TestId="home-logical-preview"/>
- </div>
- </div>
- <!-- Tags (component renders its own internal header) -->
- <div class="@_cardClass">
- <div class="@_cardBodyClass">
- <TagListComponent/>
- </div>
- </div>
- <!-- Labels -->
- <div class="@_cardClass">
- <div class="@_cardBodyClass">
- <LabelListComponent/>
- </div>
- </div>
- <!-- Hardware breakdown -->
- <div class="@_cardClass">
- <div class="@_cardHeaderClass">Hardware</div>
- <div class="@_cardBodyClass">
- <ul class="space-y-2 text-sm">
- <li class="text-zinc-100">├─ Hardware (@_hardware!.TotalHardware)</li>
- @if (_hardware.HardwareByKind.Any())
- {
- <ul class="ml-4 mt-2 border-l border-zinc-800 pl-4 space-y-1">
- @foreach (var (kind, count) in _hardware.HardwareByKind.OrderByDescending(x => x.Value))
- {
- var pluralKind = Resource.KindToPlural(kind);
- <NavLink href="@($"/{Uri.EscapeDataString(pluralKind)}/list")" class="block">
- <li class="text-zinc-500 hover:text-emerald-300">
- └─ @pluralKind (@count)
- </li>
- </NavLink>
- }
- </ul>
- }
- </ul>
- </div>
- </div>
- <!-- Systems breakdown -->
- <div class="@_cardClass">
- <div class="@_cardHeaderClass">Systems</div>
- <div class="@_cardBodyClass">
- <ul class="space-y-3 text-sm">
- <li>
- <div class="text-zinc-100">├─ Total (@_system!.TotalSystems)</div>
- @if (_system.SystemsByType.Any())
- {
- <ul class="ml-4 mt-2 border-l border-zinc-800 pl-4 space-y-1">
- <li class="text-zinc-400">Types</li>
- @foreach (var (type, count) in _system.SystemsByType.OrderByDescending(x => x.Value))
- {
- <li class="text-zinc-500 hover:text-emerald-300">
- <NavLink href="@($"systems/list?type={Uri.EscapeDataString(type)}")" class="block">
- └─ @type (@count)
- </NavLink>
- </li>
- }
- </ul>
- }
- @if (_system.SystemsByOs.Any())
- {
- <ul class="ml-4 mt-2 border-l border-zinc-800 pl-4 space-y-1">
- <li class="text-zinc-400">Operating Systems</li>
- @foreach (var (os, count) in _system.SystemsByOs.OrderByDescending(x => x.Value))
- {
- <li class="text-zinc-500 hover:text-emerald-300">
- <NavLink href="@($"systems/list?os={Uri.EscapeDataString(os)}")" class="block">
- └─ @os (@count)
- </NavLink>
- </li>
- }
- </ul>
- }
- </li>
- </ul>
- </div>
- </div>
- <!-- Services breakdown -->
- <div class="@_cardClass">
- <div class="@_cardHeaderClass">Services</div>
- <div class="@_cardBodyClass">
- <ul class="text-sm">
- <li class="text-zinc-100">├─ Total (@_service!.TotalServices)</li>
- <li class="ml-4 text-zinc-500">└─ IP Addresses (@_service!.TotalIpAddresses)</li>
- </ul>
- </div>
- </div>
- </div>
- }
- </div>
- @code {
- private bool _loading = true;
- private SystemSummary? _system;
- private AllServicesSummary? _service;
- private HardwareSummary? _hardware;
- private string? _topologySource;
- private string? _logicalSource;
- private static readonly MermaidSerialiser _serialiser = new();
- // Shared card chrome — defined once so every card looks identical.
- // Card height is set by the parent grid's `auto-rows-[20rem]`; cards
- // that span multiple rows (e.g. diagram cards with `row-span-2`) get
- // the combined height plus gap automatically.
- private const string _cardClass =
- "border border-zinc-800 rounded-md p-4 bg-zinc-900/30 flex flex-col overflow-hidden";
- private const string _cardHeaderClass =
- "text-xs text-zinc-500 uppercase tracking-wider mb-3 flex-shrink-0";
- private const string _cardBodyClass =
- "flex-1 min-h-0 overflow-y-auto pr-1";
- protected override async Task OnInitializedAsync()
- {
- Task<SystemSummary> systemTask = SystemSummaryUseCase.ExecuteAsync();
- Task<AllServicesSummary> serviceTask = ServiceSummaryUseCase.ExecuteAsync();
- Task<HardwareSummary> hardwareTask = HardwareSummaryUseCase.ExecuteAsync();
- Task<RackPeek.Domain.Graph.Graph> topologyTask = TopologyUseCase.ExecuteAsync();
- Task<RackPeek.Domain.Graph.Graph> logicalTask = LogicalUseCase.ExecuteAsync();
- await Task.WhenAll(systemTask, serviceTask, hardwareTask, topologyTask, logicalTask);
- _system = systemTask.Result;
- _service = serviceTask.Result;
- _hardware = hardwareTask.Result;
- _topologySource = _serialiser.Serialise(topologyTask.Result);
- _logicalSource = _serialiser.Serialise(logicalTask.Result);
- _loading = false;
- }
- }
|