Replace admin MudBlazor shell with plain HTML
TaxBaik CI/CD / build-and-deploy (push) Successful in 3m29s
TaxBaik CI/CD / build-and-deploy (push) Successful in 3m29s
This commit is contained in:
@@ -1,240 +1,10 @@
|
||||
@inject TaxBaik.Web.Client.Components.Admin.Services.IAdminDashboardClient DashboardClient
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
<PageTitle>대시보드</PageTitle>
|
||||
|
||||
<AdminPageHeader Title="대시보드" Eyebrow="Home" Subtitle="운영 현황과 바로가기 작업을 한 화면에서 확인합니다.">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Refresh" Disabled="@loading" @onclick="ReloadAsync">
|
||||
새로고침
|
||||
</MudButton>
|
||||
</AdminPageHeader>
|
||||
<AdminPageHeader Title="대시보드" Eyebrow="Home" Subtitle="관리자 대시보드 초기화 문제를 분리하는 최소 렌더 화면입니다." />
|
||||
|
||||
<MudGrid Class="admin-dashboard-grid">
|
||||
@if (loading)
|
||||
{
|
||||
<MudItem xs="12">
|
||||
<AdminDataPanel Loading="true" />
|
||||
</MudItem>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<AdminMetricCard Label="이번 달 문의" Value="@summary?.ThisMonthInquiries" Caption="이번 달 접수된 문의" Accent="accent-blue" Icon="@Icons.Material.Filled.MarkUnreadChatAlt" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<AdminMetricCard Label="신규 문의" Value="@summary?.NewInquiries" Caption="처리 대기 중인 문의" Accent="accent-amber" Icon="@Icons.Material.Filled.PriorityHigh" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<AdminMetricCard Label="총 게시글" Value="@summary?.TotalPosts" Caption="홈페이지 블로그 누적" Accent="accent-slate" Icon="@Icons.Material.Filled.Article" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<AdminMetricCard Label="게시중 글" Value="@summary?.PublishedPosts" Caption="현재 공개 중인 글" Accent="accent-green" Icon="@Icons.Material.Filled.Public" />
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" md="5">
|
||||
<AdminDataPanel>
|
||||
<AdminPageHeader Title="빠른 작업" Subtitle="자주 쓰는 관리 화면으로 바로 이동합니다." />
|
||||
<MudStack Spacing="1">
|
||||
@foreach (var action in QuickActions)
|
||||
{
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Primary" FullWidth="true" StartIcon="@action.Icon" OnClick="@(() => Navigation.NavigateTo(action.Href))">
|
||||
@action.Label
|
||||
</MudButton>
|
||||
}
|
||||
</MudStack>
|
||||
</AdminDataPanel>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" md="7">
|
||||
<AdminDataPanel>
|
||||
<AdminPageHeader Title="최근 문의" Subtitle="최신 처리 흐름을 빠르게 확인합니다." />
|
||||
@if (summary?.RecentInquiries is null || !summary.RecentInquiries.Any())
|
||||
{
|
||||
<AdminEmptyState Icon="@Icons.Material.Filled.Inbox" Message="새로운 문의가 들어오면 이 영역에 표시됩니다." />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudSimpleTable Dense="true" Class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>제목</th>
|
||||
<th>상태</th>
|
||||
<th>접수일</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var inquiry in summary.RecentInquiries)
|
||||
{
|
||||
<tr>
|
||||
<td>@inquiry.Name</td>
|
||||
<td>@inquiry.Status</td>
|
||||
<td>@inquiry.CreatedAt.ToString("yyyy-MM-dd")</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</MudSimpleTable>
|
||||
}
|
||||
</AdminDataPanel>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" md="6">
|
||||
<AdminDataPanel Loading="@statsLoading">
|
||||
<AdminPageHeader Title="월간 통계" Subtitle="문의 처리 전환과 상태 분포를 확인합니다." />
|
||||
@if (monthlyStats is not null)
|
||||
{
|
||||
<div class="admin-stats-grid">
|
||||
<div class="admin-stat">
|
||||
<span class="admin-stat-label">총 문의</span>
|
||||
<strong class="admin-stat-value">@monthlyStats.TotalInquiries</strong>
|
||||
</div>
|
||||
<div class="admin-stat">
|
||||
<span class="admin-stat-label">상담 중</span>
|
||||
<strong class="admin-stat-value">@monthlyStats.ConsultingCount</strong>
|
||||
</div>
|
||||
<div class="admin-stat">
|
||||
<span class="admin-stat-label">완료</span>
|
||||
<strong class="admin-stat-value">@monthlyStats.CompletedCount</strong>
|
||||
</div>
|
||||
<div class="admin-stat">
|
||||
<span class="admin-stat-label">완료율</span>
|
||||
<strong class="admin-stat-value">@monthlyStats.CompletionRate.ToString("0.#")%</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admin-chart">
|
||||
<div class="admin-chart-header">
|
||||
<span>최근 6개월 문의 추세</span>
|
||||
<span>@monthlyStats.Month</span>
|
||||
</div>
|
||||
<div class="admin-chart-bars">
|
||||
@foreach (var point in trendPoints)
|
||||
{
|
||||
<div class="admin-chart-row">
|
||||
<span>@point.Label</span>
|
||||
<div class="admin-chart-track">
|
||||
<div class="admin-chart-fill accent-blue" style="width:@BarWidth(point.Total, trendMax)"></div>
|
||||
</div>
|
||||
<span>@point.Total</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</AdminDataPanel>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" md="6">
|
||||
<AdminDataPanel>
|
||||
<AdminPageHeader Title="바로가기" Subtitle="운영 중 자주 사용하는 화면으로 이동합니다." />
|
||||
<div class="admin-quick-grid">
|
||||
@foreach (var action in QuickActions)
|
||||
{
|
||||
<MudPaper Class="admin-quick-card" Elevation="0" @onclick="@(() => Navigation.NavigateTo(action.Href))">
|
||||
<MudIcon Icon="@action.Icon" />
|
||||
<span>@action.Label</span>
|
||||
</MudPaper>
|
||||
}
|
||||
</div>
|
||||
</AdminDataPanel>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" md="6">
|
||||
<AdminDataPanel>
|
||||
<AdminPageHeader Title="운영 메모" Subtitle="이번 배포에서 확인해야 할 핵심 포인트입니다." />
|
||||
<AdminDataPanel>
|
||||
<div class="admin-note-list">
|
||||
<div>대시보드와 메뉴가 역할 기반으로 필터링됩니다.</div>
|
||||
<div>공통 CRUD 화면은 `AdminPageHeader`/`AdminCrudPageShell` 슬롯 패턴을 따릅니다.</div>
|
||||
<div>월간 통계는 문의 전환 흐름을 빠르게 보는 용도입니다.</div>
|
||||
<div>대시보드 본문을 단계적으로 복원하며 `get` interop 오류를 추적합니다.</div>
|
||||
<div>현재 화면은 JS 호출 없이 렌더만 수행합니다.</div>
|
||||
</div>
|
||||
</AdminDataPanel>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" md="6">
|
||||
<AdminDataPanel>
|
||||
<AdminPageHeader Title="알림" Subtitle="운영 상태를 빠르게 점검합니다." />
|
||||
<MudStack Spacing="1">
|
||||
<MudAlert Severity="@(summary is not null && summary.NewInquiries > 0 ? Severity.Warning : Severity.Info)">
|
||||
@(summary is not null && summary.NewInquiries > 0
|
||||
? $"처리 대기 중인 신규 문의가 {summary.NewInquiries}건 있습니다."
|
||||
: "현재 신규 문의가 없습니다.")
|
||||
</MudAlert>
|
||||
<MudAlert Severity="@(summary is not null && summary.PublishedPosts < summary.TotalPosts ? Severity.Info : Severity.Success)">
|
||||
@(summary is not null && summary.PublishedPosts < summary.TotalPosts
|
||||
? "비공개 중인 게시글이 있어 발행 상태 확인이 필요합니다."
|
||||
: "게시글 발행 상태가 안정적입니다.")
|
||||
</MudAlert>
|
||||
</MudStack>
|
||||
</AdminDataPanel>
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
|
||||
@code {
|
||||
private bool loading = true;
|
||||
private bool statsLoading = true;
|
||||
private AdminDashboardSummary? summary;
|
||||
private TaxBaik.Web.Client.Components.Admin.Services.MonthlyStatsDto? monthlyStats;
|
||||
private readonly List<(string Label, int Total)> trendPoints = [];
|
||||
|
||||
private static readonly (string Label, string Href, string Icon)[] QuickActions =
|
||||
[
|
||||
("공지 등록", "/taxbaik/admin/announcements/create", Icons.Material.Filled.Campaign),
|
||||
("고객사 등록", "/taxbaik/admin/companies/create", Icons.Material.Filled.Business),
|
||||
("문의 관리", "/taxbaik/admin/inquiries", Icons.Material.Filled.Inbox),
|
||||
("설정 열기", "/taxbaik/admin/settings", Icons.Material.Filled.Settings),
|
||||
];
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadAsync();
|
||||
await LoadStatsAsync();
|
||||
await LoadTrendAsync();
|
||||
}
|
||||
|
||||
private async Task ReloadAsync()
|
||||
{
|
||||
await LoadAsync();
|
||||
}
|
||||
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
loading = true;
|
||||
try
|
||||
{
|
||||
summary = await DashboardClient.GetSummaryAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadStatsAsync()
|
||||
{
|
||||
statsLoading = true;
|
||||
try
|
||||
{
|
||||
monthlyStats = await DashboardClient.GetMonthlyStatsAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
statsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadTrendAsync()
|
||||
{
|
||||
trendPoints.Clear();
|
||||
var today = DateTime.Today;
|
||||
for (var i = 5; i >= 0; i--)
|
||||
{
|
||||
var month = today.AddMonths(-i);
|
||||
var monthly = await DashboardClient.GetMonthlyStatsAsync(month.ToString("yyyy-MM"));
|
||||
trendPoints.Add(($"{month:MM}", monthly.TotalInquiries));
|
||||
}
|
||||
}
|
||||
|
||||
private static string BarWidth(int value, int total)
|
||||
=> total <= 0 ? "0%" : $"{Math.Max(6, (int)Math.Round(value * 100.0 / total))}%";
|
||||
|
||||
private int trendMax => Math.Max(1, trendPoints.Count == 0 ? 1 : trendPoints.Max(x => x.Total));
|
||||
}
|
||||
</AdminDataPanel>
|
||||
|
||||
@@ -9,12 +9,8 @@
|
||||
<div class="admin-brand-subtitle">세무회계 관리 대시보드</div>
|
||||
</div>
|
||||
<div class="admin-topbar-actions">
|
||||
<MudButton Variant="Variant.Text" Size="Size.Small" Class="admin-topbar-action" Href="/taxbaik" Target="_blank" Rel="noopener">
|
||||
공개 사이트
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Text" Size="Size.Small" Class="admin-topbar-action" Href="/taxbaik/admin/logout">
|
||||
로그아웃
|
||||
</MudButton>
|
||||
<a class="admin-topbar-action" href="/taxbaik" target="_blank" rel="noopener">공개 사이트</a>
|
||||
<a class="admin-topbar-action" href="/taxbaik/admin/logout">로그아웃</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -45,6 +41,8 @@
|
||||
|
||||
private void OnLocationChanged(object? sender, LocationChangedEventArgs args)
|
||||
{
|
||||
drawerOpen = true;
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private void ToggleDrawer()
|
||||
|
||||
@@ -11,36 +11,26 @@
|
||||
</div>
|
||||
|
||||
<nav class="admin-nav" aria-label="관리자 메뉴">
|
||||
<MudNavMenu Dense="true">
|
||||
@foreach (var section in visibleSections)
|
||||
{
|
||||
<MudText Typo="Typo.caption" Class="admin-nav-group">@section.Label</MudText>
|
||||
<div class="admin-nav-group">@section.Label</div>
|
||||
@foreach (var item in section.Items)
|
||||
{
|
||||
<MudNavLink Href="@item.Href" Match="@(item.IsExact ? NavLinkMatch.All : NavLinkMatch.Prefix)">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudIcon Icon="@item.Icon" Size="Size.Small" />
|
||||
<span>@item.Label</span>
|
||||
<a class="admin-nav-link" href="@item.Href">
|
||||
<span class="admin-nav-link-icon">@item.Icon</span>
|
||||
<span class="admin-nav-link-label">@item.Label</span>
|
||||
@if (!string.IsNullOrWhiteSpace(item.Badge))
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Class="admin-nav-badge">@item.Badge</MudChip>
|
||||
<span class="admin-nav-badge">@item.Badge</span>
|
||||
}
|
||||
</MudStack>
|
||||
</MudNavLink>
|
||||
</a>
|
||||
}
|
||||
}
|
||||
</MudNavMenu>
|
||||
</nav>
|
||||
|
||||
<div class="admin-drawer-footer">
|
||||
<div class="admin-footer-item">
|
||||
<MudIcon Icon="@Icons.Material.Filled.OpenInNew" Size="Size.Small" />
|
||||
<MudLink Href="/taxbaik" Target="_blank" Underline="Underline.None">공개 사이트</MudLink>
|
||||
</div>
|
||||
<div class="admin-footer-item">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Logout" Size="Size.Small" />
|
||||
<MudLink Href="/taxbaik/admin/logout" Underline="Underline.None">로그아웃</MudLink>
|
||||
</div>
|
||||
<a class="admin-footer-item" href="/taxbaik" target="_blank" rel="noopener">공개 사이트</a>
|
||||
<a class="admin-footer-item" href="/taxbaik/admin/logout">로그아웃</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user