feat(admin): stabilize blog and admin patterns
TaxBaik CI/CD / build-and-deploy (push) Has been cancelled

This commit is contained in:
2026-07-02 10:46:27 +09:00
parent b3cab87539
commit cb47349a25
67 changed files with 1354 additions and 486 deletions
@@ -1,8 +1,9 @@
@page "/admin/clients/{ClientId:int}"
@attribute [Authorize]
@using TaxBaik.Application.Services
@inject ClientService ClientService
@inject ConsultationService ConsultationService
@using TaxBaik.Web.Services
@using TaxBaik.Web.Services.AdminClients
@inject IClientBrowserClient ClientClient
@inject IConsultingActivityBrowserClient ConsultingClient
@inject NavigationManager Navigation
@inject ISnackbar Snackbar
@@ -102,8 +103,8 @@
<MudDatePicker @bind-Date="newDate" Label="상담일" DateFormat="yyyy-MM-dd" />
</MudItem>
<MudItem xs="12" sm="6">
<MudSelect T="string" @bind-Value="newServiceType" Label="서비스 분야">
@foreach (var t in ClientService.ServiceTypes)
<MudSelect T="string" @bind-Value="newServiceType" Label="서비스 분야">
@foreach (var t in serviceTypes)
{
<MudSelectItem Value="@t">@t</MudSelectItem>
}
@@ -116,7 +117,7 @@
<MudItem xs="12" sm="6">
<MudSelect T="string" @bind-Value="newResult" Label="결과">
<MudSelectItem Value="@("")">-</MudSelectItem>
@foreach (var r in ConsultationService.Results)
@foreach (var r in results)
{
<MudSelectItem Value="@r">@r</MudSelectItem>
}
@@ -182,6 +183,8 @@
private Domain.Entities.Client? client;
private List<Domain.Entities.Consultation> consultations = [];
private static readonly string[] serviceTypes = ["기장대리", "세무조정", "양도세", "증여세", "상속세", "부가세", "종소세", "기타"];
private static readonly string[] results = ["", "상담완료", "추가자료 요청", "견적발송", "계약전환", "보류"];
private bool showAddForm;
private DateTime? newDate = DateTime.Today;
@@ -197,8 +200,19 @@
private async Task LoadAll()
{
client = await ClientService.GetByIdAsync(ClientId);
consultations = (await ConsultationService.GetByClientIdAsync(ClientId)).ToList();
client = await ClientClient.GetByIdAsync(ClientId);
consultations = (await ConsultingClient.GetByClientIdAsync(ClientId))
.Select(c => new Domain.Entities.Consultation
{
Id = c.Id,
ClientId = c.ClientId,
ConsultationDate = c.ActivityDate,
ServiceType = c.ActivityType,
Summary = c.Description,
Result = null,
Fee = null
})
.ToList();
}
private void OpenAddConsultation()
@@ -215,30 +229,35 @@
{
try
{
var c = new Domain.Entities.Consultation
{
ClientId = ClientId,
ConsultationDate = newDate?.ToUniversalTime() ?? DateTime.UtcNow,
ServiceType = string.IsNullOrWhiteSpace(newServiceType) ? null : newServiceType,
Summary = newSummary,
Result = string.IsNullOrWhiteSpace(newResult) ? null : newResult,
Fee = newFee
};
await ConsultationService.CreateAsync(c);
var newId = await ConsultingClient.CreateAsync(
ClientId,
string.IsNullOrWhiteSpace(newServiceType) ? "기타" : newServiceType,
newDate?.ToUniversalTime() ?? DateTime.UtcNow,
newSummary,
null,
null);
if (newId <= 0)
throw new Exception("상담 생성 실패");
showAddForm = false;
consultations = (await ConsultationService.GetByClientIdAsync(ClientId)).ToList();
await LoadAll();
Snackbar.Add("상담이 추가되었습니다.", Severity.Success);
}
catch (ValidationException ex)
{
Snackbar.Add(ex.Message, Severity.Error);
}
catch (Exception ex)
{
Snackbar.Add(ex.Message, Severity.Error);
}
}
private async Task DeleteConsultation(int id)
{
await ConsultationService.DeleteAsync(id);
consultations = (await ConsultationService.GetByClientIdAsync(ClientId)).ToList();
await ConsultingClient.DeleteAsync(id);
await LoadAll();
Snackbar.Add("삭제되었습니다.", Severity.Info);
}
}
@@ -4,6 +4,7 @@
@using TaxBaik.Application.DTOs
@using TaxBaik.Web.Services
@using TaxBaik.Domain.Entities
@using TaxBaik.Web.Components.Admin.Shared
@inject IClientBrowserClient ClientClient
@inject NavigationManager Navigation
@inject ISnackbar Snackbar
@@ -54,20 +55,10 @@
<MudDivider />
</MudItem>
<MudItem xs="12" md="6">
<MudSelect @bind-Value="dto.ServiceType" Label="서비스 유형" T="string" Clearable="true">
@foreach (var t in ClientService.ServiceTypes)
{
<MudSelectItem Value="@t">@t</MudSelectItem>
}
</MudSelect>
<CommonCodeSelect @bind-Value="dto.ServiceType" Group="CLIENT_SERVICE_TYPE" Label="서비스 유형" Clearable="true" />
</MudItem>
<MudItem xs="12" md="6">
<MudSelect @bind-Value="dto.TaxType" Label="세금 유형" T="string" Clearable="true">
@foreach (var t in ClientService.TaxTypes)
{
<MudSelectItem Value="@t">@t</MudSelectItem>
}
</MudSelect>
<CommonCodeSelect @bind-Value="dto.TaxType" Group="CLIENT_TAX_TYPE" Label="세금 유형" Clearable="true" />
</MudItem>
@* 관리 정보 *@
@@ -76,18 +67,10 @@
<MudDivider />
</MudItem>
<MudItem xs="12" md="6">
<MudSelect @bind-Value="dto.Status" Label="상태 *" T="string" Required="true">
<MudSelectItem Value="@("active")">활성</MudSelectItem>
<MudSelectItem Value="@("inactive")">비활성</MudSelectItem>
</MudSelect>
<CommonCodeSelect @bind-Value="dto.Status" Group="CLIENT_STATUS" Label="상태 *" Required="true" />
</MudItem>
<MudItem xs="12" md="6">
<MudSelect @bind-Value="dto.Source" Label="유입 경로" T="string" Clearable="true">
@foreach (var s in ClientService.Sources)
{
<MudSelectItem Value="@s">@s</MudSelectItem>
}
</MudSelect>
<CommonCodeSelect @bind-Value="dto.Source" Group="CLIENT_SOURCE" Label="유입 경로" Clearable="true" />
</MudItem>
<MudItem xs="12">
<MudTextField @bind-Value="dto.Memo" Label="메모"
@@ -119,7 +102,6 @@
private bool isValid;
private bool isLoading = true;
private bool isSaving;
protected override async Task OnInitializedAsync()
{
if (Id.HasValue)
@@ -9,18 +9,15 @@
<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>
<MudButton Variant="Variant.Filled" Color="Color.Primary"
StartIcon="@Icons.Material.Filled.PersonAdd"
Href="/taxbaik/admin/clients/create">
고객 등록
</MudButton>
</section>
<AdminPageHeader Title="고객 관리" Eyebrow="CRM" Subtitle="고객 카드를 등록하고 상담 이력을 관리합니다.">
<ChildContent>
<MudButton Variant="Variant.Filled" Color="Color.Primary"
StartIcon="@Icons.Material.Filled.PersonAdd"
Href="/taxbaik/admin/clients/create">
고객 등록
</MudButton>
</ChildContent>
</AdminPageHeader>
@* 검색/필터 바 *@
<MudPaper Class="admin-surface mb-3 pa-3" Elevation="0">
@@ -53,10 +50,7 @@
}
else if (!clients.Any())
{
<div class="pa-6 text-center">
<MudIcon Icon="@Icons.Material.Filled.PeopleAlt" Style="font-size:3rem; opacity:.3;" />
<MudText Class="mt-2 text-muted">등록된 고객이 없습니다.</MudText>
</div>
<AdminEmptyState Icon="@Icons.Material.Filled.PeopleAlt" Message="등록된 고객이 없습니다." />
}
else
{