fix(admin): minimize crm render path
TaxBaik CI/CD / build-and-deploy (push) Successful in 2m24s

This commit is contained in:
2026-07-05 19:13:59 +09:00
parent 6a44272f25
commit b7cb442937
3 changed files with 100 additions and 508 deletions
@@ -5,7 +5,6 @@
@inject IConsultingActivityBrowserClient ActivityClient @inject IConsultingActivityBrowserClient ActivityClient
@inject IClientBrowserClient ClientClient @inject IClientBrowserClient ClientClient
@inject ISnackbar Snackbar @inject ISnackbar Snackbar
@inject IDialogService DialogService
@attribute [Authorize] @attribute [Authorize]
<PageTitle>상담 활동 관리</PageTitle> <PageTitle>상담 활동 관리</PageTitle>
@@ -14,11 +13,8 @@
<div> <div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">CRM & 세무관리</MudText> <MudText Typo="Typo.caption" Class="admin-eyebrow">CRM & 세무관리</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">상담 활동 관리</MudText> <MudText Typo="Typo.h4" Class="admin-page-title">상담 활동 관리</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">고객별 상담 이력과 팔로업을 추적합니다.</MudText> <MudText Typo="Typo.body2" Class="admin-page-subtitle">초기 렌더 진단용 최소 화면입니다.</MudText>
</div> </div>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="OpenCreateDialog" StartIcon="@Icons.Material.Filled.Add">
새 활동 기록
</MudButton>
</section> </section>
<AdminDataPanel Loading="@(activities is null)" SkeletonContent="@ActivitySkeleton"> <AdminDataPanel Loading="@(activities is null)" SkeletonContent="@ActivitySkeleton">
@@ -27,102 +23,36 @@
} }
else if (activities.Count == 0) else if (activities.Count == 0)
{ {
<MudAlert Severity="Severity.Info" Class="mt-4"> <MudAlert Severity="Severity.Info" Class="mt-4">상담 활동이 없습니다.</MudAlert>
<MudIcon Icon="@Icons.Material.Filled.Timeline" Class="me-2" />
상담 활동이 없습니다.
</MudAlert>
} }
else else
{ {
<MudDataGrid T="ConsultingActivity" <table class="admin-grid table">
Items="@activities" <thead>
Dense="true" <tr>
Hover="true" <th>ID</th>
Striped="true" <th>고객</th>
<th>활동 유형</th>
RowsPerPage="30" <th>활동일시</th>
Class="admin-grid"> <th>설명</th>
<Columns> </tr>
<PropertyColumn Property="x => x.Id" Title="ID" Sortable="true" /> </thead>
<TemplateColumn Title="고객"> <tbody>
<CellTemplate> @foreach (var item in activities)
@if (clientMap.TryGetValue(context.Item.ClientId, out var clientName))
{ {
<MudLink Href="@($"/taxbaik/admin/clients/{context.Item.ClientId}")" Color="Color.Primary"> <tr>
@clientName <td>@item.Id</td>
</MudLink> <td>@clientMap.GetValueOrDefault(item.ClientId)</td>
<td>@item.ActivityType</td>
<td>@item.ActivityDate.ToString("g")</td>
<td>@item.Description</td>
</tr>
} }
</CellTemplate> </tbody>
</TemplateColumn> </table>
<PropertyColumn Property="x => x.ActivityType" Title="활동 유형" />
<PropertyColumn Property="x => x.ActivityDate" Title="활동일시" Format="g" />
<TemplateColumn Title="설명">
<CellTemplate>
@{
var desc = context.Item.Description ?? "";
if (desc.Length > 30) desc = desc.Substring(0, 30) + "...";
}
<span>@desc</span>
</CellTemplate>
</TemplateColumn>
<TemplateColumn Title="다음 팔로업">
<CellTemplate>
@if (context.Item.NextFollowupDate.HasValue)
{
var daysLeft = (context.Item.NextFollowupDate.Value.Date - DateTime.Today).Days;
<MudChip Size="Size.Small"
Color="@(daysLeft < 0 ? Color.Error : daysLeft <= 3 ? Color.Warning : Color.Success)"
Variant="Variant.Filled">
@context.Item.NextFollowupDate.Value.ToString("yyyy-MM-dd")
</MudChip>
}
</CellTemplate>
</TemplateColumn>
<TemplateColumn Title="작업" Sortable="false">
<CellTemplate>
<MudButtonGroup Size="Size.Small" Variant="Variant.Outlined">
<MudIconButton Icon="@Icons.Material.Filled.Edit" OnClick="@(async () => await OpenEditDialog(context.Item))" />
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error"
OnClick="@(async () => await DeleteActivity(context.Item.Id))" />
</MudButtonGroup>
</CellTemplate>
</TemplateColumn>
</Columns>
</MudDataGrid>
} }
</AdminDataPanel> </AdminDataPanel>
<MudDialog @bind-IsVisible="isDialogOpen" Options="new DialogOptions { MaxWidth = MaxWidth.Small, FullWidth = true }">
<TitleContent>
<MudText Typo="Typo.h6">@(editingActivity == null ? "새 활동 기록" : "활동 기록 수정")</MudText>
</TitleContent>
<DialogContent>
<MudForm @ref="form">
<div class="mb-4">
<label class="mud-input-label mud-input-label-animated mud-input-label-outlined mb-1">고객</label>
<select class="mud-input mud-input-outlined mud-input-root mud-input-root-adorned-start"
style="width: 100%; min-height: 56px; padding: 16px 14px;"
value="@activityForm.ClientId"
@onchange="e => activityForm.ClientId = int.TryParse(e.Value?.ToString(), out var clientId) ? clientId : 0">
<option value="0">고객을 선택하세요.</option>
@foreach (var client in clients)
{
<option value="@client.Id">@GetClientDisplayName(client)</option>
}
</select>
</div>
<CommonCodeSelect @bind-Value="activityForm.ActivityType" Group="CONSULTING_ACTIVITY_TYPE" Label="활동 유형" Class="mb-4" Required="true" />
<MudTextField T="string" Label="활동일" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Required="true" Value="@(activityForm.ActivityDate?.ToString("yyyy-MM-dd HH:mm") ?? string.Empty)" />
<MudTextField T="string" @bind-Value="activityForm.Description" Label="설명" Variant="Variant.Outlined" FullWidth="true" Lines="3" Class="mb-4" Required="true" />
<MudTextField T="string" Label="다음 팔로업일" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Value="@(activityForm.NextFollowupDate?.ToString("yyyy-MM-dd") ?? string.Empty)" />
</MudForm>
</DialogContent>
<DialogActions>
<MudButton OnClick="CloseDialog">취소</MudButton>
<MudButton Color="Color.Primary" OnClick="SaveActivity">저장</MudButton>
</DialogActions>
</MudDialog>
@code { @code {
[CascadingParameter] [CascadingParameter]
private Task<AuthenticationState>? AuthStateTask { get; set; } private Task<AuthenticationState>? AuthStateTask { get; set; }
@@ -130,10 +60,6 @@
private List<ConsultingActivity>? activities; private List<ConsultingActivity>? activities;
private List<Client> clients = []; private List<Client> clients = [];
private Dictionary<int, string> clientMap = new(); private Dictionary<int, string> clientMap = new();
private MudForm? form;
private bool isDialogOpen;
private ConsultingActivity? editingActivity;
private ConsultingActivityForm activityForm = new();
private RenderFragment ActivitySkeleton => builder => private RenderFragment ActivitySkeleton => builder =>
{ {
@@ -178,126 +104,6 @@
} }
} }
private void OpenCreateDialog()
{
editingActivity = null;
activityForm = new ConsultingActivityForm
{
ActivityDate = DateTime.Now,
ClientId = clients.FirstOrDefault()?.Id ?? 0
};
isDialogOpen = true;
}
private async Task OpenEditDialog(ConsultingActivity activity)
{
editingActivity = activity;
activityForm = new ConsultingActivityForm
{
ClientId = activity.ClientId,
ActivityType = activity.ActivityType,
ActivityDate = activity.ActivityDate,
Description = activity.Description,
NextFollowupDate = activity.NextFollowupDate
};
isDialogOpen = true;
}
private async Task SaveActivity()
{
if (form != null)
{
await form.Validate();
if (!form.IsValid)
{
Snackbar.Add("필수 항목을 입력해주세요.", Severity.Warning);
return;
}
}
try
{
if (editingActivity == null)
{
var actDate = activityForm.ActivityDate ?? DateTime.Now;
var newId = await ActivityClient.CreateAsync(
activityForm.ClientId,
activityForm.ActivityType,
actDate,
activityForm.Description,
null,
activityForm.NextFollowupDate);
if (newId > 0)
{
Snackbar.Add("활동이 기록되었습니다.", Severity.Success);
CloseDialog();
await LoadData();
}
}
else
{
await ActivityClient.UpdateAsync(
editingActivity.Id,
null,
activityForm.NextFollowupDate);
Snackbar.Add("활동이 업데이트되었습니다.", Severity.Success);
CloseDialog();
await LoadData();
}
}
catch (Exception ex)
{
Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
}
}
private async Task DeleteActivity(int id)
{
var parameters = new DialogParameters
{
{ "Title", "삭제 확인" },
{ "Message", "이 활동을 삭제하시겠습니까?" }
};
var dialog = await DialogService.ShowAsync<ConfirmDialog>("", parameters);
var result = await dialog.Result;
if (result?.Canceled ?? true)
return;
try
{
await ActivityClient.DeleteAsync(id);
Snackbar.Add("활동이 삭제되었습니다.", Severity.Success);
await LoadData();
}
catch (Exception ex)
{
Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error);
}
}
private void CloseDialog()
{
isDialogOpen = false;
editingActivity = null;
activityForm = new();
}
private static string GetClientDisplayName(Client client) private static string GetClientDisplayName(Client client)
=> !string.IsNullOrWhiteSpace(client.CompanyName) => !string.IsNullOrWhiteSpace(client.CompanyName) ? $"{client.Name} ({client.CompanyName})" : client.Name;
? client.CompanyName
: !string.IsNullOrWhiteSpace(client.Name)
? client.Name
: $"Client #{client.Id}";
private class ConsultingActivityForm
{
public int ClientId { get; set; }
public string ActivityType { get; set; } = "";
public DateTime? ActivityDate { get; set; } = DateTime.Now;
public string Description { get; set; } = "";
public DateTime? NextFollowupDate { get; set; }
}
} }
@@ -5,7 +5,6 @@
@inject IRevenueTrackingBrowserClient RevenueClient @inject IRevenueTrackingBrowserClient RevenueClient
@inject IClientBrowserClient ClientClient @inject IClientBrowserClient ClientClient
@inject ISnackbar Snackbar @inject ISnackbar Snackbar
@inject IDialogService DialogService
@attribute [Authorize] @attribute [Authorize]
<PageTitle>수익 추적 관리</PageTitle> <PageTitle>수익 추적 관리</PageTitle>
@@ -14,11 +13,8 @@
<div> <div>
<MudText Typo="Typo.caption" Class="admin-eyebrow">CRM & 세무관리</MudText> <MudText Typo="Typo.caption" Class="admin-eyebrow">CRM & 세무관리</MudText>
<MudText Typo="Typo.h4" Class="admin-page-title">수익 추적 관리</MudText> <MudText Typo="Typo.h4" Class="admin-page-title">수익 추적 관리</MudText>
<MudText Typo="Typo.body2" Class="admin-page-subtitle">청구, 납부, 미수금 상태를 한 화면에서 관리합니다.</MudText> <MudText Typo="Typo.body2" Class="admin-page-subtitle">초기 렌더 진단용 최소 화면입니다.</MudText>
</div> </div>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="OpenCreateDialog" StartIcon="@Icons.Material.Filled.Add">
새 청구 추가
</MudButton>
</section> </section>
<AdminDataPanel Loading="@(revenues is null)" SkeletonContent="@RevenueSkeleton"> <AdminDataPanel Loading="@(revenues is null)" SkeletonContent="@RevenueSkeleton">
@@ -27,99 +23,36 @@
} }
else if (revenues.Count == 0) else if (revenues.Count == 0)
{ {
<MudAlert Severity="Severity.Info" Class="mt-4"> <MudAlert Severity="Severity.Info" Class="mt-4">청구 기록이 없습니다.</MudAlert>
<MudIcon Icon="@Icons.Material.Filled.Payments" Class="me-2" />
청구 기록이 없습니다.
</MudAlert>
} }
else else
{ {
<MudDataGrid T="RevenueTracking" <table class="admin-grid table">
Items="@revenues" <thead>
Dense="true" <tr>
Hover="true" <th>ID</th>
Striped="true" <th>고객</th>
<th>청구번호</th>
RowsPerPage="30" <th>청구일</th>
Class="admin-grid"> <th>청구액</th>
<Columns> </tr>
<PropertyColumn Property="x => x.Id" Title="ID" Sortable="true" /> </thead>
<TemplateColumn Title="고객"> <tbody>
<CellTemplate> @foreach (var item in revenues)
@if (clientMap.TryGetValue(context.Item.ClientId, out var clientName))
{ {
<MudLink Href="@($"/taxbaik/admin/clients/{context.Item.ClientId}")" Color="Color.Primary"> <tr>
@clientName <td>@item.Id</td>
</MudLink> <td>@clientMap.GetValueOrDefault(item.ClientId)</td>
<td>@item.InvoiceNumber</td>
<td>@item.InvoiceDate.ToString("yyyy-MM-dd")</td>
<td>@item.Amount.ToString("C")</td>
</tr>
} }
</CellTemplate> </tbody>
</TemplateColumn> </table>
<PropertyColumn Property="x => x.InvoiceNumber" Title="청구번호" />
<PropertyColumn Property="x => x.InvoiceDate" Title="청구일" Format="yyyy-MM-dd" />
<PropertyColumn Property="x => x.Amount" Title="청구액" Format="C" />
<TemplateColumn Title="납부여부">
<CellTemplate>
@if (context.Item.PaymentStatus == "paid")
{
<MudChip Size="Size.Small" Color="Color.Success" Variant="Variant.Filled">납부</MudChip>
}
else
{
<MudChip Size="Size.Small" Color="Color.Warning" Variant="Variant.Filled">미납</MudChip>
}
</CellTemplate>
</TemplateColumn>
<TemplateColumn Title="작업" Sortable="false">
<CellTemplate>
<MudButtonGroup Size="Size.Small" Variant="Variant.Outlined">
@if (context.Item.PaymentStatus != "paid")
{
<MudIconButton Icon="@Icons.Material.Filled.CheckCircle" Color="Color.Success"
OnClick="@(async () => await MarkPaid(context.Item.Id))" Title="납부 처리" />
}
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error"
OnClick="@(async () => await DeleteRevenue(context.Item.Id))" />
</MudButtonGroup>
</CellTemplate>
</TemplateColumn>
</Columns>
</MudDataGrid>
} }
</AdminDataPanel> </AdminDataPanel>
<!-- Create Dialog -->
<MudDialog @bind-IsVisible="isDialogOpen" Options="new DialogOptions { MaxWidth = MaxWidth.Small, FullWidth = true }">
<TitleContent>
<MudText Typo="Typo.h6">새 청구 추가</MudText>
</TitleContent>
<DialogContent>
<MudForm @ref="form">
<div class="mb-4">
<label class="mud-input-label mud-input-label-animated mud-input-label-outlined mb-1">고객</label>
<select class="mud-input mud-input-outlined mud-input-root mud-input-root-adorned-start"
style="width: 100%; min-height: 56px; padding: 16px 14px;"
value="@revenueForm.ClientId"
@onchange="e => revenueForm.ClientId = int.TryParse(e.Value?.ToString(), out var clientId) ? clientId : 0">
<option value="0">고객을 선택하세요.</option>
@foreach (var client in clients)
{
<option value="@client.Id">@GetClientDisplayName(client)</option>
}
</select>
</div>
<MudTextField T="string" @bind-Value="revenueForm.InvoiceNumber" Label="청구번호" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Required="true" />
<MudTextField T="string" Label="청구일" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Required="true" Value="@(revenueForm.InvoiceDate?.ToString("yyyy-MM-dd") ?? string.Empty)" />
<MudNumericField T="decimal" @bind-Value="revenueForm.Amount" Label="청구액" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Required="true" />
<CommonCodeSelect @bind-Value="revenueForm.ServiceType" Group="REVENUE_SERVICE_TYPE" Label="서비스 유형" Class="mb-4" />
<MudTextField T="string" Label="납부예정일" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Value="@(revenueForm.DueDate?.ToString("yyyy-MM-dd") ?? string.Empty)" />
</MudForm>
</DialogContent>
<DialogActions>
<MudButton OnClick="CloseDialog">취소</MudButton>
<MudButton Color="Color.Primary" OnClick="SaveRevenue">저장</MudButton>
</DialogActions>
</MudDialog>
@code { @code {
[CascadingParameter] [CascadingParameter]
private Task<AuthenticationState>? AuthStateTask { get; set; } private Task<AuthenticationState>? AuthStateTask { get; set; }
@@ -127,9 +60,6 @@
private List<RevenueTracking>? revenues; private List<RevenueTracking>? revenues;
private List<Client> clients = []; private List<Client> clients = [];
private Dictionary<int, string> clientMap = new(); private Dictionary<int, string> clientMap = new();
private MudForm? form;
private bool isDialogOpen;
private RevenueForm revenueForm = new();
private RenderFragment RevenueSkeleton => builder => private RenderFragment RevenueSkeleton => builder =>
{ {
@@ -174,111 +104,6 @@
} }
} }
private void OpenCreateDialog()
{
revenueForm = new RevenueForm
{
ClientId = clients.FirstOrDefault()?.Id ?? 0,
InvoiceDate = DateTime.Today,
DueDate = DateTime.Today.AddDays(14)
};
isDialogOpen = true;
}
private async Task SaveRevenue()
{
if (form != null)
{
await form.Validate();
if (!form.IsValid)
{
Snackbar.Add("필수 항목을 입력해주세요.", Severity.Warning);
return;
}
}
try
{
var newId = await RevenueClient.CreateAsync(
revenueForm.ClientId,
revenueForm.InvoiceNumber,
revenueForm.InvoiceDate ?? DateTime.Now,
revenueForm.Amount,
revenueForm.ServiceType,
revenueForm.DueDate);
if (newId > 0)
{
Snackbar.Add("청구가 추가되었습니다.", Severity.Success);
CloseDialog();
await LoadData();
}
}
catch (Exception ex)
{
Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
}
}
private async Task MarkPaid(int id)
{
try
{
await RevenueClient.MarkPaidAsync(id, DateTime.Now);
Snackbar.Add("납부가 처리되었습니다.", Severity.Success);
await LoadData();
}
catch (Exception ex)
{
Snackbar.Add($"처리 실패: {ex.Message}", Severity.Error);
}
}
private async Task DeleteRevenue(int id)
{
var parameters = new DialogParameters
{
{ "Title", "삭제 확인" },
{ "Message", "이 청구를 삭제하시겠습니까?" }
};
var dialog = await DialogService.ShowAsync<ConfirmDialog>("", parameters);
var result = await dialog.Result;
if (result?.Canceled ?? true)
return;
try
{
await RevenueClient.DeleteAsync(id);
Snackbar.Add("청구가 삭제되었습니다.", Severity.Success);
await LoadData();
}
catch (Exception ex)
{
Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error);
}
}
private void CloseDialog()
{
isDialogOpen = false;
revenueForm = new();
}
private static string GetClientDisplayName(Client client) private static string GetClientDisplayName(Client client)
=> !string.IsNullOrWhiteSpace(client.CompanyName) => !string.IsNullOrWhiteSpace(client.CompanyName) ? $"{client.Name} ({client.CompanyName})" : client.Name;
? client.CompanyName
: !string.IsNullOrWhiteSpace(client.Name)
? client.Name
: $"Client #{client.Id}";
private class RevenueForm
{
public int ClientId { get; set; }
public string InvoiceNumber { get; set; } = "";
public DateTime? InvoiceDate { get; set; }
public decimal Amount { get; set; }
public string? ServiceType { get; set; }
public DateTime? DueDate { get; set; }
}
} }
@@ -1,100 +1,66 @@
@inject NavigationManager Navigation @inject NavigationManager Navigation
@implements IDisposable @implements IDisposable
<MudLayout Class="admin-shell"> <div class="admin-shell">
<MudAppBar Elevation="0" Class="admin-topbar"> <header class="admin-topbar">
<MudIconButton Icon="@Icons.Material.Filled.Menu" <button type="button" class="admin-menu-button" @onclick="ToggleDrawer">Menu</button>
Color="Color.Inherit"
Edge="Edge.Start"
Class="admin-menu-button"
OnClick="@ToggleDrawer" />
<div class="admin-topbar-title"> <div class="admin-topbar-title">
<MudText Typo="Typo.body2" Class="font-weight-bold admin-brand-text">TaxBaik</MudText> <div class="admin-brand-text">TaxBaik</div>
<MudText Typo="Typo.body2" Class="admin-brand-subtitle">세무회계 관리 대시보드</MudText> <div class="admin-brand-subtitle">세무회계 관리 대시보드</div>
</div> </div>
<MudSpacer />
<div class="admin-topbar-actions"> <div class="admin-topbar-actions">
<MudButton Class="admin-topbar-action" <a class="admin-topbar-action" href="/taxbaik" target="_blank" rel="noopener">공개 사이트</a>
Variant="Variant.Text" <a class="admin-topbar-action" href="/taxbaik/admin/logout">로그아웃</a>
Color="Color.Inherit"
Size="Size.Small"
StartIcon="@Icons.Material.Filled.OpenInNew"
Href="/taxbaik"
Target="_blank">
공개 사이트
</MudButton>
<MudDivider Vertical="true" FlexItem="true" Class="mx-2" />
<MudButton Class="admin-topbar-action"
Variant="Variant.Text"
Color="Color.Error"
Size="Size.Small"
StartIcon="@Icons.Material.Filled.Logout"
Href="/taxbaik/admin/logout">
로그아웃
</MudButton>
</div> </div>
</MudAppBar> </header>
<MudDrawer Open="@drawerOpen" <div class="admin-body">
Elevation="0" @if (drawerOpen)
Variant="DrawerVariant.Responsive" {
Breakpoint="Breakpoint.Md" <aside class="admin-drawer">
Class="admin-drawer">
<div class="admin-drawer-brand"> <div class="admin-drawer-brand">
<div class="admin-brand-mark">T</div> <div class="admin-brand-mark">T</div>
<div> <div>
<MudText Typo="Typo.subtitle1">TaxBaik</MudText> <div class="admin-drawer-title">TaxBaik</div>
<MudText Typo="Typo.caption">세무 운영 콘솔</MudText> <div class="admin-drawer-subtitle">세무 운영 콘솔</div>
</div> </div>
</div> </div>
<MudNavMenu Class="admin-nav"> <nav class="admin-nav">
<MudNavLink Href="/taxbaik/admin/dashboard" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Dashboard" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/dashboard"))">대시보드</MudNavLink> <a href="/taxbaik/admin/dashboard">대시보드</a>
<MudNavGroup Title="CRM & 세무관리" Icon="@Icons.Material.Filled.BusinessCenter" @bind-Expanded="@expandedCRMGroup"> <div class="admin-nav-group">CRM & 세무관리</div>
<MudNavLink Href="/taxbaik/admin/tax-profiles" Icon="@Icons.Material.Filled.Assignment" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/tax-profiles"))">세무 프로필</MudNavLink> <a href="/taxbaik/admin/tax-profiles">세무 프로필</a>
<MudNavLink Href="/taxbaik/admin/tax-filing-schedules" Icon="@Icons.Material.Filled.CalendarMonth" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/tax-filing-schedules"))">신고 일정</MudNavLink> <a href="/taxbaik/admin/tax-filing-schedules">신고 일정</a>
<MudNavLink Href="/taxbaik/admin/contracts" Icon="@Icons.Material.Filled.Description" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/contracts"))">계약 관리</MudNavLink> <a href="/taxbaik/admin/contracts">계약 관리</a>
<MudNavLink Href="/taxbaik/admin/consulting-activities" Icon="@Icons.Material.Filled.ChatBubble" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/consulting-activities"))">상담 활동</MudNavLink> <a href="/taxbaik/admin/consulting-activities">상담 활동</a>
<MudNavLink Href="/taxbaik/admin/revenue-trackings" Icon="@Icons.Material.Filled.Receipt" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/revenue-trackings"))">수익 추적</MudNavLink> <a href="/taxbaik/admin/revenue-trackings">수익 추적</a>
</MudNavGroup> <div class="admin-nav-group">고객 관리</div>
<MudNavGroup Title="고객 관리" Icon="@Icons.Material.Filled.PeopleAlt" @bind-Expanded="@expandedCustomerGroup"> <a href="/taxbaik/admin/clients">고객 카드</a>
<MudNavLink Href="/taxbaik/admin/clients" Icon="@Icons.Material.Filled.ContactPage" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/clients"))">고객 카드</MudNavLink> <a href="/taxbaik/admin/tax-filings">세무신고</a>
<MudNavLink Href="/taxbaik/admin/tax-filings" Icon="@Icons.Material.Filled.Assessment" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/tax-filings"))">세무신고</MudNavLink> <div class="admin-nav-group">홈페이지</div>
</MudNavGroup> <a href="/taxbaik/admin/announcements">공지사항</a>
<MudNavGroup Title="홈페이지" Icon="@Icons.Material.Filled.Home" @bind-Expanded="@expandedWebsiteGroup"> <a href="/taxbaik/admin/faqs">FAQ 관리</a>
<MudNavLink Href="/taxbaik/admin/announcements" Icon="@Icons.Material.Filled.Campaign" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/announcements"))">공지사항</MudNavLink> <a href="/taxbaik/admin/blog">블로그 관리</a>
<MudNavLink Href="/taxbaik/admin/faqs" Icon="@Icons.Material.Filled.QuestionAnswer" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/faqs"))">FAQ 관리</MudNavLink> <a href="/taxbaik/admin/season-simulator">시즌 시뮬레이터</a>
<MudNavLink Href="/taxbaik/admin/blog" Icon="@Icons.Material.Filled.Article" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/blog"))">블로그 관리</MudNavLink> <a href="/taxbaik/admin/inquiries">문의 관리</a>
<MudNavLink Href="/taxbaik/admin/season-simulator" Icon="@Icons.Material.Filled.Preview" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/season-simulator"))">시즌 시뮬레이터</MudNavLink> <a href="/taxbaik/admin/settings">설정</a>
</MudNavGroup> <a href="/taxbaik/admin/common-codes">공통관리</a>
<MudNavLink Href="/taxbaik/admin/inquiries" Icon="@Icons.Material.Filled.Forum" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/inquiries"))">문의 관리</MudNavLink> </nav>
<MudNavLink Href="/taxbaik/admin/settings" Icon="@Icons.Material.Filled.Tune" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/settings"))">설정</MudNavLink> </aside>
<MudNavLink Href="/taxbaik/admin/common-codes" Icon="@Icons.Material.Filled.Category" ClickPreventDefault="true" OnClick="@(() => NavigateTo("/taxbaik/admin/common-codes"))">공통관리</MudNavLink> }
</MudNavMenu>
<div class="admin-drawer-version">
<div class="admin-drawer-version-label">Version</div>
<div class="admin-drawer-version-value">vunknown</div>
<div class="admin-drawer-version-built">unknown</div>
</div>
</MudDrawer>
<MudMainContent Class="admin-main"> <main class="admin-main">
<MudContainer MaxWidth="MaxWidth.False" Class="admin-content"> <div class="admin-content">
@ChildContent @ChildContent
</MudContainer> </div>
</MudMainContent> </main>
</MudLayout> </div>
</div>
@code { @code {
[Parameter] [Parameter]
public RenderFragment? ChildContent { get; set; } public RenderFragment? ChildContent { get; set; }
private bool drawerOpen = true; private bool drawerOpen = true;
private bool expandedCRMGroup = true;
private bool expandedCustomerGroup = false;
private bool expandedWebsiteGroup = false;
protected override void OnInitialized() protected override void OnInitialized()
{ {
@@ -110,11 +76,6 @@
drawerOpen = !drawerOpen; drawerOpen = !drawerOpen;
} }
private void NavigateTo(string url)
{
Navigation.NavigateTo(url);
}
public void Dispose() public void Dispose()
{ {
Navigation.LocationChanged -= OnLocationChanged; Navigation.LocationChanged -= OnLocationChanged;