From 28e1a8775f624724935b3635d9db133d6142442a Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Wed, 1 Jul 2026 13:24:46 +0900 Subject: [PATCH] feat(ui): migrate web shell to mudblazor --- .../Client/Layout/AuthLayout.razor | 3 + .../Client/Layout/MainLayout.razor | 94 ++---- .../Client/Layout/NavMenu.razor | 14 +- .../Client/Pages/Collection.razor | 167 +++++------ .../Client/Pages/Dashboard.razor | 143 +++++---- .../QuantEngine.Web/Client/Pages/Login.razor | 273 ++---------------- .../Client/Pages/Operations.razor | 143 +++++---- src/dotnet/QuantEngine.Web/Client/Program.cs | 4 - .../Client/QuantEngine.Web.Client.csproj | 3 +- .../QuantEngine.Web/Client/_Imports.razor | 3 +- .../QuantEngine.Web/Components/App.razor | 14 +- .../QuantEngine.Web/Components/_Imports.razor | 3 +- src/dotnet/QuantEngine.Web/Program.cs | 7 +- .../QuantEngine.Web/QuantEngine.Web.csproj | 3 +- 14 files changed, 285 insertions(+), 589 deletions(-) create mode 100644 src/dotnet/QuantEngine.Web/Client/Layout/AuthLayout.razor diff --git a/src/dotnet/QuantEngine.Web/Client/Layout/AuthLayout.razor b/src/dotnet/QuantEngine.Web/Client/Layout/AuthLayout.razor new file mode 100644 index 0000000..4f3f76d --- /dev/null +++ b/src/dotnet/QuantEngine.Web/Client/Layout/AuthLayout.razor @@ -0,0 +1,3 @@ +@inherits LayoutComponentBase + +@Body diff --git a/src/dotnet/QuantEngine.Web/Client/Layout/MainLayout.razor b/src/dotnet/QuantEngine.Web/Client/Layout/MainLayout.razor index b4feea1..8ce1b45 100644 --- a/src/dotnet/QuantEngine.Web/Client/Layout/MainLayout.razor +++ b/src/dotnet/QuantEngine.Web/Client/Layout/MainLayout.razor @@ -2,74 +2,36 @@ @inject HttpClient Http @inject AuthenticationStateProvider AuthStateProvider @inject NavigationManager NavigationManager -@using System.Net.Http.Json -@using Microsoft.FluentUI.AspNetCore.Components -@using QuantEngine.Web.Client.Infrastructure - - - - - - ☰ - -

QuantEngine v@appVersion

- - -
- 관리자 (@context.User.Identity?.Name) - - 로그아웃 - -
-
-
-
-
+ + + + QuantEngine v@appVersion + + + + 관리자 (@context.User.Identity?.Name) + 로그아웃 + + + - - - - @if (navOpen) - { - - } + + + + +
+ QuantEngine v@appVersion + 배포: @buildTime +
+
- - + + @Body - -
-
- -
- -
- - + + + @code { private bool navOpen = true; @@ -89,7 +51,6 @@ } catch { - // Fail-safe default fallback values } } @@ -97,7 +58,7 @@ { var customProvider = (CustomAuthenticationStateProvider)AuthStateProvider; await customProvider.LogoutFromServerAsync(); - NavigationManager.NavigateTo("login"); + NavigationManager.NavigateTo("/login"); } private class VersionInfo @@ -106,4 +67,3 @@ public string? Built { get; set; } } } - diff --git a/src/dotnet/QuantEngine.Web/Client/Layout/NavMenu.razor b/src/dotnet/QuantEngine.Web/Client/Layout/NavMenu.razor index 974cb08..e6bee1c 100644 --- a/src/dotnet/QuantEngine.Web/Client/Layout/NavMenu.razor +++ b/src/dotnet/QuantEngine.Web/Client/Layout/NavMenu.razor @@ -1,10 +1,4 @@ -@using Microsoft.FluentUI.AspNetCore.Components - - - - Dashboard - - - Operations - - + + Dashboard + Operations + diff --git a/src/dotnet/QuantEngine.Web/Client/Pages/Collection.razor b/src/dotnet/QuantEngine.Web/Client/Pages/Collection.razor index 1c70cfd..a7154e8 100644 --- a/src/dotnet/QuantEngine.Web/Client/Pages/Collection.razor +++ b/src/dotnet/QuantEngine.Web/Client/Pages/Collection.razor @@ -6,118 +6,88 @@ QuantEngine - Collection -

Data Collection

-

- KIS API data collection dashboard. Monitor runs, snapshots, and error trends. -

+Data Collection +KIS API data collection dashboard. API-first로만 동작합니다. - - - - @if (IsProcessing) { Running... } else { Start Collection } - - - Refresh - - + + + @(IsProcessing ? "Running..." : "Start Collection") + + Refresh + - @if (IsLoading) { - - - - + } else if (DashboardState != null) { - - - -
-

Last Run

-

@(DashboardState.LastRunStatus ?? "N/A")

-

@(DashboardState.LastFinishedAt ?? "Not finished")

-
-
- -
-

Total Snapshots

-

@DashboardState.TotalSnapshots

-
-
- -
-

Total Errors

-

@DashboardState.TotalErrors

-
-
-
+ + + + Last Run + @(DashboardState.LastRunStatus ?? "N/A") + @(DashboardState.LastFinishedAt ?? "Not finished") + + + + + Total Snapshots + @DashboardState.TotalSnapshots + + + + + Total Errors + @DashboardState.TotalErrors + + + - @if (DashboardState.RecentErrors.Count > 0) { - -
-

Recent Errors

- - - - - - - - - - - @foreach (var error in DashboardState.RecentErrors) - { - - - - - - - } - -
SourceKindTickerMessage
@error.SourceName@error.ErrorKind@error.Ticker@error.ErrorMessage
-
-
+ + Recent Errors + + + Source + Kind + Ticker + Message + + + @context.SourceName + @context.ErrorKind + @context.Ticker + @context.ErrorMessage + + + } - @if (RecentRuns != null && RecentRuns.Count > 0) { - -
-

Recent Runs

- - - - - - - - - - - - - @foreach (var run in RecentRuns) - { - - - - - - - - - } - -
Run IDStatusStartedFinishedSnapshotsErrors
@run.RunId@run.Status@run.StartedAt@run.FinishedAt@run.TotalSnapshots@run.TotalErrors
-
-
+ + Recent Runs + + + Run ID + Status + Started + Finished + Snapshots + Errors + + + @context.RunId + @context.Status + @context.StartedAt + @context.FinishedAt + @context.TotalSnapshots + @context.TotalErrors + + + } } @@ -138,7 +108,6 @@ else if (DashboardState != null) try { DashboardState = await ApiClient.GetCollectionStateAsync(); - var runsResponse = await ApiClient.GetCollectionRunsAsync(10); RecentRuns = runsResponse?.Runs ?? new(); } diff --git a/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor b/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor index d22ffce..bc2def6 100644 --- a/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor +++ b/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor @@ -1,89 +1,84 @@ -@page "/" +@page "/dashboard" @attribute [Authorize] @using QuantEngine.Core.Infrastructure @inject HttpClient Http Quant Engine - Dashboard -

Quant Engine

-

- 루트 화면은 운영 진입점입니다. 가짜 성과 수치 없이 현재 스냅샷 상태와 리포트 경로만 보여줍니다. -

+Quant Engine +운영 진입점입니다. 로그인 후 현재 스냅샷 상태와 리포트 경로만 표시합니다. - - - -
-

Operational Report

-

@ReportStateLabel

-

@ReportPath

-
-
- -
-

Sections

-

@SectionCountLabel

-

Temp/operational_report.json

-
-
- -
-

Primary Route

- - Open Operations - -
-
-
+ + + + Operational Report + @ReportStateLabel + @ReportPath + + + + + Sections + @SectionCountLabel + Temp/operational_report.json + + + + + Primary Route + Open Operations + + + - - - -
-

Current State

- -

Status: @ReportChipLabel

-

Generated: @GeneratedAtLabel

-

Source: @SourceLabel

-

Decision feed: @DecisionFeedLabel

-

Factor feed: @FactorFeedLabel

-

Raw feed: @RawFeedLabel

-
-
-
- - -
-

Routing Notes

-
    + + + + Current State + + Status: @ReportChipLabel + Generated: @GeneratedAtLabel + Source: @SourceLabel + Decision feed: @DecisionFeedLabel + Factor feed: @FactorFeedLabel + Raw feed: @RawFeedLabel + + + + + + Routing Notes +
    • 운영 데이터는 snapshot 우선입니다.
    • -
    • Excel/GAS 의존 문구는 운영 경로에서 제거 대상입니다.
    • +
    • Excel/GAS 의존 문구는 제거 대상입니다.
    • 숫자는 provenance 없으면 표시하지 않습니다.
    -
-
-
+ + + - - -
-

Coverage Summary

- @if (Sections.Count == 0) - { -
- DATA_MISSING: operational_report.json이 비어 있거나 아직 생성되지 않았습니다. -
- } - else - { - - - - - - } -
-
+ + Coverage Summary + @if (Sections.Count == 0) + { + DATA_MISSING: operational_report.json이 비어 있거나 아직 생성되지 않았습니다. + } + else + { + + + Name + Title + Preview + + + @context.Name + @context.Title + @context.Preview + + + } + @code { private readonly List Sections = new(); diff --git a/src/dotnet/QuantEngine.Web/Client/Pages/Login.razor b/src/dotnet/QuantEngine.Web/Client/Pages/Login.razor index bc25fc1..02d72be 100644 --- a/src/dotnet/QuantEngine.Web/Client/Pages/Login.razor +++ b/src/dotnet/QuantEngine.Web/Client/Pages/Login.razor @@ -1,264 +1,54 @@ @page "/login" @attribute [AllowAnonymous] +@layout AuthLayout @inject AuthenticationStateProvider AuthStateProvider @inject NavigationManager NavigationManager @inject HttpClient Http 로그인 - QuantEngine -
-
-
- -

QuantEngine

-

은퇴자산포트폴리오 투자 관리 시스템

-
+
-
+ + @(IsSubmitting ? "인증 중..." : "로그인") + + + + @@ -291,7 +81,6 @@ try { var response = await Http.PostAsJsonAsync("api/auth/login", new { Username, Password }); - if (response.IsSuccessStatusCode) { var auth = await response.Content.ReadFromJsonAsync(); @@ -303,9 +92,7 @@ var customProvider = (CustomAuthenticationStateProvider)AuthStateProvider; await customProvider.MarkUserAsAuthenticatedAsync(auth.Username ?? Username, auth.AccessToken, auth.Role ?? "Admin"); - - // Redirect back to home dashboard - NavigationManager.NavigateTo(""); + NavigationManager.NavigateTo("/dashboard"); } else { diff --git a/src/dotnet/QuantEngine.Web/Client/Pages/Operations.razor b/src/dotnet/QuantEngine.Web/Client/Pages/Operations.razor index 9e44e3f..5c94800 100644 --- a/src/dotnet/QuantEngine.Web/Client/Pages/Operations.razor +++ b/src/dotnet/QuantEngine.Web/Client/Pages/Operations.razor @@ -3,87 +3,82 @@ @using QuantEngine.Core.Infrastructure @inject HttpClient Http -Quant Engine - Operations +QuantEngine - Operations -

Operational Report

-

- 이 페이지는 `Temp/operational_report.json`만 읽습니다. DB 연결과 무관하게 동일한 결과를 보여주는 운영 고정 화면입니다. -

+Operational Report +Temp/operational_report.json만 읽는 운영 고정 화면입니다. - - - -
-

Schema

-

@SchemaVersion

-
-
- -
-

Sections

-

@SectionCountLabel

-
-
- -
-

Source

-

@SourceJson

-
-
- -
-

Generated

-

@GeneratedAt

-
-
-
+ + + + Schema + @SchemaVersion + + + + + Sections + @SectionCountLabel + + + + + Source + @SourceJson + + + + + Generated + @GeneratedAt + + + - - + @foreach (var section in HighlightSections) { - -
-

@(section.Name)

-

@(section.Title)

-

@(section.Preview)

-
-
+ + + @(section.Name) + @(section.Title) + @(section.Preview) + + } -
+ - - -
-

Report Health

- -

Status: @HealthLabel

-

Path: @ReportPath

-

Sections rendered: @RenderedSectionCountLabel

-
-
-
+ + Report Health + + Status: @HealthLabel + Path: @ReportPath + Sections rendered: @RenderedSectionCountLabel + + - - -
-

Sections

- @if (Sections.Count == 0) - { -
- DATA_MISSING: operational_report.json에 표시할 섹션이 없습니다. -
- } - else - { - - - - - - } -
-
+ + Sections + @if (Sections.Count == 0) + { + DATA_MISSING: operational_report.json에 표시할 섹션이 없습니다. + } + else + { + + + Name + Title + Preview + + + @context.Name + @context.Title + @context.Preview + + + } + @code { private readonly List Sections = new(); @@ -106,7 +101,7 @@ SchemaVersion = report.SchemaVersion; SourceJson = report.SourceJson; GeneratedAt = report.GeneratedAt; - + Sections.Clear(); Sections.AddRange(report.Sections); diff --git a/src/dotnet/QuantEngine.Web/Client/Program.cs b/src/dotnet/QuantEngine.Web/Client/Program.cs index 89b6b09..f7a2fc4 100644 --- a/src/dotnet/QuantEngine.Web/Client/Program.cs +++ b/src/dotnet/QuantEngine.Web/Client/Program.cs @@ -1,14 +1,10 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.FluentUI.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; using QuantEngine.Web.Client.Services; using QuantEngine.Web.Client.Infrastructure; var builder = WebAssemblyHostBuilder.CreateDefault(args); -// Register Fluent UI -builder.Services.AddFluentUIComponents(); - // Register LocalStorage for cross-platform session persistence builder.Services.AddScoped(); diff --git a/src/dotnet/QuantEngine.Web/Client/QuantEngine.Web.Client.csproj b/src/dotnet/QuantEngine.Web/Client/QuantEngine.Web.Client.csproj index ad69745..be0cf34 100644 --- a/src/dotnet/QuantEngine.Web/Client/QuantEngine.Web.Client.csproj +++ b/src/dotnet/QuantEngine.Web/Client/QuantEngine.Web.Client.csproj @@ -16,8 +16,7 @@ - - + diff --git a/src/dotnet/QuantEngine.Web/Client/_Imports.razor b/src/dotnet/QuantEngine.Web/Client/_Imports.razor index b98bd91..fe60c8a 100644 --- a/src/dotnet/QuantEngine.Web/Client/_Imports.razor +++ b/src/dotnet/QuantEngine.Web/Client/_Imports.razor @@ -6,8 +6,7 @@ @using static Microsoft.AspNetCore.Components.Web.RenderMode @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop -@using Microsoft.FluentUI.AspNetCore.Components -@using Microsoft.FluentUI.AspNetCore.Components.Icons +@using MudBlazor @using QuantEngine.Web.Client @using QuantEngine.Web.Client.Pages @using QuantEngine.Web.Client.Layout diff --git a/src/dotnet/QuantEngine.Web/Components/App.razor b/src/dotnet/QuantEngine.Web/Components/App.razor index e12a7a9..39ac796 100644 --- a/src/dotnet/QuantEngine.Web/Components/App.razor +++ b/src/dotnet/QuantEngine.Web/Components/App.razor @@ -1,14 +1,13 @@ - + - - + @@ -18,13 +17,14 @@ - + + + - + - - + diff --git a/src/dotnet/QuantEngine.Web/Components/_Imports.razor b/src/dotnet/QuantEngine.Web/Components/_Imports.razor index 9fae3a5..50a9bc5 100644 --- a/src/dotnet/QuantEngine.Web/Components/_Imports.razor +++ b/src/dotnet/QuantEngine.Web/Components/_Imports.razor @@ -6,8 +6,7 @@ @using static Microsoft.AspNetCore.Components.Web.RenderMode @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop -@using Microsoft.FluentUI.AspNetCore.Components -@using Microsoft.FluentUI.AspNetCore.Components.Icons +@using MudBlazor @using QuantEngine.Web @using QuantEngine.Web.Components @using QuantEngine.Web.Components.Layout diff --git a/src/dotnet/QuantEngine.Web/Program.cs b/src/dotnet/QuantEngine.Web/Program.cs index d7179ba..78dc3c5 100644 --- a/src/dotnet/QuantEngine.Web/Program.cs +++ b/src/dotnet/QuantEngine.Web/Program.cs @@ -8,7 +8,6 @@ using QuantEngine.Core.Interfaces; using QuantEngine.Application.Services; using System.Text.Json; using static QuantEngine.Application.Services.DataCollectionService; -using Microsoft.FluentUI.AspNetCore.Components; using Serilog; using QuantEngine.Web.Client.Infrastructure; using QuantEngine.Web.Client.Services; @@ -19,6 +18,7 @@ using QuantEngine.Core.Models; using Microsoft.AspNetCore.Authentication; using System.Text.Encodings.Web; using Microsoft.Extensions.Options; +using MudBlazor.Services; // Serilog Configuration with Telegram Sink Log.Logger = new LoggerConfiguration() @@ -43,8 +43,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddAuthorizationCore(); -// Fluent UI Services -builder.Services.AddFluentUIComponents(); +builder.Services.AddMudServices(); // PostgreSQL Dapper Setup var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") @@ -112,6 +111,8 @@ app.UseAuthorization(); app.MapStaticAssets(); +app.MapGet("/", () => Results.Redirect("/login")); + // Collection API Endpoints (must be before MapRazorComponents) app.MapCollectionEndpoints(); diff --git a/src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj b/src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj index 38d8a2b..9ff8b4e 100644 --- a/src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj +++ b/src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj @@ -8,8 +8,7 @@ - - +