Files
MeterVault/src/App/Components/Pages/Dashboard.razor
T
schmidt.florian 85d650a8f5
ci / build-test (push) Successful in 1m12s
Fix: enable global interactivity so dialogs/selects actually work
Editing a meter (any admin dialog, select dropdown, snackbar, dark-mode toggle)
did nothing: pages declared @rendermode InteractiveServer individually, but
MainLayout — which hosts MudDialogProvider/MudPopoverProvider/MudSnackbarProvider
— was rendered by <Routes>, which was static. Inline MudDialogs and MudSelect
popovers render through those providers, so with the providers non-interactive no
dialog could ever open.

Set the render mode on <Routes> and <HeadOutlet> in App.razor (global
interactivity) and removed the now-redundant per-page @rendermode declarations
(they would otherwise throw "parent already has a render mode").

Why it slipped through: the integration render tests only issue a GET (static
prerender), which never exercises the SignalR circuit. Verified this fix with a
real headless-browser run (Playwright): the meter edit dialog opens and the
"Sub-meter of (upstream meters)" multi-select opens with options — proving the
layout providers are now interactive. 116 tests still green.

Claude-Session: https://claude.ai/code/session_01Lz2RqAsnQhetqWNoCDfexK
2026-07-14 14:22:15 +02:00

102 lines
4.2 KiB
Plaintext

@page "/"
@inject DashboardService Dash
<PageTitle>MeterVault — Overview</PageTitle>
<MudText Typo="Typo.h4" Class="mb-4">Overview</MudText>
@if (_summary is null)
{
<MudProgressLinear Indeterminate="true" Color="Color.Primary" />
}
else
{
<MudGrid>
<MudItem xs="12" sm="4">
<MudPaper Class="pa-4" Elevation="2">
<MudText Typo="Typo.overline" Color="Color.Secondary">This month</MudText>
<MudText Typo="Typo.h5">@Format.Euro(_summary.Month.Current)</MudText>
<DeltaChip Kpi="_summary.Month" />
</MudPaper>
</MudItem>
<MudItem xs="12" sm="4">
<MudPaper Class="pa-4" Elevation="2">
<MudText Typo="Typo.overline" Color="Color.Secondary">This year</MudText>
<MudText Typo="Typo.h5">@Format.Euro(_summary.Year.Current)</MudText>
<DeltaChip Kpi="_summary.Year" />
</MudPaper>
</MudItem>
<MudItem xs="12" sm="4">
<MudPaper Class="pa-4" Elevation="2">
<MudText Typo="Typo.overline" Color="Color.Secondary">Latest month with data</MudText>
<MudText Typo="Typo.h5">@Format.Euro(_summary.LatestMonthCost)</MudText>
</MudPaper>
</MudItem>
<MudItem xs="12" md="6">
<MudPaper Class="pa-4" Elevation="2" Style="height:100%">
<MudText Typo="Typo.h6" Class="mb-2">What costs most (this year)</MudText>
@if (_breakdown is { Count: > 0 })
{
<CategoryDonut Slices="_breakdown" />
<MudSimpleTable Dense="true" Hover="true" Class="mt-2">
<tbody>
@foreach (var slice in _breakdown)
{
<tr>
<td>@slice.Name</td>
<td style="text-align:right">@Format.Euro(slice.Cost)</td>
</tr>
}
</tbody>
</MudSimpleTable>
}
else
{
<MudText Typo="Typo.body2" Color="Color.Secondary">No cost data yet — import a sheet or add tariffs.</MudText>
}
</MudPaper>
</MudItem>
<MudItem xs="12" md="6">
<MudPaper Class="pa-4" Elevation="2" Style="height:100%">
<MudText Typo="Typo.h6" Class="mb-2">What cost more / less (year vs last year)</MudText>
<MudSimpleTable Dense="true" Hover="true">
<thead>
<tr><th>Category</th><th style="text-align:right">Now</th><th style="text-align:right">Prev</th><th style="text-align:right">Δ</th></tr>
</thead>
<tbody>
@foreach (var row in _difference)
{
<tr>
<td>@row.Name</td>
<td style="text-align:right">@Format.Euro(row.Current)</td>
<td style="text-align:right">@Format.Euro(row.Previous)</td>
<td style="text-align:right" class="@(row.Delta >= 0 ? "mv-up" : "mv-down")">
@Format.DirectionIcon(Math.Sign(row.Delta)) @Format.Euro(Math.Abs(row.Delta))
</td>
</tr>
}
</tbody>
</MudSimpleTable>
</MudPaper>
</MudItem>
</MudGrid>
}
@code {
private DashboardSummary? _summary;
private IReadOnlyList<CategorySlice> _breakdown = [];
private IReadOnlyList<DifferenceRow> _difference = [];
protected override async Task OnInitializedAsync()
{
var asOf = DateOnly.FromDateTime(DateTime.UtcNow);
_summary = await Dash.GetSummaryAsync(asOf);
var yearStart = new DateOnly(asOf.Year, 1, 1);
_breakdown = await Dash.GetCategoryBreakdownAsync(yearStart, asOf.AddMonths(1));
_difference = await Dash.GetCategoryDifferenceAsync(yearStart, yearStart.AddYears(-1), asOf.AddMonths(1));
}
}