191 lines
8.2 KiB
Plaintext
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", "삭제되었습니다.");
|
|
}
|
|
}
|