feat: CRM Phase 1-2 완성 + 시즌 시뮬레이터 + 개인정보처리방침/이용약관
- WBS-CRM-02: 상담 이력 (consultations 테이블 V008, ClientDetail.razor) - WBS-CRM-03: 문의→고객 전환 (V009 client_id FK, InquiryDetail 고객등록 버튼) - WBS-CRM-04: 신고 일정 캘린더 (tax_filings 테이블 V010, TaxFilingList.razor) - WBS-CRM-05: 문의 상태 5단계 확장 (V011, InquiryStatus enum, InquiryList 탭) - WBS-MKT-04: 시즌 시뮬레이터 어드민 페이지 (SeasonSimulator.razor) - WBS-UX-04: 개인정보처리방침 /taxbaik/privacy, 이용약관 /taxbaik/terms - Dashboard.razor 마감 임박 신고 위젯 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
@page "/admin/clients/{ClientId:int}"
|
||||
@attribute [Authorize]
|
||||
@using TaxBaik.Application.Services
|
||||
@inject ClientService ClientService
|
||||
@inject ConsultationService ConsultationService
|
||||
@inject NavigationManager Navigation
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<PageTitle>고객 상세</PageTitle>
|
||||
|
||||
@if (client == null)
|
||||
{
|
||||
<MudText>고객을 찾을 수 없습니다.</MudText>
|
||||
return;
|
||||
}
|
||||
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Class="mb-4" Spacing="2">
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.ArrowBack"
|
||||
@onclick="@(() => Navigation.NavigateTo("/taxbaik/admin/clients"))">
|
||||
목록으로
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Warning"
|
||||
StartIcon="@Icons.Material.Filled.Edit"
|
||||
Href="@($"/taxbaik/admin/clients/{ClientId}/edit")">
|
||||
수정
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
|
||||
<MudGrid>
|
||||
<MudItem xs="12" md="5">
|
||||
<MudPaper Class="pa-4" Elevation="1">
|
||||
<MudText Typo="Typo.h6" Class="mb-3">고객 정보</MudText>
|
||||
<MudGrid Spacing="2">
|
||||
<MudItem xs="6">
|
||||
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">이름</MudText>
|
||||
<MudText>@client.Name</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="6">
|
||||
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">상호</MudText>
|
||||
<MudText>@(client.CompanyName ?? "-")</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="6">
|
||||
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">연락처</MudText>
|
||||
<MudText>@(client.Phone ?? "-")</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="6">
|
||||
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">이메일</MudText>
|
||||
<MudText>@(client.Email ?? "-")</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="6">
|
||||
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">서비스</MudText>
|
||||
<MudText>@(client.ServiceType ?? "-")</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="6">
|
||||
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">사업자 유형</MudText>
|
||||
<MudText>@(client.TaxType ?? "-")</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="6">
|
||||
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">유입 경로</MudText>
|
||||
<MudText>@(client.Source ?? "-")</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="6">
|
||||
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">등록일</MudText>
|
||||
<MudText>@client.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd")</MudText>
|
||||
</MudItem>
|
||||
@if (!string.IsNullOrWhiteSpace(client.Memo))
|
||||
{
|
||||
<MudItem xs="12">
|
||||
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">메모</MudText>
|
||||
<MudText Style="white-space: pre-wrap;">@client.Memo</MudText>
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" md="7">
|
||||
<MudPaper Class="pa-4" Elevation="1">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Class="mb-3">
|
||||
<MudText Typo="Typo.h6">상담 이력</MudText>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary"
|
||||
Size="Size.Small"
|
||||
OnClick="OpenAddConsultation">
|
||||
+ 상담 추가
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
|
||||
@if (showAddForm)
|
||||
{
|
||||
<MudPaper Class="pa-3 mb-3" Outlined="true">
|
||||
<MudGrid Spacing="2">
|
||||
<MudItem xs="12" sm="6">
|
||||
<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)
|
||||
{
|
||||
<MudSelectItem Value="@t">@t</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudTextField T="string" @bind-Value="newSummary" Label="상담 내용 *"
|
||||
Lines="3" Variant="Variant.Outlined" Required="true" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6">
|
||||
<MudSelect T="string" @bind-Value="newResult" Label="결과">
|
||||
<MudSelectItem Value="@("")">-</MudSelectItem>
|
||||
@foreach (var r in ConsultationService.Results)
|
||||
{
|
||||
<MudSelectItem Value="@r">@r</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6">
|
||||
<MudNumericField T="decimal?" @bind-Value="newFee" Label="수임료 (원)"
|
||||
Format="N0" />
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
<MudStack Row="true" Class="mt-2" Spacing="2">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="AddConsultation">저장</MudButton>
|
||||
<MudButton Variant="Variant.Outlined" OnClick="@(() => showAddForm = false)">취소</MudButton>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
|
||||
@if (consultations.Count == 0)
|
||||
{
|
||||
<MudText Color="Color.Secondary">상담 이력이 없습니다.</MudText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudList T="string" Dense="true">
|
||||
@foreach (var c in consultations)
|
||||
{
|
||||
<MudListItem>
|
||||
<MudPaper Class="pa-3" Outlined="true" Style="width:100%">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Start" Justify="Justify.SpaceBetween">
|
||||
<div>
|
||||
<MudText Typo="Typo.caption" Color="Color.Secondary">
|
||||
@c.ConsultationDate.ToString("yyyy-MM-dd")
|
||||
@if (!string.IsNullOrEmpty(c.ServiceType)) { <text> · @c.ServiceType</text> }
|
||||
</MudText>
|
||||
<MudText Style="white-space: pre-wrap;" Class="mt-1">@c.Summary</MudText>
|
||||
@if (!string.IsNullOrEmpty(c.Result))
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Info" Class="mt-1">@c.Result</MudChip>
|
||||
}
|
||||
@if (c.Fee.HasValue)
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="mt-1">
|
||||
수임료: @c.Fee.Value.ToString("N0")원
|
||||
</MudText>
|
||||
}
|
||||
</div>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||
Size="Size.Small" Color="Color.Error"
|
||||
OnClick="@(() => DeleteConsultation(c.Id))" />
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
}
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
@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;
|
||||
|
||||
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
|
||||
{
|
||||
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();
|
||||
Snackbar.Add("상담이 추가되었습니다.", Severity.Success);
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
Snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteConsultation(int id)
|
||||
{
|
||||
await ConsultationService.DeleteAsync(id);
|
||||
consultations = (await ConsultationService.GetByClientIdAsync(ClientId)).ToList();
|
||||
Snackbar.Add("삭제되었습니다.", Severity.Info);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user