Refactor admin dashboard and standardize UI shells
TaxBaik CI/CD / build-and-deploy (push) Successful in 2m46s

This commit is contained in:
2026-07-07 23:16:31 +09:00
parent fc655e20c1
commit 08347a4e20
35 changed files with 625 additions and 230 deletions
@@ -12,14 +12,9 @@
<PageTitle>@(Id.HasValue ? "공지 수정" : "공지 등록")</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">Homepage</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">@(Id.HasValue ? "공지 수정" : "공지 등록")</MudText>
</div>
</section>
<AdminPageHeader Title="@(Id.HasValue ? "공지 수정" : "공지 등록")" Eyebrow="Homepage" />
<MudPaper Class="admin-surface" Elevation="0">
<AdminEditorPanel Loading="@isSaving" SkeletonContent="@null">
<MudForm @ref="form">
<MudGrid>
<MudItem xs="12">
@@ -89,7 +84,7 @@
</MudButton>
</div>
</MudForm>
</MudPaper>
</AdminEditorPanel>
@code {
[Parameter] public int? Id { get; set; }
@@ -10,23 +10,20 @@
<PageTitle>공지사항 관리</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">Homepage</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">공지사항 관리</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">홈페이지 상단에 노출되는 공지사항을 등록하고 관리합니다.</MudText>
</div>
<AdminPageHeader Title="공지사항 관리" Eyebrow="Homepage" Subtitle="홈페이지 상단에 노출되는 공지사항을 등록하고 관리합니다.">
<MudButton Variant="Variant.Filled" Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Add"
Href="/taxbaik/admin/announcements/create">
공지 등록
</MudButton>
</section>
</AdminPageHeader>
<div class="d-flex pa-4 gap-4 align-center">
<MudTextField @bind-Value="searchQuery" Placeholder="공지사항 제목 검색..." Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="flex-grow-1" Immediate="true" Clearable="true" />
</div>
<AdminSearchBar>
<SearchContent>
<MudTextField @bind-Value="searchQuery" Placeholder="공지사항 제목 검색..." Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="flex-grow-1" Immediate="true" Clearable="true" />
</SearchContent>
</AdminSearchBar>
<AdminDataPanel Loading="@(announcements is null)" SkeletonContent="@AnnouncementSkeleton">
@if (announcements is null)
@@ -11,13 +11,7 @@
<PageTitle>고객 상세</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">Client Details</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">고객 상세</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">고객 정보와 상담 이력을 관리합니다.</MudText>
</div>
</section>
<AdminPageHeader Title="고객 상세" Eyebrow="Client Details" Subtitle="고객 정보와 상담 이력을 관리합니다." />
@if (client == null)
{
@@ -12,14 +12,10 @@
<PageTitle>@(Id.HasValue ? "고객 수정" : "고객 등록")</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">CRM</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">@(Id.HasValue ? "고객 수정" : "고객 등록")</MudText>
</div>
<AdminPageHeader Title="@(Id.HasValue ? "고객 수정" : "고객 등록")" Eyebrow="CRM">
<MudButton Variant="Variant.Outlined" Href="/taxbaik/admin/clients"
StartIcon="@Icons.Material.Filled.ArrowBack">목록으로</MudButton>
</section>
</AdminPageHeader>
<AdminEditorPanel Loading="@isLoading" SkeletonContent="@ClientEditSkeleton">
@if (isLoading)
@@ -21,24 +21,24 @@
</AdminPageHeader>
@* 검색/필터 바 *@
<MudPaper Class="admin-surface mb-3 pa-3" Elevation="0">
<MudGrid>
<MudItem xs="12" md="5">
<MudTextField @bind-Value="searchText" Label="검색 (이름·연락처·회사명)"
Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Search"
Immediate="false" OnKeyUp="@OnSearchKeyUp" />
</MudItem>
<MudItem xs="12" md="3">
<CommonCodeSelect @bind-Value="statusFilter" Group="CLIENT_STATUS" Label="상태" Placeholder="전체" Clearable="true" />
</MudItem>
<MudItem xs="12" md="2" Class="d-flex align-center">
<MudButton Variant="Variant.Outlined" OnClick="@SearchAsync" FullWidth="true">검색</MudButton>
</MudItem>
<MudItem xs="12" md="2" Class="d-flex align-center">
<MudButton Variant="Variant.Text" OnClick="@ResetAsync" FullWidth="true">초기화</MudButton>
</MudItem>
</MudGrid>
</MudPaper>
<AdminSearchBar>
<SearchContent>
<MudGrid>
<MudItem xs="12" md="5">
<MudTextField @bind-Value="searchText" Label="검색 (이름·연락처·회사명)"
Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Search"
Immediate="false" OnKeyUp="@OnSearchKeyUp" />
</MudItem>
<MudItem xs="12" md="3">
<CommonCodeSelect @bind-Value="statusFilter" Group="CLIENT_STATUS" Label="상태" Placeholder="전체" Clearable="true" />
</MudItem>
</MudGrid>
</SearchContent>
<Actions>
<MudButton Variant="Variant.Outlined" OnClick="@SearchAsync">검색</MudButton>
<MudButton Variant="Variant.Text" OnClick="@ResetAsync">초기화</MudButton>
</Actions>
</AdminSearchBar>
<AdminDataPanel Loading="@(clients is null)" SkeletonContent="@ClientListSkeleton">
@if (clients is null)
@@ -8,14 +8,9 @@
<PageTitle>고객사 등록</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">Settings</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">새 고객사 등록</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">새로운 고객사를 추가합니다.</MudText>
</div>
<AdminPageHeader Title="새 고객사 등록" Eyebrow="Settings" Subtitle="새로운 고객사를 추가합니다.">
<MudButton Variant="Variant.Outlined" StartIcon="@Icons.Material.Filled.Close" @onclick="GoBack">취소</MudButton>
</section>
</AdminPageHeader>
<MudPaper Class="pa-4 mt-4" Elevation="1">
<CompanyForm ButtonText="등록" OnSubmit="HandleCreate" OnCancel="GoBack" />
@@ -9,14 +9,9 @@
<PageTitle>고객사 수정</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">Settings</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">고객사 수정</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">고객사 정보를 수정합니다.</MudText>
</div>
<AdminPageHeader Title="고객사 수정" Eyebrow="Settings" Subtitle="고객사 정보를 수정합니다.">
<MudButton Variant="Variant.Outlined" StartIcon="@Icons.Material.Filled.Close" @onclick="GoBack">취소</MudButton>
</section>
</AdminPageHeader>
<AdminEditorPanel Loading="@isLoading" SkeletonContent="@CompanySkeleton">
@if (formModel == null)
@@ -6,15 +6,10 @@
<PageTitle>고객사 관리</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">Settings</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">고객사 관리</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">등록된 고객사를 관리하고 새로운 고객사를 추가합니다.</MudText>
</div>
<AdminPageHeader Title="고객사 관리" Eyebrow="Settings" Subtitle="등록된 고객사를 관리하고 새로운 고객사를 추가합니다.">
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add"
Href="/taxbaik/admin/companies/create">새 고객사 등록</MudButton>
</section>
</AdminPageHeader>
<MudPaper Class="admin-surface mb-4 mt-4" Elevation="0">
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
@@ -9,13 +9,7 @@
<PageTitle>상담 활동 관리</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">CRM & 세무관리</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">상담 활동 관리</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">초기 렌더 진단용 최소 화면입니다.</MudText>
</div>
</section>
<AdminPageHeader Title="상담 활동 관리" Eyebrow="CRM & 세무관리" Subtitle="초기 렌더 진단용 최소 화면입니다." />
<AdminDataPanel Loading="@(activities is null)" SkeletonContent="@ActivitySkeleton">
@if (activities is null)
@@ -10,23 +10,17 @@
<PageTitle>계약 관리</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">CRM & 세무관리</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">계약 관리</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">고객 계약과 월 정기수익을 함께 관리합니다.</MudText>
<AdminPageHeader Title="계약 관리" Eyebrow="CRM & 세무관리" Subtitle="고객 계약과 월 정기수익을 함께 관리합니다.">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
@if (mrr > 0)
{
<MudText Typo="Typo.body2" Class="mt-2">
월 정기수익:
<MudChip T="string" Size="Size.Small" Color="Color.Primary" Variant="Variant.Filled">₩@mrr.ToString("N0")</MudChip>
</MudText>
<MudChip T="string" Size="Size.Small" Color="Color.Primary" Variant="Variant.Filled">월 정기수익 ₩@mrr.ToString("N0")</MudChip>
}
</div>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="PrepareCreate" StartIcon="@Icons.Material.Filled.Add" id="btn-add-contract">
새 계약 추가
</MudButton>
</section>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="PrepareCreate" StartIcon="@Icons.Material.Filled.Add" id="btn-add-contract">
새 계약 추가
</MudButton>
</MudStack>
</AdminPageHeader>
<AdminEditorPanel Loading="@(contracts is null)" SkeletonContent="@ContractSkeleton">
@if (contracts is null)
@@ -1,14 +1,5 @@
@page "/admin/dashboard"
@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
@attribute [Authorize]
<PageTitle>대시보드</PageTitle>
<section style="padding: 24px;">
<h1 style="margin: 0 0 12px 0; font-size: 1.8rem;">대시보드</h1>
<p style="margin: 0 0 12px 0;">관리자 SPA 부팅 검증용 최소 화면입니다.</p>
<ul>
<li>WASM 부팅 성공 여부</li>
<li>라우팅 성공 여부</li>
<li>JS interop 예외 여부</li>
</ul>
</section>
<DashboardOverview />
@@ -0,0 +1,240 @@
@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>
<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="이번 배포에서 확인해야 할 핵심 포인트입니다." />
<div class="admin-note-list">
<div>대시보드와 메뉴가 역할 기반으로 필터링됩니다.</div>
<div>공통 CRUD 화면은 `AdminPageHeader`/`AdminCrudPageShell` 슬롯 패턴을 따릅니다.</div>
<div>월간 통계는 문의 전환 흐름을 빠르게 보는 용도입니다.</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));
}
@@ -9,14 +9,10 @@
<PageTitle>@(Id.HasValue ? "FAQ 수정" : "FAQ 등록")</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">홈페이지</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">@(Id.HasValue ? "FAQ 수정" : "FAQ 등록")</MudText>
</div>
<AdminPageHeader Title="@(Id.HasValue ? "FAQ 수정" : "FAQ 등록")" Eyebrow="홈페이지">
<MudButton Variant="Variant.Outlined" Href="/taxbaik/admin/faqs"
StartIcon="@Icons.Material.Filled.ArrowBack">목록으로</MudButton>
</section>
</AdminPageHeader>
<AdminEditorPanel Loading="@isLoading" SkeletonContent="@FaqSkeleton">
<MudPaper Class="admin-surface" Elevation="0" Style="max-width:720px;">
@@ -10,23 +10,20 @@
<PageTitle>FAQ 관리</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">홈페이지</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">FAQ 관리</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">홈페이지 자주 묻는 질문을 등록하고 순서를 관리합니다.</MudText>
</div>
<AdminPageHeader Title="FAQ 관리" Eyebrow="홈페이지" Subtitle="홈페이지 자주 묻는 질문을 등록하고 순서를 관리합니다.">
<MudButton Variant="Variant.Filled" Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Add"
Href="/taxbaik/admin/faqs/create">
FAQ 등록
</MudButton>
</section>
</AdminPageHeader>
<div class="d-flex pa-4 gap-4 align-center">
<MudTextField @bind-Value="searchQuery" Placeholder="질문 또는 답변 검색..." Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="flex-grow-1" Immediate="true" Clearable="true" />
</div>
<AdminSearchBar>
<SearchContent>
<MudTextField @bind-Value="searchQuery" Placeholder="질문 또는 답변 검색..." Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="flex-grow-1" Immediate="true" Clearable="true" />
</SearchContent>
</AdminSearchBar>
<AdminDataPanel Loading="@(faqs is null)" SkeletonContent="@FaqListSkeleton">
@if (faqs is null)
@@ -9,13 +9,7 @@
<PageTitle>문의 상세</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">Inquiry Details</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">문의 상세</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">문의 정보를 확인하고 처리 상태를 관리합니다.</MudText>
</div>
</section>
<AdminPageHeader Title="문의 상세" Eyebrow="Inquiry Details" Subtitle="문의 정보를 확인하고 처리 상태를 관리합니다." />
@if (inquiry != null)
{
@@ -9,13 +9,7 @@
<PageTitle>수익 추적 관리</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">CRM & 세무관리</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">수익 추적 관리</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">초기 렌더 진단용 최소 화면입니다.</MudText>
</div>
</section>
<AdminPageHeader Title="수익 추적 관리" Eyebrow="CRM & 세무관리" Subtitle="청구 기록과 수익 흐름을 확인합니다." />
<AdminDataPanel Loading="@(revenues is null)" SkeletonContent="@RevenueSkeleton">
@if (revenues is null)
@@ -6,13 +6,7 @@
<PageTitle>시즌 시뮬레이터</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">Season Preview</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">시즌 시뮬레이터</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">날짜를 선택해 홈페이지 시즌 화면이 어떻게 보이는지 미리 확인합니다.</MudText>
</div>
</section>
<AdminPageHeader Title="시즌 시뮬레이터" Eyebrow="Season Preview" Subtitle="날짜를 선택해 홈페이지 시즌 화면이 어떻게 보이는지 미리 확인합니다." />
<MudGrid>
<MudItem xs="12" md="4">
@@ -9,19 +9,11 @@
<PageTitle>설정</PageTitle>
<MudContainer MaxWidth="MaxWidth.Large" Class="pa-6">
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">System</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">설정</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">공개 사이트 연락처와 관리자 계정 보안을 관리합니다.</MudText>
</div>
</section>
</MudContainer>
<AdminPageHeader Title="설정" Eyebrow="System" Subtitle="공개 사이트 연락처와 관리자 계정 보안을 관리합니다." />
<MudGrid>
<MudItem xs="12" md="7">
<MudPaper Class="admin-surface" Elevation="0">
<AdminEditorPanel Loading="@isLoadingSettings">
<div class="admin-section-header compact">
<div>
<MudText Typo="Typo.h6">사이트 정보</MudText>
@@ -45,11 +37,11 @@
StartIcon="@Icons.Material.Filled.Save"
@onclick="SaveSettings">사이트 정보 저장</MudButton>
</MudForm>
</MudPaper>
</AdminEditorPanel>
</MudItem>
<MudItem xs="12" md="5">
<MudPaper Class="admin-surface admin-account-card" Elevation="0">
<AdminEditorPanel Loading="@isLoadingSettings">
<div class="admin-section-header compact">
<div>
<MudText Typo="Typo.h6">계정 관리</MudText>
@@ -74,7 +66,7 @@
@(isChangingPassword ? "변경 중..." : "비밀번호 변경")
</MudButton>
</MudForm>
</MudPaper>
</AdminEditorPanel>
</MudItem>
</MudGrid>
@@ -11,16 +11,11 @@
<PageTitle>신고 일정</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">CRM & 세무관리</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">신고 일정</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">고객별 마감일과 처리 상태를 한 화면에서 관리합니다.</MudText>
</div>
<AdminPageHeader Title="신고 일정" Eyebrow="CRM & 세무관리" Subtitle="고객별 마감일과 처리 상태를 한 화면에서 관리합니다.">
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="PrepareCreate" StartIcon="@Icons.Material.Filled.Add" id="btn-add-schedule">
새 일정 추가
</MudButton>
</section>
</AdminPageHeader>
<AdminDataPanel Loading="@(schedules is null)" SkeletonContent="@ScheduleSkeleton">
@if (schedules is null)
@@ -10,18 +10,13 @@
<PageTitle>신고 일정 관리</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">Tax Schedule</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">신고 일정</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">고객별 세금 신고 마감일을 관리하고 완료 처리합니다.</MudText>
</div>
<AdminPageHeader Title="신고 일정" Eyebrow="Tax Schedule" Subtitle="고객별 세금 신고 마감일을 관리하고 완료 처리합니다.">
<MudButton Variant="Variant.Filled" Color="Color.Primary"
OnClick="@(() => showAddForm = !showAddForm)"
StartIcon="@Icons.Material.Filled.Add">
일정 추가
</MudButton>
</section>
</AdminPageHeader>
@if (showAddForm)
{
@@ -11,16 +11,11 @@
<PageTitle>세무 프로필</PageTitle>
<section class="admin-page-hero">
<div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">CRM & 세무관리</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">세무 프로필</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">고객별 세무 프로필, 신고 일정, 위험도 추적</MudText>
</div>
<AdminPageHeader Title="세무 프로필" Eyebrow="CRM & 세무관리" Subtitle="고객별 세무 프로필, 신고 일정, 위험도 추적">
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="PrepareCreate" StartIcon="@Icons.Material.Filled.Add" id="btn-add-profile">
새 프로필 추가
</MudButton>
</section>
</AdminPageHeader>
<AdminDataPanel Loading="@(profiles == null)" SkeletonContent="@ProfileSkeleton">
@if (profiles == null)