Files
taxbaik/TaxBaik.Web/Components/Admin/Pages/ConsultingActivities.razor
T

285 lines
11 KiB
Plaintext

@page "/admin/consulting-activities"
@using TaxBaik.Web.Services.AdminClients
@inject IConsultingActivityBrowserClient ActivityClient
@inject IClientBrowserClient ClientClient
@inject ISnackbar Snackbar
@inject IDialogService DialogService
@attribute [Authorize]
<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" OnClick="OpenCreateDialog" StartIcon="@Icons.Material.Filled.Add">
새 활동 기록
</MudButton>
</section>
<MudPaper Class="admin-surface" Elevation="0">
@if (activities is null)
{
<MudProgressLinear Indeterminate="true" />
}
else if (activities.Count == 0)
{
<MudAlert Severity="Severity.Info" Class="mt-4">
<MudIcon Icon="@Icons.Material.Filled.Timeline" Class="me-2" />
상담 활동이 없습니다.
</MudAlert>
}
else
{
<MudDataGrid T="ConsultingActivity"
Items="@activities"
Dense="true"
Hover="true"
Striped="true"
Virtualize="true"
RowsPerPage="30"
Class="admin-grid">
<Columns>
<PropertyColumn Property="x => x.Id" Title="ID" Sortable="true" />
<TemplateColumn Title="고객">
<CellTemplate>
@if (clientMap.TryGetValue(context.Item.ClientId, out var clientName))
{
<MudLink Href="@($"/taxbaik/admin/clients/{context.Item.ClientId}")" Color="Color.Primary">
@clientName
</MudLink>
}
</CellTemplate>
</TemplateColumn>
<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>
}
</MudPaper>
<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">
<MudSelect T="int" @bind-Value="activityForm.ClientId" Label="고객" Required="true" Variant="Variant.Outlined" FullWidth="true" Class="mb-4">
@foreach (var client in clients)
{
<MudSelectItem Value="@client.Id">@client.CompanyName</MudSelectItem>
}
</MudSelect>
<MudSelect T="string" @bind-Value="activityForm.ActivityType" Label="활동 유형" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Required="true">
<MudSelectItem Value="@("방문 상담")">방문 상담</MudSelectItem>
<MudSelectItem Value="@("전화 상담")">전화 상담</MudSelectItem>
<MudSelectItem Value="@("세무조사 대응 미팅")">세무조사 대응 미팅</MudSelectItem>
<MudSelectItem Value="@("카카오톡 상담")">카카오톡 상담</MudSelectItem>
<MudSelectItem Value="@("이메일 자료 접수")">이메일 자료 접수</MudSelectItem>
<MudSelectItem Value="@("기타")">기타</MudSelectItem>
</MudSelect>
<MudDatePicker @bind-Date="activityForm.ActivityDate" Label="활동일" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Required="true" />
<MudTextField T="string" @bind-Value="activityForm.Description" Label="설명" Variant="Variant.Outlined" FullWidth="true" Lines="3" Class="mb-4" Required="true" />
<MudDatePicker @bind-Date="activityForm.NextFollowupDate" Label="다음 팔로업일" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" />
</MudForm>
</DialogContent>
<DialogActions>
<MudButton OnClick="CloseDialog">취소</MudButton>
<MudButton Color="Color.Primary" OnClick="SaveActivity">저장</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
private Task<AuthenticationState>? AuthStateTask { get; set; }
private List<ConsultingActivity>? activities;
private List<Client> clients = [];
private Dictionary<int, string> clientMap = new();
private MudForm? form;
private bool isDialogOpen;
private ConsultingActivity? editingActivity;
private ConsultingActivityForm activityForm = new();
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (AuthStateTask != null)
{
var authState = await AuthStateTask;
if (authState.User.Identity?.IsAuthenticated == true)
{
await LoadData();
StateHasChanged();
}
}
}
}
private async Task LoadData()
{
try
{
activities = await ActivityClient.GetAllAsync();
var (clientItems, _) = await ClientClient.GetPagedAsync();
clients = clientItems.ToList();
clientMap = clients.ToDictionary(c => c.Id, c => c.CompanyName ?? "");
}
catch (Exception ex)
{
Snackbar.Add($"데이터 로드 실패: {ex.Message}", Severity.Error);
}
}
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 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; }
}
}