ui dashboard cleanup
This commit is contained in:
@@ -1,28 +1,8 @@
|
||||
<MudNavMenu>
|
||||
<MudNavMenu>
|
||||
<MudNavLink Href="/" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Dashboard">
|
||||
Dashboard
|
||||
</MudNavLink>
|
||||
|
||||
<MudNavLink Href="/portfolio" Icon="@Icons.Material.Filled.Inventory2">
|
||||
Portfolio
|
||||
</MudNavLink>
|
||||
|
||||
<MudNavLink Href="/analytics" Icon="@Icons.Material.Filled.Analytics">
|
||||
Analytics
|
||||
</MudNavLink>
|
||||
|
||||
<MudNavLink Href="/reports" Icon="@Icons.Material.Filled.DocumentScanner">
|
||||
Reports
|
||||
</MudNavLink>
|
||||
|
||||
<MudDivider Class="my-2" />
|
||||
|
||||
<MudNavLink Href="/settings" Icon="@Icons.Material.Filled.Settings">
|
||||
Settings
|
||||
</MudNavLink>
|
||||
|
||||
<MudNavLink Href="/" Icon="@Icons.Material.Filled.Help">
|
||||
Help
|
||||
<MudNavLink Href="/operations" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.Assessment">
|
||||
Operations
|
||||
</MudNavLink>
|
||||
</MudNavMenu>
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
@page "/counter"
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<PageTitle>Counter</PageTitle>
|
||||
|
||||
<h1>Counter</h1>
|
||||
|
||||
<p role="status">Current count: @currentCount</p>
|
||||
|
||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
||||
|
||||
@code {
|
||||
private int currentCount = 0;
|
||||
|
||||
private void IncrementCount()
|
||||
{
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
@@ -1,310 +1,138 @@
|
||||
@page "/"
|
||||
@using QuantEngine.Core.Models
|
||||
@using QuantEngine.Core.Interfaces
|
||||
@inject NavigationManager NavManager
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IPostgresqlHistorySnapshotReader HistoryReader
|
||||
@using QuantEngine.Core.Infrastructure
|
||||
@inject IWebHostEnvironment Environment
|
||||
|
||||
<PageTitle>Quant Engine - Dashboard</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h4" Class="mb-4">Quant Engine Dashboard</MudText>
|
||||
<MudText Typo="Typo.h4" Class="mb-2">Quant Engine</MudText>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary" Class="mb-4">
|
||||
루트 화면은 운영 진입점입니다. 가짜 성과 수치 없이 현재 스냅샷 상태와 리포트 경로만 보여줍니다.
|
||||
</MudText>
|
||||
|
||||
<!-- KPI Cards -->
|
||||
<MudGrid Class="mb-4">
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Active Positions</MudText>
|
||||
<MudText Typo="Typo.h5" Class="mt-2">@activePositions</MudText>
|
||||
<MudText Typo="Typo.caption" Class="mt-1">from history snapshot</MudText>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Operational Report</MudText>
|
||||
<MudText Typo="Typo.h6" Class="mt-2">@ReportStateLabel</MudText>
|
||||
<MudText Typo="Typo.caption">@ReportPath</MudText>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Portfolio Value</MudText>
|
||||
<MudText Typo="Typo.h5" Class="mt-2">@portfolioValueLabel</MudText>
|
||||
<MudText Typo="Typo.caption" Class="mt-1">PostgreSQL snapshot</MudText>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Sections</MudText>
|
||||
<MudText Typo="Typo.h6" Class="mt-2">@SectionCountLabel</MudText>
|
||||
<MudText Typo="Typo.caption">Temp/operational_report.json</MudText>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Signal Quality</MudText>
|
||||
<MudText Typo="Typo.h5" Class="mt-2">@signalQualityLabel</MudText>
|
||||
<MudText Typo="Typo.caption" Class="mt-1">decision_result_history</MudText>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">System Status</MudText>
|
||||
<MudChip Color="Color.Success" Icon="@Icons.Material.Filled.Check" Class="mt-2">@dbStatusLabel</MudChip>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Primary Route</MudText>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Href="/operations" Class="mt-2">
|
||||
Open Operations
|
||||
</MudButton>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
<!-- Market Overview -->
|
||||
<MudGrid Class="mb-4">
|
||||
<MudItem xs="12" md="6">
|
||||
<MudCard>
|
||||
<MudItem xs="12" md="7">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Market Status</MudText>
|
||||
<MudText Typo="Typo.h6">Current State</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudStack Spacing="2">
|
||||
<MudText Typo="Typo.body2">
|
||||
<strong>Market Regime:</strong> <MudChip Size="Size.Small" Color="Color.Warning">@marketRegimeLabel</MudChip>
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2">
|
||||
<strong>Volatility:</strong> @volatilityLabel
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2">
|
||||
<strong>Cash Position:</strong> @cashPositionLabel
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2">
|
||||
<strong>Last Updated:</strong> @lastUpdatedLabel
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2"><strong>Status:</strong> <MudChip Size="Size.Small" Color="@ReportChipColor">@ReportChipLabel</MudChip></MudText>
|
||||
<MudText Typo="Typo.body2"><strong>Generated:</strong> @GeneratedAtLabel</MudText>
|
||||
<MudText Typo="Typo.body2"><strong>Source:</strong> @SourceLabel</MudText>
|
||||
<MudText Typo="Typo.body2"><strong>Decision feed:</strong> @DecisionFeedLabel</MudText>
|
||||
<MudText Typo="Typo.body2"><strong>Factor feed:</strong> @FactorFeedLabel</MudText>
|
||||
<MudText Typo="Typo.body2"><strong>Raw feed:</strong> @RawFeedLabel</MudText>
|
||||
</MudStack>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" md="6">
|
||||
<MudCard>
|
||||
<MudItem xs="12" md="5">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">System Health</MudText>
|
||||
<MudText Typo="Typo.h6">Routing Notes</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudStack Spacing="2">
|
||||
<MudText Typo="Typo.body2">
|
||||
<strong>Database:</strong>
|
||||
<MudChip Size="Size.Small" Color="Color.Success">@databaseLabel</MudChip>
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2">
|
||||
<strong>DB History Feed:</strong>
|
||||
<MudChip Size="Size.Small" Color="Color.Success">@historyFeedLabel</MudChip>
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2">
|
||||
<strong>Signal Generator:</strong>
|
||||
<MudChip Size="Size.Small" Color="Color.Info">@signalGeneratorLabel</MudChip>
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2">
|
||||
<strong>API Uptime:</strong> 99.8%
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2">- 운영 데이터는 snapshot 우선입니다.</MudText>
|
||||
<MudText Typo="Typo.body2">- Excel/GAS 의존 문구는 운영 경로에서 제거 대상입니다.</MudText>
|
||||
<MudText Typo="Typo.body2">- 숫자는 provenance 없으면 표시하지 않습니다.</MudText>
|
||||
</MudStack>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
<!-- Performance Metrics -->
|
||||
<MudCard Class="mb-4">
|
||||
<MudCard>
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Performance Metrics</MudText>
|
||||
<MudText Typo="Typo.h6">Coverage Summary</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudStack Spacing="3">
|
||||
<MudStack Row="true" Spacing="3">
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudText Typo="Typo.body2"><strong>YTD Return</strong></MudText>
|
||||
<MudText Typo="Typo.h6" Color="Color.Success">@ytdReturnLabel</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudText Typo="Typo.body2"><strong>Sharpe Ratio</strong></MudText>
|
||||
<MudText Typo="Typo.h6">@sharpeLabel</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudText Typo="Typo.body2"><strong>Max Drawdown</strong></MudText>
|
||||
<MudText Typo="Typo.h6" Color="Color.Warning">@maxDrawdownLabel</MudText>
|
||||
</MudItem>
|
||||
</MudStack>
|
||||
|
||||
<MudStack Row="true" Spacing="3">
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudText Typo="Typo.body2"><strong>Win Rate</strong></MudText>
|
||||
<MudText Typo="Typo.h6" Color="Color.Success">@winRateLabel</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudText Typo="Typo.body2"><strong>Profit Factor</strong></MudText>
|
||||
<MudText Typo="Typo.h6">@profitFactorLabel</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudText Typo="Typo.body2"><strong>Trades This Month</strong></MudText>
|
||||
<MudText Typo="Typo.h6">@tradesThisMonthLabel</MudText>
|
||||
</MudItem>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<!-- Algorithm Status -->
|
||||
<MudCard Class="mb-4">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Algorithm Status (v9 Hardening)</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudTable Items="algorithmPhases" Hover="true" Striped="true" Dense="true">
|
||||
@if (Sections.Count == 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined">
|
||||
DATA_MISSING: operational_report.json이 비어 있거나 아직 생성되지 않았습니다.
|
||||
</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTable Items="Sections" Hover="true" Striped="true" Dense="true">
|
||||
<HeaderContent>
|
||||
<MudTh>Phase</MudTh>
|
||||
<MudTh>Name</MudTh>
|
||||
<MudTh>Status</MudTh>
|
||||
<MudTh>Progress</MudTh>
|
||||
<MudTh>Title</MudTh>
|
||||
<MudTh>Preview</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context["Phase"]</MudTd>
|
||||
<MudTd>@context["Name"]</MudTd>
|
||||
<MudTd>
|
||||
@{
|
||||
var status = context["Status"].ToString();
|
||||
var chipColor = "Calibrated".Equals(status) ? Color.Success : Color.Info;
|
||||
}
|
||||
<MudChip Size="Size.Small" Color="@chipColor">@status</MudChip>
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
@{
|
||||
var progress = context.TryGetValue("Progress", out var p) ? p?.ToString() ?? string.Empty : string.Empty;
|
||||
var progressValue = int.TryParse(progress.Replace("%", ""), out var val) ? val : 0;
|
||||
}
|
||||
<MudProgressLinear Value="@progressValue" Size="Size.Small" />
|
||||
</MudTd>
|
||||
<MudTd>@context.Name</MudTd>
|
||||
<MudTd>@context.Title</MudTd>
|
||||
<MudTd>@context.Preview</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<!-- Live Signal Feed -->
|
||||
<MudCard>
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Recent Signals (Live Feed)</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudTable Items="recentSignals" Hover="true" Striped="true" Dense="true">
|
||||
<HeaderContent>
|
||||
<MudTh>Timestamp</MudTh>
|
||||
<MudTh>Ticker</MudTh>
|
||||
<MudTh>Signal</MudTh>
|
||||
<MudTh>Score</MudTh>
|
||||
<MudTh>Style</MudTh>
|
||||
<MudTh>Status</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context["Timestamp"]</MudTd>
|
||||
<MudTd><strong>@context["Ticker"]</strong></MudTd>
|
||||
<MudTd>
|
||||
@{
|
||||
var signal = context["Signal"].ToString();
|
||||
var signalColor = "BUY".Equals(signal) ? Color.Success : Color.Warning;
|
||||
}
|
||||
<MudChip Size="Size.Small" Color="@signalColor">@signal</MudChip>
|
||||
</MudTd>
|
||||
<MudTd>@context["Score"]</MudTd>
|
||||
<MudTd>@context["Style"]</MudTd>
|
||||
<MudTd>
|
||||
<MudChip Size="Size.Small" Variant="Variant.Text">@context["Status"]</MudChip>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
@code {
|
||||
private List<Dictionary<string, object>> algorithmPhases = new();
|
||||
private List<Dictionary<string, object>> recentSignals = new();
|
||||
private string activePositions = "0";
|
||||
private string portfolioValueLabel = "n/a";
|
||||
private string signalQualityLabel = "n/a";
|
||||
private string dbStatusLabel = "Pending";
|
||||
private string marketRegimeLabel = "PENDING";
|
||||
private string volatilityLabel = "n/a";
|
||||
private string cashPositionLabel = "n/a";
|
||||
private string lastUpdatedLabel = "n/a";
|
||||
private string databaseLabel = "Pending";
|
||||
private string historyFeedLabel = "Pending";
|
||||
private string signalGeneratorLabel = "Pending";
|
||||
private string ytdReturnLabel = "n/a";
|
||||
private string sharpeLabel = "n/a";
|
||||
private string maxDrawdownLabel = "n/a";
|
||||
private string winRateLabel = "n/a";
|
||||
private string profitFactorLabel = "n/a";
|
||||
private string tradesThisMonthLabel = "0";
|
||||
private readonly List<OperationalReportSection> Sections = new();
|
||||
private string ReportStateLabel = "DATA_MISSING";
|
||||
private string ReportChipLabel = "DATA_MISSING";
|
||||
private Color ReportChipColor = Color.Warning;
|
||||
private string SectionCountLabel = "0";
|
||||
private string GeneratedAtLabel = "n/a";
|
||||
private string SourceLabel = "n/a";
|
||||
private string DecisionFeedLabel = "DISCONNECTED";
|
||||
private string FactorFeedLabel = "DISCONNECTED";
|
||||
private string RawFeedLabel = "DISCONNECTED";
|
||||
private string ReportPath = "n/a";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
await LoadHistoryAsync();
|
||||
}
|
||||
|
||||
private async Task LoadHistoryAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var decisions = await HistoryReader.ReadAsync("decision_result_history", 5);
|
||||
activePositions = decisions.Count.ToString();
|
||||
signalQualityLabel = decisions.Count > 0 ? "snapshot" : "n/a";
|
||||
dbStatusLabel = decisions.Count > 0 ? "Connected" : "Empty";
|
||||
databaseLabel = dbStatusLabel;
|
||||
historyFeedLabel = decisions.Count > 0 ? "Active" : "Pending";
|
||||
signalGeneratorLabel = decisions.Count > 0 ? "Snapshot-driven" : "Pending";
|
||||
marketRegimeLabel = decisions.Count > 0 ? "SNAPSHOT" : "PENDING";
|
||||
volatilityLabel = decisions.Count > 0 ? "Snapshot-derived" : "n/a";
|
||||
cashPositionLabel = decisions.Count > 0 ? "Snapshot-derived" : "n/a";
|
||||
lastUpdatedLabel = decisions.Count > 0
|
||||
? (decisions[0].TryGetValue("decided_at", out var decidedAt) ? decidedAt?.ToString() ?? "n/a" : "n/a")
|
||||
: "n/a";
|
||||
ytdReturnLabel = decisions.Count > 0 ? "snapshot" : "n/a";
|
||||
sharpeLabel = decisions.Count > 0 ? "snapshot" : "n/a";
|
||||
maxDrawdownLabel = decisions.Count > 0 ? "snapshot" : "n/a";
|
||||
winRateLabel = decisions.Count > 0 ? "snapshot" : "n/a";
|
||||
profitFactorLabel = decisions.Count > 0 ? "snapshot" : "n/a";
|
||||
tradesThisMonthLabel = decisions.Count.ToString();
|
||||
recentSignals = decisions.Select(row => new Dictionary<string, object>
|
||||
{
|
||||
{ "Timestamp", row.TryGetValue("decided_at", out var decidedAt) ? decidedAt?.ToString() ?? "" : "" },
|
||||
{ "Ticker", row.TryGetValue("instrument_id", out var ticker) ? ticker?.ToString() ?? "" : "" },
|
||||
{ "Signal", row.TryGetValue("action", out var action) ? action?.ToString() ?? "" : "" },
|
||||
{ "Score", row.TryGetValue("score", out var score) ? score?.ToString() ?? "" : "" },
|
||||
{ "Style", row.TryGetValue("source_version", out var sourceVersion) ? sourceVersion?.ToString() ?? "" : "" },
|
||||
{ "Status", row.TryGetValue("gate", out var gate) ? gate?.ToString() ?? "" : "" }
|
||||
}).ToList();
|
||||
|
||||
var rawCount = (await HistoryReader.ReadAsync("market_raw_history", 1)).Count;
|
||||
var factorCount = (await HistoryReader.ReadAsync("factor_output_history", 1)).Count;
|
||||
var gapCount = (await HistoryReader.ReadAsync("market_vs_engine_gap_history", 1)).Count;
|
||||
portfolioValueLabel = rawCount > 0 ? "snapshot" : "n/a";
|
||||
|
||||
algorithmPhases = new()
|
||||
{
|
||||
new() { { "Phase", "P0" }, { "Name", "History Contract" }, { "Status", "Calibrated" }, { "Progress", "100%" } },
|
||||
new() { { "Phase", "P1" }, { "Name", "PostgreSQL Store" }, { "Status", rawCount > 0 ? "Active" : "Pending" }, { "Progress", rawCount > 0 ? "100%" : "0%" } },
|
||||
new() { { "Phase", "P2" }, { "Name", "Factor Output History" }, { "Status", factorCount > 0 ? "Active" : "Pending" }, { "Progress", factorCount > 0 ? "100%" : "0%" } },
|
||||
new() { { "Phase", "P3" }, { "Name", "Decision Result History" }, { "Status", recentSignals.Count > 0 ? "Active" : "Pending" }, { "Progress", recentSignals.Count > 0 ? "100%" : "0%" } },
|
||||
new() { { "Phase", "P4" }, { "Name", "Gap History" }, { "Status", gapCount > 0 ? "Active" : "Pending" }, { "Progress", gapCount > 0 ? "100%" : "0%" } }
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
algorithmPhases = new()
|
||||
{
|
||||
new() { { "Phase", "P0" }, { "Name", "History Contract" }, { "Status", "Pending" }, { "Progress", "0%" } }
|
||||
};
|
||||
recentSignals = new();
|
||||
}
|
||||
ReportPath = Path.GetFullPath(Path.Combine(Environment.ContentRootPath, "..", "..", "..", "Temp", "operational_report.json"));
|
||||
var report = OperationalReportLoader.Load(ReportPath);
|
||||
Sections.AddRange(report.Sections);
|
||||
SectionCountLabel = report.SectionCount.ToString();
|
||||
GeneratedAtLabel = report.GeneratedAt;
|
||||
SourceLabel = report.SourceJson;
|
||||
ReportStateLabel = Sections.Count > 0 ? "READY" : "DATA_MISSING";
|
||||
ReportChipLabel = Sections.Count > 0 ? "READY" : "DATA_MISSING";
|
||||
ReportChipColor = Sections.Count > 0 ? Color.Success : Color.Warning;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
@page "/operations"
|
||||
@using QuantEngine.Core.Infrastructure
|
||||
@inject IWebHostEnvironment Environment
|
||||
|
||||
<PageTitle>Quant Engine - Operations</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h4" Class="mb-2">Operational Report</MudText>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary" Class="mb-4">
|
||||
이 페이지는 `Temp/operational_report.json`만 읽습니다. DB 연결과 무관하게 동일한 결과를 보여주는 운영 고정 화면입니다.
|
||||
</MudText>
|
||||
|
||||
<MudGrid Class="mb-4">
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Schema</MudText>
|
||||
<MudText Typo="Typo.h6" Class="mt-2">@SchemaVersion</MudText>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Sections</MudText>
|
||||
<MudText Typo="Typo.h6" Class="mt-2">@SectionCountLabel</MudText>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Source</MudText>
|
||||
<MudText Typo="Typo.h6" Class="mt-2">@SourceJson</MudText>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Generated</MudText>
|
||||
<MudText Typo="Typo.h6" Class="mt-2">@GeneratedAt</MudText>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
<MudGrid Class="mb-4">
|
||||
@foreach (var section in HighlightSections)
|
||||
{
|
||||
<MudItem xs="12" sm="6" lg="3">
|
||||
<MudCard Class="h-100">
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">@(section.Name)</MudText>
|
||||
<MudText Typo="Typo.subtitle1" Class="mt-1">@(section.Title)</MudText>
|
||||
<MudText Typo="Typo.caption" Color="Color.Secondary">@(section.Preview)</MudText>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
|
||||
<MudCard Class="mb-4">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Report Health</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudStack Spacing="2">
|
||||
<MudText Typo="Typo.body2"><strong>Status:</strong> <MudChip Size="Size.Small" Color="@HealthColor">@HealthLabel</MudChip></MudText>
|
||||
<MudText Typo="Typo.body2"><strong>Path:</strong> @ReportPath</MudText>
|
||||
<MudText Typo="Typo.body2"><strong>Sections rendered:</strong> @RenderedSectionCountLabel</MudText>
|
||||
</MudStack>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<MudCard>
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Sections</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
@if (Sections.Count == 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined">
|
||||
DATA_MISSING: operational_report.json에 표시할 섹션이 없습니다.
|
||||
</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTable Items="Sections" Hover="true" Striped="true" Dense="true">
|
||||
<HeaderContent>
|
||||
<MudTh>Name</MudTh>
|
||||
<MudTh>Title</MudTh>
|
||||
<MudTh>Preview</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Name</MudTd>
|
||||
<MudTd>@context.Title</MudTd>
|
||||
<MudTd>@context.Preview</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
}
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
@code {
|
||||
private readonly List<OperationalReportSection> Sections = new();
|
||||
private readonly List<OperationalReportSection> HighlightSections = new();
|
||||
private string SchemaVersion = "n/a";
|
||||
private string SourceJson = "n/a";
|
||||
private string GeneratedAt = "n/a";
|
||||
private string SectionCountLabel = "0";
|
||||
private string RenderedSectionCountLabel = "0";
|
||||
private string HealthLabel = "DATA_MISSING";
|
||||
private Color HealthColor = Color.Warning;
|
||||
private string ReportPath = "n/a";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
ReportPath = Path.GetFullPath(Path.Combine(Environment.ContentRootPath, "..", "..", "..", "Temp", "operational_report.json"));
|
||||
|
||||
var report = OperationalReportLoader.Load(ReportPath);
|
||||
SchemaVersion = report.SchemaVersion;
|
||||
SourceJson = report.SourceJson;
|
||||
GeneratedAt = report.GeneratedAt;
|
||||
Sections.AddRange(report.Sections);
|
||||
|
||||
HighlightSections.Clear();
|
||||
HighlightSections.AddRange(Sections.Take(4));
|
||||
|
||||
SectionCountLabel = report.SectionCount.ToString();
|
||||
RenderedSectionCountLabel = Sections.Count.ToString();
|
||||
HealthLabel = Sections.Count > 0 ? "PASS" : "DATA_MISSING";
|
||||
HealthColor = Sections.Count > 0 ? Color.Success : Color.Warning;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
@page "/weather"
|
||||
@attribute [StreamRendering]
|
||||
|
||||
<PageTitle>Weather</PageTitle>
|
||||
|
||||
<h1>Weather</h1>
|
||||
|
||||
<p>This component demonstrates showing data.</p>
|
||||
|
||||
@if (forecasts == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th aria-label="Temperature in Celsius">Temp. (C)</th>
|
||||
<th aria-label="Temperature in Fahrenheit">Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var forecast in forecasts)
|
||||
{
|
||||
<tr>
|
||||
<td>@forecast.Date.ToShortDateString()</td>
|
||||
<td>@forecast.TemperatureC</td>
|
||||
<td>@forecast.TemperatureF</td>
|
||||
<td>@forecast.Summary</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
@code {
|
||||
private WeatherForecast[]? forecasts;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Simulate asynchronous loading to demonstrate streaming rendering
|
||||
await Task.Delay(500);
|
||||
|
||||
var startDate = DateOnly.FromDateTime(DateTime.Now);
|
||||
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
|
||||
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = startDate.AddDays(index),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = summaries[Random.Shared.Next(summaries.Length)]
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private class WeatherForecast
|
||||
{
|
||||
public DateOnly Date { get; set; }
|
||||
public int TemperatureC { get; set; }
|
||||
public string? Summary { get; set; }
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user