185 lines
8.4 KiB
Plaintext
185 lines
8.4 KiB
Plaintext
@page "/admin/dashboard"
|
|
@attribute [Authorize]
|
|
@using TaxBaik.Application.Services
|
|
@inject AdminDashboardService DashboardService
|
|
@inject TaxFilingService FilingService
|
|
@inject NavigationManager Nav
|
|
|
|
<PageTitle>대시보드</PageTitle>
|
|
|
|
<section class="admin-page-hero">
|
|
<div>
|
|
<MudText Typo="Typo.caption" Class="admin-eyebrow">Overview</MudText>
|
|
<MudText Typo="Typo.h4" Class="admin-page-title">대시보드</MudText>
|
|
<MudText Typo="Typo.body2" Class="admin-page-subtitle">문의 흐름과 콘텐츠 상태를 한 화면에서 확인합니다.</MudText>
|
|
</div>
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" Href="/taxbaik/admin/blog/create">
|
|
새 포스트 작성
|
|
</MudButton>
|
|
</section>
|
|
|
|
<MudGrid Class="admin-metric-grid">
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="admin-metric-card accent-blue cursor-pointer" Elevation="0" @onclick='(() => Nav.NavigateTo("/taxbaik/admin/inquiries"))' Style="cursor: pointer; transition: transform 0.2s, box-shadow 0.2s;">
|
|
<MudText Typo="Typo.caption">이번달 문의</MudText>
|
|
<div class="d-flex align-center justify-space-between">
|
|
<MudText Typo="Typo.h3">@summary.ThisMonthInquiries</MudText>
|
|
<MudIcon Icon="@Icons.Material.Filled.Forum" Color="Color.Primary" Size="Size.Large" Style="opacity: 0.2;" />
|
|
</div>
|
|
<MudText Typo="Typo.body2">월간 상담 유입 (클릭 시 이동)</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="admin-metric-card accent-amber cursor-pointer" Elevation="0" @onclick='(() => Nav.NavigateTo("/taxbaik/admin/inquiries?status=new"))' Style="cursor: pointer; transition: transform 0.2s, box-shadow 0.2s;">
|
|
<MudText Typo="Typo.caption">신규 문의</MudText>
|
|
<div class="d-flex align-center justify-space-between">
|
|
<MudText Typo="Typo.h3">@summary.NewInquiries</MudText>
|
|
<MudIcon Icon="@Icons.Material.Filled.NotificationImportant" Color="Color.Warning" Size="Size.Large" Style="opacity: 0.2;" />
|
|
</div>
|
|
<MudText Typo="Typo.body2">처리 대기 (클릭 시 이동)</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="admin-metric-card accent-slate cursor-pointer" Elevation="0" @onclick='(() => Nav.NavigateTo("/taxbaik/admin/blog"))' Style="cursor: pointer; transition: transform 0.2s, box-shadow 0.2s;">
|
|
<MudText Typo="Typo.caption">전체 포스트</MudText>
|
|
<div class="d-flex align-center justify-space-between">
|
|
<MudText Typo="Typo.h3">@summary.TotalPosts</MudText>
|
|
<MudIcon Icon="@Icons.Material.Filled.Article" Color="Color.Default" Size="Size.Large" Style="opacity: 0.2;" />
|
|
</div>
|
|
<MudText Typo="Typo.body2">콘텐츠 자산 (클릭 시 이동)</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="admin-metric-card accent-green cursor-pointer" Elevation="0" @onclick='(() => Nav.NavigateTo("/taxbaik/admin/blog"))' Style="cursor: pointer; transition: transform 0.2s, box-shadow 0.2s;">
|
|
<MudText Typo="Typo.caption">발행된 포스트</MudText>
|
|
<div class="d-flex align-center justify-space-between">
|
|
<MudText Typo="Typo.h3">@summary.PublishedPosts</MudText>
|
|
<MudIcon Icon="@Icons.Material.Filled.Public" Color="Color.Success" Size="Size.Large" Style="opacity: 0.2;" />
|
|
</div>
|
|
<MudText Typo="Typo.body2">검색 노출 대상 (클릭 시 이동)</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
@if (upcomingFilings.Count > 0)
|
|
{
|
|
<MudPaper Class="admin-surface mt-4" Elevation="0">
|
|
<div class="admin-section-header">
|
|
<div>
|
|
<MudText Typo="Typo.h6">이번 달 마감 임박 신고</MudText>
|
|
<MudText Typo="Typo.body2">30일 이내 신고 예정 건 (고객명 클릭 시 상세 카드로 연결)</MudText>
|
|
</div>
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary" Href="/taxbaik/admin/tax-filings">전체 일정 보기</MudButton>
|
|
</div>
|
|
<MudSimpleTable Striped="true" Dense="true" Class="admin-table">
|
|
<thead>
|
|
<tr>
|
|
<th>고객</th>
|
|
<th>신고 유형</th>
|
|
<th>기한</th>
|
|
<th>D-day</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var f in upcomingFilings)
|
|
{
|
|
var dday = (f.DueDate.Date - DateTime.Today).Days;
|
|
<tr>
|
|
<td>
|
|
<MudLink Href="@($"/taxbaik/admin/clients/{f.ClientId}")" Underline="Underline.Hover" Color="Color.Primary" Class="font-weight-bold">
|
|
@f.ClientName
|
|
</MudLink>
|
|
</td>
|
|
<td>@f.FilingType</td>
|
|
<td>@f.DueDate.ToString("yyyy-MM-dd")</td>
|
|
<td>
|
|
@if (dday < 0)
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Dark">기한 초과 (@(-dday)일)</MudChip>
|
|
}
|
|
else if (dday <= 7)
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Error">D-@dday</MudChip>
|
|
}
|
|
else
|
|
{
|
|
<span>D-@dday</span>
|
|
}
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</MudSimpleTable>
|
|
</MudPaper>
|
|
}
|
|
|
|
<MudPaper Class="admin-surface mt-4" Elevation="0">
|
|
<div class="admin-section-header">
|
|
<div>
|
|
<MudText Typo="Typo.h6">최근 문의</MudText>
|
|
<MudText Typo="Typo.body2">최근 유입된 상담 요청을 빠르게 확인합니다. (이름 클릭 시 상세 관리 화면으로 연계)</MudText>
|
|
</div>
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary" Href="/taxbaik/admin/inquiries">문의 전체 보기</MudButton>
|
|
</div>
|
|
<MudSimpleTable Striped="true" Dense="true" Class="admin-table">
|
|
<thead>
|
|
<tr>
|
|
<th>이름</th>
|
|
<th>전화</th>
|
|
<th>분야</th>
|
|
<th>상태</th>
|
|
<th>날짜</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var inquiry in summary.RecentInquiries)
|
|
{
|
|
<tr>
|
|
<td>
|
|
<MudLink Href="@($"/taxbaik/admin/inquiries?id={inquiry.Id}")" Underline="Underline.Hover" Color="Color.Primary" Class="font-weight-bold">
|
|
@inquiry.Name
|
|
</MudLink>
|
|
</td>
|
|
<td>@inquiry.Phone</td>
|
|
<td>@inquiry.ServiceType</td>
|
|
<td>
|
|
<MudChip T="string" Size="Size.Small" Color="@StatusColor(inquiry.Status)">
|
|
@GetStatusLabel(inquiry.Status)
|
|
</MudChip>
|
|
</td>
|
|
<td>@inquiry.CreatedAt.ToString("yyyy-MM-dd")</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</MudSimpleTable>
|
|
</MudPaper>
|
|
|
|
@code {
|
|
private AdminDashboardSummary summary = new(0, 0, 0, 0, []);
|
|
private List<Domain.Entities.TaxFiling> upcomingFilings = [];
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
var summaryTask = DashboardService.GetSummaryAsync();
|
|
var filingsTask = FilingService.GetUpcomingAsync(30);
|
|
await Task.WhenAll(summaryTask, filingsTask);
|
|
summary = await summaryTask;
|
|
upcomingFilings = (await filingsTask).ToList();
|
|
}
|
|
|
|
private static string GetStatusLabel(string status) => InquiryStatusMapper.Labels.GetValueOrDefault(status, status);
|
|
|
|
private static Color StatusColor(string status) => status switch
|
|
{
|
|
"new" => Color.Warning,
|
|
"consulting" => Color.Info,
|
|
"contracted" => Color.Success,
|
|
"rejected" => Color.Error,
|
|
"closed" => Color.Dark,
|
|
_ => Color.Default
|
|
};
|
|
}
|