Files
taxbaik/TaxBaik.Web/Components/Admin/Pages/Clients/ClientDetail.razor
T
kjh2064 1b173376ee
TaxBaik CI/CD / build-and-deploy (push) Failing after 1m53s
refactor: admin ui를 fluent v5와 html 기반으로 전환
2026-06-29 22:37:40 +09:00

191 lines
8.2 KiB
Plaintext

@page "/admin/clients/{ClientId:int}"
@attribute [Authorize]
@using TaxBaik.Application.Services
@inject ClientService ClientService
@inject ConsultationService ConsultationService
@inject NavigationManager Navigation
@inject IJSRuntime JS
<PageTitle>고객 상세</PageTitle>
<section class="admin-page-hero">
<div>
<div class="admin-eyebrow">Client Details</div>
<h1 class="admin-page-title">고객 상세</h1>
<p class="admin-page-subtitle">고객 정보와 상담 이력을 관리합니다.</p>
</div>
</section>
@if (client == null)
{
<div class="admin-surface mt-4">고객을 찾을 수 없습니다.</div>
}
else
{
<div class="admin-page-actions">
<button type="button" class="site-button secondary" @onclick='() => Navigation.NavigateTo("/taxbaik/admin/clients")'>목록으로</button>
<a class="site-button secondary" href="@($"/taxbaik/admin/clients/{ClientId}/edit")">수정</a>
</div>
<div class="admin-detail-grid">
<section class="admin-surface">
<h3 class="admin-section-title">고객 정보</h3>
<div class="admin-kv-grid">
<div><span>이름</span><strong>@client.Name</strong></div>
<div><span>상호</span><strong>@(client.CompanyName ?? "-")</strong></div>
<div><span>연락처</span><strong>@(client.Phone ?? "-")</strong></div>
<div><span>이메일</span><strong>@(client.Email ?? "-")</strong></div>
<div><span>서비스</span><strong>@(client.ServiceType ?? "-")</strong></div>
<div><span>사업자 유형</span><strong>@(client.TaxType ?? "-")</strong></div>
<div><span>유입 경로</span><strong>@(client.Source ?? "-")</strong></div>
<div><span>등록일</span><strong>@client.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd")</strong></div>
@if (!string.IsNullOrWhiteSpace(client.Memo))
{
<div class="span-2"><span>메모</span><strong style="white-space: pre-wrap;">@client.Memo</strong></div>
}
</div>
</section>
<section class="admin-surface">
<div class="admin-section-header compact">
<div>
<h3 class="admin-section-title">상담 이력</h3>
</div>
<button type="button" class="site-button primary" @onclick="OpenAddConsultation">+ 상담 추가</button>
</div>
@if (showAddForm)
{
<form class="admin-dialog-card mb-4" @onsubmit="AddConsultation" @onsubmit:preventDefault="true">
<label>상담일 <input class="admin-input" type="text" placeholder="2026-06-29" @bind="ConsultationDateText" /></label>
<label>서비스 분야
<select class="admin-input" @bind="newServiceType">
<option value="">선택하세요</option>
@foreach (var t in ClientService.ServiceTypes)
{
<option value="@t">@t</option>
}
</select>
</label>
<label>상담 내용 * <textarea class="admin-input" rows="3" @bind="newSummary"></textarea></label>
<label>결과
<select class="admin-input" @bind="newResult">
<option value="">-</option>
@foreach (var r in ConsultationService.Results)
{
<option value="@r">@r</option>
}
</select>
</label>
<label>수임료 (원) <input class="admin-input" type="text" placeholder="100000" @bind="FeeText" /></label>
<div class="admin-dialog-actions">
<button type="submit" class="site-button primary">저장</button>
<button type="button" class="site-button secondary" @onclick='() => showAddForm = false'>취소</button>
</div>
</form>
}
@if (consultations.Count == 0)
{
<p class="muted">상담 이력이 없습니다.</p>
}
else
{
<div class="admin-activity-list">
@foreach (var c in consultations)
{
<article class="admin-activity-card">
<div class="admin-activity-head">
<div>
<span class="muted">@c.ConsultationDate.ToString("yyyy-MM-dd") @(string.IsNullOrEmpty(c.ServiceType) ? "" : $"· {c.ServiceType}")</span>
</div>
<button type="button" class="admin-icon-button danger" @onclick="@(() => DeleteConsultation(c.Id))">✕</button>
</div>
<p style="white-space: pre-wrap;">@c.Summary</p>
@if (!string.IsNullOrEmpty(c.Result))
{
<span class="status-pill info">@c.Result</span>
}
@if (c.Fee.HasValue)
{
<div class="muted">수임료: @c.Fee.Value.ToString("N0")원</div>
}
</article>
}
</div>
}
</section>
</div>
}
@code {
[Parameter] public int ClientId { get; set; }
private Domain.Entities.Client? client;
private List<Domain.Entities.Consultation> consultations = [];
private bool showAddForm;
private DateTime? newDate = DateTime.Today;
private string newServiceType = "";
private string newSummary = "";
private string newResult = "";
private decimal? newFee;
private string ConsultationDateText { get => newDate?.ToString("yyyy-MM-dd") ?? ""; set => newDate = DateTime.TryParse(value, out var dt) ? dt : null; }
private string FeeText { get => newFee?.ToString() ?? ""; set => newFee = decimal.TryParse(value, out var d) ? d : null; }
protected override async Task OnInitializedAsync() => await LoadAll();
private async Task LoadAll()
{
client = await ClientService.GetByIdAsync(ClientId);
consultations = (await ConsultationService.GetByClientIdAsync(ClientId)).ToList();
}
private void OpenAddConsultation()
{
showAddForm = true;
newDate = DateTime.Today;
newServiceType = "";
newSummary = "";
newResult = "";
newFee = null;
}
private async Task AddConsultation()
{
try
{
if (string.IsNullOrWhiteSpace(newSummary))
{
await JS.InvokeVoidAsync("alert", "상담 내용을 입력하세요.");
return;
}
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);
showAddForm = false;
consultations = (await ConsultationService.GetByClientIdAsync(ClientId)).ToList();
await JS.InvokeVoidAsync("alert", "상담이 추가되었습니다.");
}
catch (ValidationException ex)
{
await JS.InvokeVoidAsync("alert", ex.Message);
}
}
private async Task DeleteConsultation(int id)
{
if (!await JS.InvokeAsync<bool>("confirm", "이 상담을 삭제하시겠습니까?")) return;
await ConsultationService.DeleteAsync(id);
consultations = (await ConsultationService.GetByClientIdAsync(ClientId)).ToList();
await JS.InvokeVoidAsync("alert", "삭제되었습니다.");
}
}