@page "/admin/consulting-activities" @using TaxBaik.Web.Services.AdminClients @inject IConsultingActivityBrowserClient ActivityClient @inject IClientBrowserClient ClientClient @inject ISnackbar Snackbar @inject IDialogService DialogService @attribute [Authorize] 상담 활동 관리
CRM & 세무관리 상담 활동 관리 고객별 상담 이력과 팔로업을 추적합니다.
새 활동 기록
@if (activities is null) { } else if (activities.Count == 0) { 상담 활동이 없습니다. } else { @if (clientMap.TryGetValue(context.Item.ClientId, out var clientName)) { @clientName } @{ var desc = context.Item.Description ?? ""; if (desc.Length > 30) desc = desc.Substring(0, 30) + "..."; } @desc @if (context.Item.NextFollowupDate.HasValue) { var daysLeft = (context.Item.NextFollowupDate.Value.Date - DateTime.Today).Days; @context.Item.NextFollowupDate.Value.ToString("yyyy-MM-dd") } } @(editingActivity == null ? "새 활동 기록" : "활동 기록 수정") @foreach (var client in clients) { @GetClientDisplayName(client) } 방문 상담 전화 상담 세무조사 대응 미팅 카카오톡 상담 이메일 자료 접수 기타 취소 저장 @code { [CascadingParameter] private Task? AuthStateTask { get; set; } private List? activities; private List clients = []; private Dictionary 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(pageSize: 1000); clients = clientItems.ToList(); clientMap = clients.ToDictionary(c => c.Id, GetClientDisplayName); } 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("", 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 static string GetClientDisplayName(Client client) => !string.IsNullOrWhiteSpace(client.CompanyName) ? client.CompanyName : !string.IsNullOrWhiteSpace(client.Name) ? client.Name : $"Client #{client.Id}"; 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; } } }