-
- @(isSaving ? "저장 중..." : "저장")
-
-
Navigation.NavigateTo("/taxbaik/admin/announcements"))">
- 취소
-
+
@code {
[Parameter] public int? Id { get; set; }
-
- private MudForm? form;
private bool isSaving;
private DateTime? startsAtDate;
private DateTime? endsAtDate;
-
private AnnouncementDto model = new();
+ private string StartsAtText { get => startsAtDate?.ToString("yyyy-MM-dd") ?? ""; set => startsAtDate = DateTime.TryParse(value, out var dt) ? dt : null; }
+ private string EndsAtText { get => endsAtDate?.ToString("yyyy-MM-dd") ?? ""; set => endsAtDate = DateTime.TryParse(value, out var dt) ? dt : null; }
protected override async Task OnInitializedAsync()
{
@@ -115,15 +61,15 @@
}
model = new AnnouncementDto
{
- Id = entity.Id,
- Title = entity.Title,
- Content = entity.Content,
+ Id = entity.Id,
+ Title = entity.Title,
+ Content = entity.Content,
DisplayType = entity.DisplayType,
- IsActive = entity.IsActive,
- SortOrder = entity.SortOrder
+ IsActive = entity.IsActive,
+ SortOrder = entity.SortOrder
};
startsAtDate = entity.StartsAt?.ToLocalTime();
- endsAtDate = entity.EndsAt?.ToLocalTime();
+ endsAtDate = entity.EndsAt?.ToLocalTime();
}
catch
{
@@ -134,41 +80,18 @@
private async Task SaveAsync()
{
- if (form is null) return;
- await form.Validate();
- if (!form.IsValid) return;
-
isSaving = true;
try
{
- model.StartsAt = startsAtDate.HasValue
- ? DateTime.SpecifyKind(startsAtDate.Value.Date, DateTimeKind.Local).ToUniversalTime()
- : null;
- model.EndsAt = endsAtDate.HasValue
- ? DateTime.SpecifyKind(endsAtDate.Value.Date.AddDays(1).AddSeconds(-1), DateTimeKind.Local).ToUniversalTime()
- : null;
-
- if (Id.HasValue)
- {
- var result = await AnnouncementClient.UpdateAsync(Id.Value, model);
- if (result != null)
- Snackbar.Add("공지사항이 저장되었습니다.", Severity.Success);
- else
- Snackbar.Add("저장 실패", Severity.Error);
- }
- else
- {
- var result = await AnnouncementClient.CreateAsync(model);
- if (result != null)
- Snackbar.Add("공지사항이 저장되었습니다.", Severity.Success);
- else
- Snackbar.Add("저장 실패", Severity.Error);
- }
+ model.StartsAt = startsAtDate.HasValue ? DateTime.SpecifyKind(startsAtDate.Value.Date, DateTimeKind.Local).ToUniversalTime() : null;
+ model.EndsAt = endsAtDate.HasValue ? DateTime.SpecifyKind(endsAtDate.Value.Date.AddDays(1).AddSeconds(-1), DateTimeKind.Local).ToUniversalTime() : null;
+ var result = Id.HasValue ? await AnnouncementClient.UpdateAsync(Id.Value, model) : await AnnouncementClient.CreateAsync(model);
+ await JS.InvokeVoidAsync("alert", result != null ? "공지사항이 저장되었습니다." : "저장 실패");
Navigation.NavigateTo("/taxbaik/admin/announcements");
}
catch (Exception ex)
{
- Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"저장 실패: {ex.Message}");
}
finally
{
diff --git a/TaxBaik.Web/Components/Admin/Pages/Announcements/AnnouncementList.razor b/TaxBaik.Web/Components/Admin/Pages/Announcements/AnnouncementList.razor
index df47db0..c64d12d 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Announcements/AnnouncementList.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Announcements/AnnouncementList.razor
@@ -4,90 +4,77 @@
@using TaxBaik.Domain.Entities
@inject IAnnouncementBrowserClient AnnouncementClient
@inject NavigationManager Navigation
-@inject IDialogService DialogService
-@inject ISnackbar Snackbar
+@inject IJSRuntime JS
공지사항 관리
-
Homepage
-
공지사항 관리
-
홈페이지 상단에 노출되는 공지사항을 등록하고 관리합니다.
+
Homepage
+
공지사항 관리
+
홈페이지 상단에 노출되는 공지사항을 등록하고 관리합니다.
-
- 공지 등록
-
+ 공지 등록
-
+
@if (announcements is null)
{
-
+
}
else if (!announcements.Any())
{
-
등록된 공지사항이 없습니다.
+
등록된 공지사항이 없습니다.
}
else
{
-
-
-
- | 제목 |
- 유형 |
- 상태 |
- 게시 기간 |
- 순서 |
- |
-
-
-
- @foreach (var item in announcements)
- {
+
+
+
- | @item.Title |
-
-
- @GetTypeLabel(item.DisplayType)
-
- |
-
- @if (IsCurrentlyActive(item))
- {
- 노출 중
- }
- else if (!item.IsActive)
- {
- 비활성
- }
- else
- {
- 기간 외
- }
- |
-
- @FormatPeriod(item)
- |
- @item.SortOrder |
-
-
- Navigation.NavigateTo($"/taxbaik/admin/announcements/{item.Id}/edit"))">
- 수정
-
- DeleteAsync(item))">
- 삭제
-
-
- |
+ 제목 |
+ 유형 |
+ 상태 |
+ 게시 기간 |
+ 순서 |
+ |
- }
-
-
+
+
+ @foreach (var item in announcements)
+ {
+
+ | @item.Title |
+ @GetTypeLabel(item.DisplayType) |
+
+ @if (IsCurrentlyActive(item))
+ {
+ 노출 중
+ }
+ else if (!item.IsActive)
+ {
+ 비활성
+ }
+ else
+ {
+ 기간 외
+ }
+ |
+ @FormatPeriod(item) |
+ @item.SortOrder |
+
+
+
+
+
+ |
+
+ }
+
+
+
}
-
+
@code {
[CascadingParameter]
@@ -97,16 +84,13 @@
protected override async Task OnAfterRenderAsync(bool firstRender)
{
- if (firstRender)
+ if (firstRender && AuthStateTask != null)
{
- if (AuthStateTask != null)
+ var authState = await AuthStateTask;
+ if (authState.User.Identity?.IsAuthenticated == true)
{
- var authState = await AuthStateTask;
- if (authState.User.Identity?.IsAuthenticated == true)
- {
- await LoadAsync();
- StateHasChanged();
- }
+ await LoadAsync();
+ StateHasChanged();
}
}
}
@@ -119,36 +103,32 @@
}
catch (Exception ex)
{
- Snackbar.Add($"오류: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"오류: {ex.Message}");
announcements = [];
}
}
private async Task DeleteAsync(Announcement item)
{
- var confirmed = await DialogService.ShowMessageBox(
- "공지 삭제",
- $"'{item.Title}' 공지를 삭제하시겠습니까?",
- yesText: "삭제", cancelText: "취소");
-
- if (confirmed != true) return;
+ var confirmed = await JS.InvokeAsync("confirm", $"'{item.Title}' 공지를 삭제하시겠습니까?");
+ if (!confirmed) return;
try
{
var success = await AnnouncementClient.DeleteAsync(item.Id);
if (success)
{
- Snackbar.Add("공지사항이 삭제되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "공지사항이 삭제되었습니다.");
await LoadAsync();
}
else
{
- Snackbar.Add("삭제 실패", Severity.Error);
+ await JS.InvokeVoidAsync("alert", "삭제 실패");
}
}
catch (Exception ex)
{
- Snackbar.Add($"오류: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"오류: {ex.Message}");
}
}
@@ -157,28 +137,21 @@
if (!a.IsActive) return false;
var now = DateTime.UtcNow;
if (a.StartsAt.HasValue && a.StartsAt > now) return false;
- if (a.EndsAt.HasValue && a.EndsAt < now) return false;
+ if (a.EndsAt.HasValue && a.EndsAt < now) return false;
return true;
}
private static string FormatPeriod(Announcement a)
{
var start = a.StartsAt?.ToLocalTime().ToString("MM/dd") ?? "즉시";
- var end = a.EndsAt?.ToLocalTime().ToString("MM/dd") ?? "무기한";
+ var end = a.EndsAt?.ToLocalTime().ToString("MM/dd") ?? "무기한";
return $"{start} ~ {end}";
}
- private static Color GetTypeColor(string type) => type switch
- {
- "urgent" => Color.Error,
- "banner" => Color.Warning,
- _ => Color.Info
- };
-
private static string GetTypeLabel(string type) => type switch
{
"urgent" => "긴급",
"banner" => "배너",
- _ => "일반"
+ _ => "일반"
};
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/Blog/BlogCreate.razor b/TaxBaik.Web/Components/Admin/Pages/Blog/BlogCreate.razor
index 5999b08..aa2526e 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Blog/BlogCreate.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Blog/BlogCreate.razor
@@ -6,77 +6,53 @@
@inject BlogService BlogService
@inject ICategoryRepository CategoryRepository
@inject NavigationManager Navigation
-@inject ISnackbar Snackbar
+@inject IJSRuntime JS
새 포스트 작성
-
-
Content
-
새 포스트 작성
-
새로운 블로그 포스트를 작성합니다.
+
Content
+
새 포스트 작성
+
새로운 블로그 포스트를 작성합니다.
- 취소
+
-
-
-
-
-
- @foreach (var category in categories)
- {
- @category.Name
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
저장
+
@code {
- private MudForm? form;
private List
categories = [];
private CreatePostModel model = new();
+ private string CategoryIdText { get => model.CategoryId?.ToString() ?? ""; set => model.CategoryId = int.TryParse(value, out var id) ? id : null; }
protected override async Task OnInitializedAsync()
{
categories = (await CategoryRepository.GetAllAsync()).ToList();
}
- private void GoBack()
- {
- Navigation.NavigateTo("/taxbaik/admin/blog");
- }
-
private async Task SavePost()
{
- if (form == null)
- return;
-
- await form.Validate();
- if (!form.IsValid)
- return;
-
try
{
await BlogService.CreateAsync(new CreateBlogPostDto
@@ -90,12 +66,12 @@
IsPublished = model.IsPublished
});
- Snackbar.Add("포스트가 저장되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "포스트가 저장되었습니다.");
Navigation.NavigateTo("/taxbaik/admin/blog");
}
catch (ValidationException ex)
{
- Snackbar.Add(ex.Message, Severity.Error);
+ await JS.InvokeVoidAsync("alert", ex.Message);
}
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/Blog/BlogEdit.razor b/TaxBaik.Web/Components/Admin/Pages/Blog/BlogEdit.razor
index de0d296..65841bd 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Blog/BlogEdit.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Blog/BlogEdit.razor
@@ -6,76 +6,60 @@
@inject BlogService BlogService
@inject ICategoryRepository CategoryRepository
@inject NavigationManager Navigation
-@inject ISnackbar Snackbar
-@inject IDialogService DialogService
+@inject IJSRuntime JS
포스트 수정
-
-
Content
-
포스트 수정
-
블로그 포스트를 수정합니다.
+
Content
+
포스트 수정
+
블로그 포스트를 수정합니다.
- 취소
+
@if (isLoading)
{
-
+
}
else if (post == null)
{
- 포스트를 찾을 수 없습니다.
+ 포스트를 찾을 수 없습니다.
}
else
{
-
-
-
-
-
- @foreach (var category in categories)
- {
- @category.Name
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
저장
-
삭제
+
}
@code {
- [Parameter]
- public int Id { get; set; }
-
- private MudForm? form;
+ [Parameter] public int Id { get; set; }
private Domain.Entities.BlogPost? post;
private List
categories = [];
private EditPostModel model = new();
private bool isLoading = true;
+ private string CategoryIdText { get => model.CategoryId?.ToString() ?? ""; set => model.CategoryId = int.TryParse(value, out var id) ? id : null; }
protected override async Task OnInitializedAsync()
{
@@ -90,7 +74,7 @@ else
}
catch (Exception ex)
{
- Snackbar.Add($"포스트 로드 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"포스트 로드 실패: {ex.Message}");
}
finally
{
@@ -109,20 +93,9 @@ else
model.IsPublished = post.IsPublished;
}
- private void GoBack()
- {
- Navigation.NavigateTo("/taxbaik/admin/blog");
- }
-
private async Task SavePost()
{
- if (form == null || post == null)
- return;
-
- await form.Validate();
- if (!form.IsValid)
- return;
-
+ if (post == null) return;
try
{
await BlogService.UpdateAsync(post.Id, new CreateBlogPostDto
@@ -135,43 +108,22 @@ else
SeoDescription = model.SeoDescription,
IsPublished = model.IsPublished
});
-
- Snackbar.Add("포스트가 저장되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "포스트가 저장되었습니다.");
Navigation.NavigateTo("/taxbaik/admin/blog");
}
catch (ValidationException ex)
{
- Snackbar.Add(ex.Message, Severity.Error);
- }
- catch (Exception ex)
- {
- Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", ex.Message);
}
}
private async Task DeletePost()
{
- if (post == null)
- return;
-
- var result = await DialogService.ShowMessageBox(
- "포스트 삭제",
- "정말 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.",
- "삭제", "취소");
-
- if (result != true)
- return;
-
- try
- {
- await BlogService.DeleteAsync(post.Id);
- Snackbar.Add("포스트가 삭제되었습니다.", Severity.Success);
- Navigation.NavigateTo("/taxbaik/admin/blog");
- }
- catch (Exception ex)
- {
- Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error);
- }
+ if (post == null) return;
+ if (!await JS.InvokeAsync("confirm", "정말 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.")) return;
+ await BlogService.DeleteAsync(post.Id);
+ await JS.InvokeVoidAsync("alert", "포스트가 삭제되었습니다.");
+ Navigation.NavigateTo("/taxbaik/admin/blog");
}
private class EditPostModel
diff --git a/TaxBaik.Web/Components/Admin/Pages/Blog/BlogList.razor b/TaxBaik.Web/Components/Admin/Pages/Blog/BlogList.razor
index d8d982d..a7086b8 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Blog/BlogList.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Blog/BlogList.razor
@@ -1,58 +1,72 @@
@page "/admin/blog"
@attribute [Authorize]
@inject IApiClient ApiClient
-@inject ISnackbar Snackbar
+@inject IJSRuntime JS
블로그 관리
-
-
Content
-
블로그 관리
-
검색 유입 콘텐츠의 발행 상태와 성과를 관리합니다.
+
Content
+
블로그 관리
+
검색 유입 콘텐츠의 발행 상태와 성과를 관리합니다.
- 새 포스트 작성
+
-
-
- @($"전체 포스트 {totalPosts}개")
- 페이지 @currentPage / @totalPages
-
-
+
+
+ 전체 포스트: @($"{totalPosts}개")
+ 페이지 @currentPage / @totalPages
+
+
-
-
-
-
-
-
-
-
-
-
-
-
- 수정하기
- await DeletePost(cell.Item.Id))">삭제
-
-
-
-
+
+ @if (isLoading)
+ {
+
+ }
+ else
+ {
+
+
+
+
+ | 제목 |
+ 발행 |
+ 조회수 |
+ 작성일 |
+ |
+
+
+
+ @foreach (var post in posts)
+ {
+
+ | @post.Title |
+ |
+ @post.ViewCount |
+ @post.CreatedAt.ToString("yyyy-MM-dd") |
+
+
+ 수정
+
+
+ |
+
+ }
+
+
+
+ }
+
-
- 이전
- 다음
-
+
@code {
- [CascadingParameter]
- private Task? AuthStateTask { get; set; }
-
+ [CascadingParameter] private Task? AuthStateTask { get; set; }
private List posts = [];
private bool isLoading = true;
private int currentPage = 1;
@@ -62,20 +76,19 @@
protected override async Task OnAfterRenderAsync(bool firstRender)
{
- if (firstRender)
+ if (firstRender && AuthStateTask != null)
{
- if (AuthStateTask != null)
+ var authState = await AuthStateTask;
+ if (authState.User.Identity?.IsAuthenticated == true)
{
- var authState = await AuthStateTask;
- if (authState.User.Identity?.IsAuthenticated == true)
- {
- await LoadPosts();
- StateHasChanged();
- }
+ await LoadPosts();
+ StateHasChanged();
}
}
}
+ private string NavTo(string url) => url;
+
private async Task LoadPosts()
{
isLoading = true;
@@ -92,58 +105,33 @@
totalPosts = 0;
totalPages = 1;
}
- isLoading = false;
+ finally
+ {
+ isLoading = false;
+ }
}
- private async Task PreviousPage()
- {
- if (currentPage <= 1)
- return;
-
- currentPage--;
- await LoadPosts();
- }
-
- private async Task NextPage()
- {
- if (currentPage >= totalPages)
- return;
-
- currentPage++;
- await LoadPosts();
- }
+ private async Task PreviousPage() { if (currentPage > 1) { currentPage--; await LoadPosts(); } }
+ private async Task NextPage() { if (currentPage < totalPages) { currentPage++; await LoadPosts(); } }
private async Task TogglePublish(TaxBaik.Domain.Entities.BlogPost post, bool isPublished)
{
var previous = post.IsPublished;
post.IsPublished = isPublished;
- var result = await ApiClient.PutAsync($"blog/{post.Id}", new
- {
- post.Title,
- post.Content,
- post.CategoryId,
- post.Tags,
- post.SeoTitle,
- post.SeoDescription,
- post.ThumbnailUrl,
- IsPublished = isPublished,
- post.AuthorId
- });
-
+ var result = await ApiClient.PutAsync($"blog/{post.Id}", new { post.Title, post.Content, post.CategoryId, post.Tags, post.SeoTitle, post.SeoDescription, post.ThumbnailUrl, IsPublished = isPublished, post.AuthorId });
if (result == null)
{
post.IsPublished = previous;
- Snackbar.Add("발행 상태 변경에 실패했습니다.", Severity.Error);
+ await JS.InvokeVoidAsync("alert", "발행 상태 변경에 실패했습니다.");
return;
}
-
- Snackbar.Add("발행 상태가 변경되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "발행 상태가 변경되었습니다.");
}
private async Task DeletePost(int postId)
{
await ApiClient.DeleteAsync($"blog/{postId}");
- Snackbar.Add("포스트가 삭제되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "포스트가 삭제되었습니다.");
await LoadPosts();
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/Clients/ClientDetail.razor b/TaxBaik.Web/Components/Admin/Pages/Clients/ClientDetail.razor
index 2fe9c75..b45542c 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Clients/ClientDetail.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Clients/ClientDetail.razor
@@ -4,185 +4,123 @@
@inject ClientService ClientService
@inject ConsultationService ConsultationService
@inject NavigationManager Navigation
-@inject ISnackbar Snackbar
+@inject IJSRuntime JS
고객 상세
-
-
Client Details
-
고객 상세
-
고객 정보와 상담 이력을 관리합니다.
+
Client Details
+
고객 상세
+
고객 정보와 상담 이력을 관리합니다.
@if (client == null)
{
- 고객을 찾을 수 없습니다.
- return;
+ 고객을 찾을 수 없습니다.
}
+else
+{
+
+
+
수정
+
-
- Navigation.NavigateTo("/taxbaik/admin/clients"))">
- 목록으로
-
-
- 수정
-
-
-
-
-
-
- 고객 정보
-
-
- 이름
- @client.Name
-
-
- 상호
- @(client.CompanyName ?? "-")
-
-
- 연락처
- @(client.Phone ?? "-")
-
-
- 이메일
- @(client.Email ?? "-")
-
-
- 서비스
- @(client.ServiceType ?? "-")
-
-
- 사업자 유형
- @(client.TaxType ?? "-")
-
-
- 유입 경로
- @(client.Source ?? "-")
-
-
- 등록일
- @client.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd")
-
+
+
+ 고객 정보
+
+
이름@client.Name
+
상호@(client.CompanyName ?? "-")
+
연락처@(client.Phone ?? "-")
+
이메일@(client.Email ?? "-")
+
서비스@(client.ServiceType ?? "-")
+
사업자 유형@(client.TaxType ?? "-")
+
유입 경로@(client.Source ?? "-")
+
등록일@client.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd")
@if (!string.IsNullOrWhiteSpace(client.Memo))
{
-
- 메모
- @client.Memo
-
+
메모@client.Memo
}
-
-
-
+
+
-
-
-
- 상담 이력
-
- + 상담 추가
-
-
+
+
@if (showAddForm)
{
-
-
-
-
-
-
-
- @foreach (var t in ClientService.ServiceTypes)
- {
- @t
- }
-
-
-
-
-
-
-
- -
- @foreach (var r in ConsultationService.Results)
- {
- @r
- }
-
-
-
-
-
-
-
- 저장
- 취소
-
-
+
}
@if (consultations.Count == 0)
{
- 상담 이력이 없습니다.
+ 상담 이력이 없습니다.
}
else
{
-
+
@foreach (var c in consultations)
{
-
-
-
-
-
- @c.ConsultationDate.ToString("yyyy-MM-dd")
- @if (!string.IsNullOrEmpty(c.ServiceType)) { · @c.ServiceType }
-
- @c.Summary
- @if (!string.IsNullOrEmpty(c.Result))
- {
- @c.Result
- }
- @if (c.Fee.HasValue)
- {
-
- 수임료: @c.Fee.Value.ToString("N0")원
-
- }
-
-
-
-
-
+
+
+
+ @c.ConsultationDate.ToString("yyyy-MM-dd") @(string.IsNullOrEmpty(c.ServiceType) ? "" : $"· {c.ServiceType}")
+
+
+
+ @c.Summary
+ @if (!string.IsNullOrEmpty(c.Result))
+ {
+ @c.Result
+ }
+ @if (c.Fee.HasValue)
+ {
+ 수임료: @c.Fee.Value.ToString("N0")원
+ }
+
}
-
+
}
-
-
-
+
+
+}
@code {
- [Parameter]
- public int ClientId { get; set; }
-
+ [Parameter] public int ClientId { get; set; }
private Domain.Entities.Client? client;
private List consultations = [];
-
private bool showAddForm;
private DateTime? newDate = DateTime.Today;
private string newServiceType = "";
@@ -190,10 +128,10 @@
private string newResult = "";
private decimal? newFee;
- protected override async Task OnInitializedAsync()
- {
- await LoadAll();
- }
+ 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()
{
@@ -215,6 +153,12 @@
{
try
{
+ if (string.IsNullOrWhiteSpace(newSummary))
+ {
+ await JS.InvokeVoidAsync("alert", "상담 내용을 입력하세요.");
+ return;
+ }
+
var c = new Domain.Entities.Consultation
{
ClientId = ClientId,
@@ -224,21 +168,23 @@
Result = string.IsNullOrWhiteSpace(newResult) ? null : newResult,
Fee = newFee
};
+
await ConsultationService.CreateAsync(c);
showAddForm = false;
consultations = (await ConsultationService.GetByClientIdAsync(ClientId)).ToList();
- Snackbar.Add("상담이 추가되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "상담이 추가되었습니다.");
}
catch (ValidationException ex)
{
- Snackbar.Add(ex.Message, Severity.Error);
+ await JS.InvokeVoidAsync("alert", ex.Message);
}
}
private async Task DeleteConsultation(int id)
{
+ if (!await JS.InvokeAsync("confirm", "이 상담을 삭제하시겠습니까?")) return;
await ConsultationService.DeleteAsync(id);
consultations = (await ConsultationService.GetByClientIdAsync(ClientId)).ToList();
- Snackbar.Add("삭제되었습니다.", Severity.Info);
+ await JS.InvokeVoidAsync("alert", "삭제되었습니다.");
}
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/Clients/ClientEdit.razor b/TaxBaik.Web/Components/Admin/Pages/Clients/ClientEdit.razor
index c31862e..8966d13 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Clients/ClientEdit.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Clients/ClientEdit.razor
@@ -6,117 +6,74 @@
@using TaxBaik.Domain.Entities
@inject IClientBrowserClient ClientClient
@inject NavigationManager Navigation
-@inject ISnackbar Snackbar
+@inject IJSRuntime JS
@(Id.HasValue ? "고객 수정" : "고객 등록")
-
-
CRM
-
@(Id.HasValue ? "고객 수정" : "고객 등록")
+
CRM
+
@(Id.HasValue ? "고객 수정" : "고객 등록")
- 목록으로
+
-
+
@code {
[Parameter] public int? Id { get; set; }
-
- private MudForm form = null!;
private CreateClientDto dto = new() { Status = "active" };
- private bool isValid;
private bool isLoading = true;
private bool isSaving;
@@ -129,7 +86,7 @@
var client = await ClientClient.GetByIdAsync(Id.Value);
if (client is null)
{
- Snackbar.Add("고객을 찾을 수 없습니다.", Severity.Error);
+ await JS.InvokeVoidAsync("alert", "고객을 찾을 수 없습니다.");
Navigation.NavigateTo("/taxbaik/admin/clients");
return;
}
@@ -145,46 +102,42 @@
Source = client.Source,
Memo = client.Memo
};
- }
- catch (Exception ex)
- {
- Snackbar.Add($"오류: {ex.Message}", Severity.Error);
- Navigation.NavigateTo("/taxbaik/admin/clients");
- return;
- }
+ }
+ catch (Exception ex)
+ {
+ await JS.InvokeVoidAsync("alert", $"오류: {ex.Message}");
+ Navigation.NavigateTo("/taxbaik/admin/clients");
+ return;
+ }
}
isLoading = false;
}
private async Task SaveAsync()
{
- await form.Validate();
- if (!isValid) return;
-
isSaving = true;
try
{
+ if (string.IsNullOrWhiteSpace(dto.Name))
+ {
+ await JS.InvokeVoidAsync("alert", "고객명을 입력하세요.");
+ return;
+ }
if (Id.HasValue)
{
var result = await ClientClient.UpdateAsync(Id.Value, dto);
- if (result != null)
- Snackbar.Add("고객 정보가 수정되었습니다.", Severity.Success);
- else
- Snackbar.Add("수정에 실패했습니다.", Severity.Error);
+ await JS.InvokeVoidAsync("alert", result != null ? "고객 정보가 수정되었습니다." : "수정에 실패했습니다.");
}
else
{
var result = await ClientClient.CreateAsync(dto);
- if (result != null)
- Snackbar.Add("고객이 등록되었습니다.", Severity.Success);
- else
- Snackbar.Add("등록에 실패했습니다.", Severity.Error);
+ await JS.InvokeVoidAsync("alert", result != null ? "고객이 등록되었습니다." : "등록에 실패했습니다.");
}
Navigation.NavigateTo("/taxbaik/admin/clients");
}
catch (Exception ex)
{
- Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"저장 실패: {ex.Message}");
}
finally
{
diff --git a/TaxBaik.Web/Components/Admin/Pages/Clients/ClientList.razor b/TaxBaik.Web/Components/Admin/Pages/Clients/ClientList.razor
index 790e06a..e0b14a0 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Clients/ClientList.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Clients/ClientList.razor
@@ -4,134 +4,94 @@
@using TaxBaik.Domain.Entities
@inject IClientBrowserClient ClientClient
@inject NavigationManager Navigation
-@inject IDialogService DialogService
-@inject ISnackbar Snackbar
+@inject IJSRuntime JS
고객 관리
-
-
CRM
-
고객 관리
-
고객 카드를 등록하고 상담 이력을 관리합니다.
+
CRM
+
고객 관리
+
고객 카드를 등록하고 상담 이력을 관리합니다.
-
- 고객 등록
-
+
-@* 검색/필터 바 *@
-
-
-
-
-
-
-
- 전체
- 활성
- 비활성
-
-
-
- 검색
-
-
- 초기화
-
-
-
+
+
+
+
+
+
+
+
-
+
@if (clients is null)
{
-
+
}
else if (!clients.Any())
{
-
-
- 등록된 고객이 없습니다.
-
+
등록된 고객이 없습니다.
}
else
{
-
-
-
- | 이름 |
- 회사명 |
- 연락처 |
- 서비스 |
- 세금 유형 |
- 상태 |
- 유입 경로 |
- 등록일 |
- |
-
-
-
- @foreach (var c in clients)
- {
+
+
+
- | @c.Name |
- @(c.CompanyName ?? "—") |
- @(c.Phone ?? "—") |
-
- @if (!string.IsNullOrEmpty(c.ServiceType))
- {
- @c.ServiceType
- }
- |
- @(c.TaxType ?? "—") |
-
- @if (c.Status == "active")
- {
- 활성
- }
- else
- {
- 비활성
- }
- |
- @(c.Source ?? "—") |
- @c.CreatedAt.ToLocalTime().ToString("yy.MM.dd") |
-
-
- Navigation.NavigateTo($"/taxbaik/admin/clients/{c.Id}/edit"))">
- 수정
-
- DeleteAsync(c))">
- 삭제
-
-
- |
+ 이름 |
+ 회사명 |
+ 연락처 |
+ 서비스 |
+ 세금 유형 |
+ 상태 |
+ 유입 경로 |
+ 등록일 |
+ |
- }
-
-
-
- @* 페이징 *@
+
+
+ @foreach (var c in clients)
+ {
+
+ | @c.Name |
+ @(c.CompanyName ?? "—") |
+ @(c.Phone ?? "—") |
+ @(c.ServiceType ?? "—") |
+ @(c.TaxType ?? "—") |
+ @(c.Status == "active" ? "활성" : "비활성") |
+ @(c.Source ?? "—") |
+ @c.CreatedAt.ToLocalTime().ToString("yy.MM.dd") |
+
+
+
+
+
+ |
+
+ }
+
+
+
@if (totalPages > 1)
{
-
-
+
}
- 총 @(totalCount)명
+
}
-
+
@code {
- [CascadingParameter]
- private Task? AuthStateTask { get; set; }
-
+ [CascadingParameter] private Task? AuthStateTask { get; set; }
private List? clients;
private string searchText = "";
private string statusFilter = "";
@@ -142,16 +102,13 @@
protected override async Task OnAfterRenderAsync(bool firstRender)
{
- if (firstRender)
+ if (firstRender && AuthStateTask != null)
{
- if (AuthStateTask != null)
+ var authState = await AuthStateTask;
+ if (authState.User.Identity?.IsAuthenticated == true)
{
- var authState = await AuthStateTask;
- if (authState.User.Identity?.IsAuthenticated == true)
- {
- await LoadAsync();
- StateHasChanged();
- }
+ await LoadAsync();
+ StateHasChanged();
}
}
}
@@ -160,75 +117,39 @@
{
try
{
- var (items, total) = await ClientClient.GetPagedAsync(
- currentPage, PageSize,
- string.IsNullOrEmpty(statusFilter) ? null : statusFilter,
- string.IsNullOrEmpty(searchText) ? null : searchText);
-
+ var (items, total) = await ClientClient.GetPagedAsync(currentPage, PageSize, string.IsNullOrEmpty(statusFilter) ? null : statusFilter, string.IsNullOrEmpty(searchText) ? null : searchText);
clients = items.ToList();
totalCount = total;
totalPages = (int)Math.Ceiling((double)total / PageSize);
}
catch (Exception ex)
{
- Snackbar.Add($"오류: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"오류: {ex.Message}");
clients = [];
- totalCount = 0;
- totalPages = 0;
}
}
- private async Task SearchAsync()
- {
- currentPage = 1;
- await LoadAsync();
- }
-
- private async Task ResetAsync()
- {
- searchText = "";
- statusFilter = "";
- currentPage = 1;
- await LoadAsync();
- }
-
- private async Task OnPageChanged(int page)
- {
- currentPage = page;
- await LoadAsync();
- }
-
- private async Task OnSearchKeyUp(KeyboardEventArgs e)
- {
- if (e.Key == "Enter") await SearchAsync();
- }
-
+ private async Task SearchAsync() { currentPage = 1; await LoadAsync(); }
+ private async Task ResetAsync() { searchText = ""; statusFilter = ""; currentPage = 1; await LoadAsync(); }
+ private async Task PreviousPage() { if (currentPage > 1) { currentPage--; await LoadAsync(); } }
+ private async Task NextPage() { if (currentPage < totalPages) { currentPage++; await LoadAsync(); } }
+ private async Task OnSearchKeyUp(KeyboardEventArgs e) { if (e.Key == "Enter") await SearchAsync(); }
private async Task DeleteAsync(Client client)
{
- var confirmed = await DialogService.ShowMessageBox(
- "고객 삭제",
- $"'{client.Name}' 고객을 삭제하시겠습니까? 관련 데이터도 함께 삭제됩니다.",
- yesText: "삭제", cancelText: "취소");
-
- if (confirmed != true) return;
-
+ var confirmed = await JS.InvokeAsync("confirm", $"'{client.Name}' 고객을 삭제하시겠습니까? 관련 데이터도 함께 삭제됩니다.");
+ if (!confirmed) return;
try
{
var success = await ClientClient.DeleteAsync(client.Id);
if (success)
{
- Snackbar.Add($"{client.Name} 고객이 삭제되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", $"{client.Name} 고객이 삭제되었습니다.");
await LoadAsync();
}
- else
- {
- Snackbar.Add("삭제에 실패했습니다.", Severity.Error);
- }
}
catch (Exception ex)
{
- Snackbar.Add($"오류: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"오류: {ex.Message}");
}
- await LoadAsync();
}
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyCreate.razor b/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyCreate.razor
index 2534826..8e515a6 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyCreate.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyCreate.razor
@@ -3,22 +3,22 @@
@using TaxBaik.Web.Components.Admin.Forms
@inject IApiClient ApiClient
@inject NavigationManager Navigation
-@inject ISnackbar Snackbar
+@inject IJSRuntime JS
고객사 등록
-
Settings
-
새 고객사 등록
-
새로운 고객사를 추가합니다.
+
Settings
+
새 고객사 등록
+
새로운 고객사를 추가합니다.
- 취소
+
-
+
-
+
@code {
private void GoBack()
@@ -40,12 +40,12 @@
memo = model.Memo
});
- Snackbar.Add("고객사가 등록되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "고객사가 등록되었습니다.");
Navigation.NavigateTo("/taxbaik/admin/companies");
}
catch (Exception ex)
{
- Snackbar.Add($"등록 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"등록 실패: {ex.Message}");
}
}
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyEdit.razor b/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyEdit.razor
index deb247e..a5793c9 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyEdit.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyEdit.razor
@@ -3,39 +3,37 @@
@using TaxBaik.Web.Components.Admin.Forms
@inject IApiClient ApiClient
@inject NavigationManager Navigation
-@inject ISnackbar Snackbar
-@inject IDialogService DialogService
+@inject IJSRuntime JS
고객사 수정
-
Settings
-
고객사 수정
-
고객사 정보를 수정합니다.
+
Settings
+
고객사 수정
+
고객사 정보를 수정합니다.
- 취소
+
@if (isLoading)
{
-
+
+
+
}
else if (formModel == null)
{
- 고객사를 찾을 수 없습니다.
+ 고객사를 찾을 수 없습니다.
}
else
{
-
+
-
-
-
-
- 고객사 삭제
-
-
+
+
+
+
}
@code {
@@ -67,7 +65,7 @@ else
}
catch (Exception ex)
{
- Snackbar.Add($"고객사 로드 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"고객사 로드 실패: {ex.Message}");
}
finally
{
@@ -95,34 +93,29 @@ else
isActive = model.IsActive
});
- Snackbar.Add("고객사가 수정되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "고객사가 수정되었습니다.");
Navigation.NavigateTo("/taxbaik/admin/companies");
}
catch (Exception ex)
{
- Snackbar.Add($"수정 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"수정 실패: {ex.Message}");
}
}
private async Task DeleteCompany()
{
- var result = await DialogService.ShowMessageBox(
- "고객사 삭제",
- "정말 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.",
- "삭제", "취소");
-
- if (result != true)
+ if (!await JS.InvokeAsync("confirm", "정말 삭제하시겠습니까? 이 작업은 취소할 수 없습니다."))
return;
try
{
await ApiClient.DeleteAsync($"company/{Id}");
- Snackbar.Add("고객사가 삭제되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "고객사가 삭제되었습니다.");
Navigation.NavigateTo("/taxbaik/admin/companies");
}
catch (Exception ex)
{
- Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"삭제 실패: {ex.Message}");
}
}
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyList.razor b/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyList.razor
index b0cd304..5156b9e 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyList.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Companies/CompanyList.razor
@@ -1,53 +1,71 @@
@page "/admin/companies"
@attribute [Authorize]
@inject IApiClient ApiClient
-@inject ISnackbar Snackbar
+@inject IJSRuntime JS
고객사 관리
-
Settings
-
고객사 관리
-
등록된 고객사를 관리하고 새로운 고객사를 추가합니다.
+
Settings
+
고객사 관리
+
등록된 고객사를 관리하고 새로운 고객사를 추가합니다.
- 새 고객사 등록
+
-
-
- @($"전체 고객사 {totalCompanies}개")
- 페이지 @currentPage / @totalPages
-
-
+
+
+ @($"전체 고객사 {totalCompanies}개")
+ 페이지 @currentPage / @totalPages
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 수정
-
-
-
-
+
+ @if (isLoading)
+ {
+
+ }
+ else
+ {
+
+
+
+
+ | 회사코드 |
+ 회사명 |
+ 담당자 |
+ 전화 |
+ 이메일 |
+ 활성 |
+ 등록일 |
+ |
+
+
+
+ @foreach (var item in companies)
+ {
+
+ | @item.CompanyCode |
+ @item.CompanyName |
+ @(item.ContactPerson ?? "—") |
+ @(item.Phone ?? "—") |
+ @(item.Email ?? "—") |
+ @(item.IsActive ? "활성" : "비활성") |
+ @item.CreatedAt.ToString("yyyy-MM-dd") |
+ 수정 |
+
+ }
+
+
+
+ }
+
-
- 이전
- 다음
-
+
@code {
private List companies = [];
@@ -100,7 +118,7 @@
}
catch (Exception ex)
{
- Snackbar.Add($"고객사 로드 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"고객사 로드 실패: {ex.Message}");
}
finally
{
@@ -131,4 +149,6 @@
public bool IsActive { get; set; }
public DateTime CreatedAt { get; set; }
}
+
+ private string NavTo(string url) => url;
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/ConsultingActivities.razor b/TaxBaik.Web/Components/Admin/Pages/ConsultingActivities.razor
index bb5cb62..0095e82 100644
--- a/TaxBaik.Web/Components/Admin/Pages/ConsultingActivities.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/ConsultingActivities.razor
@@ -2,150 +2,122 @@
@using TaxBaik.Web.Services.AdminClients
@inject IConsultingActivityBrowserClient ActivityClient
@inject IClientBrowserClient ClientClient
-@inject ISnackbar Snackbar
-@inject IDialogService DialogService
+@inject IJSRuntime JS
@attribute [Authorize]
상담 활동 관리
-
-
CRM & 세무관리
-
상담 활동 관리
-
고객별 상담 이력과 팔로업을 추적합니다.
+
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")
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ | ID |
+ 고객 |
+ 활동 유형 |
+ 활동일시 |
+ 설명 |
+ 다음 팔로업 |
+ 작업 |
+
+
+
+ @foreach (var item in activities)
+ {
+
+ | @item.Id |
+ @clientMap.GetValueOrDefault(item.ClientId, $"Client #{item.ClientId}") |
+ @item.ActivityType |
+ @item.ActivityDate.ToString("g") |
+ @Truncate(item.Description) |
+ @(item.NextFollowupDate?.ToString("yyyy-MM-dd") ?? "—") |
+
+
+
+
+
+ |
+
+ }
+
+
+
}
-
+
-
-
- @(editingActivity == null ? "새 활동 기록" : "활동 기록 수정")
-
-
-
-
+
-
- 방문 상담
- 전화 상담
- 세무조사 대응 미팅
- 카카오톡 상담
- 이메일 자료 접수
- 기타
-
-
-
-
-
-
-
- 취소
- 저장
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@code {
- [CascadingParameter]
- private Task? AuthStateTask { get; set; }
-
+ [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();
+ private string ClientIdText { get => activityForm.ClientId > 0 ? activityForm.ClientId.ToString() : ""; set => activityForm.ClientId = int.TryParse(value, out var id) ? id : 0; }
+ private string ActivityDateText { get => activityForm.ActivityDate?.ToString("yyyy-MM-dd HH:mm") ?? ""; set => activityForm.ActivityDate = DateTime.TryParse(value, out var dt) ? dt : null; }
+ private string NextFollowupText { get => activityForm.NextFollowupDate?.ToString("yyyy-MM-dd") ?? ""; set => activityForm.NextFollowupDate = DateTime.TryParse(value, out var dt) ? dt : null; }
+
protected override async Task OnAfterRenderAsync(bool firstRender)
{
- if (firstRender)
+ if (firstRender && AuthStateTask != null)
{
- if (AuthStateTask != null)
+ var authState = await AuthStateTask;
+ if (authState.User.Identity?.IsAuthenticated == true)
{
- var authState = await AuthStateTask;
- if (authState.User.Identity?.IsAuthenticated == true)
- {
- await LoadData();
- StateHasChanged();
- }
+ await LoadData();
+ StateHasChanged();
}
}
}
@@ -161,18 +133,14 @@
}
catch (Exception ex)
{
- Snackbar.Add($"데이터 로드 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"데이터 로드 실패: {ex.Message}");
}
}
private void OpenCreateDialog()
{
editingActivity = null;
- activityForm = new ConsultingActivityForm
- {
- ActivityDate = DateTime.Now,
- ClientId = clients.FirstOrDefault()?.Id ?? 0
- };
+ activityForm = new ConsultingActivityForm { ClientId = clients.FirstOrDefault()?.Id ?? 0, ActivityDate = DateTime.Now };
isDialogOpen = true;
}
@@ -188,103 +156,60 @@
NextFollowupDate = activity.NextFollowupDate
};
isDialogOpen = true;
+ await Task.CompletedTask;
}
private async Task SaveActivity()
{
- if (form != null)
+ if (activityForm.ClientId <= 0 || string.IsNullOrWhiteSpace(activityForm.ActivityType) || string.IsNullOrWhiteSpace(activityForm.Description))
{
- await form.Validate();
- if (!form.IsValid)
- {
- Snackbar.Add("필수 항목을 입력해주세요.", Severity.Warning);
- return;
- }
+ await JS.InvokeVoidAsync("alert", "필수 항목을 입력해주세요.");
+ 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);
-
+ var newId = await ActivityClient.CreateAsync(activityForm.ClientId, activityForm.ActivityType, activityForm.ActivityDate ?? DateTime.Now, activityForm.Description, null, activityForm.NextFollowupDate);
if (newId > 0)
{
- Snackbar.Add("활동이 기록되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "활동이 기록되었습니다.");
CloseDialog();
await LoadData();
}
}
else
{
- await ActivityClient.UpdateAsync(
- editingActivity.Id,
- null,
- activityForm.NextFollowupDate);
-
- Snackbar.Add("활동이 업데이트되었습니다.", Severity.Success);
+ await ActivityClient.UpdateAsync(editingActivity.Id, null, activityForm.NextFollowupDate);
+ await JS.InvokeVoidAsync("alert", "활동이 업데이트되었습니다.");
CloseDialog();
await LoadData();
}
}
catch (Exception ex)
{
- Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"저장 실패: {ex.Message}");
}
}
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;
-
+ if (!await JS.InvokeAsync("confirm", "이 활동을 삭제하시겠습니까?")) return;
try
{
await ActivityClient.DeleteAsync(id);
- Snackbar.Add("활동이 삭제되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "활동이 삭제되었습니다.");
await LoadData();
}
catch (Exception ex)
{
- Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"삭제 실패: {ex.Message}");
}
}
- 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; }
- }
+ private void CloseDialog() { isDialogOpen = false; editingActivity = null; activityForm = new(); }
+ private static string Truncate(string? text) => string.IsNullOrWhiteSpace(text) ? "—" : text.Length > 30 ? text[..30] + "..." : text;
+ private static string GetClientDisplayName(Client client) => !string.IsNullOrWhiteSpace(client.CompanyName) ? client.CompanyName : !string.IsNullOrWhiteSpace(client.Name) ? client.Name : $"Client #{client.Id}";
+ private sealed 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; } }
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/Contracts.razor b/TaxBaik.Web/Components/Admin/Pages/Contracts.razor
index 9d480be..c924ff0 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Contracts.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Contracts.razor
@@ -2,160 +2,123 @@
@using TaxBaik.Web.Services.AdminClients
@inject IContractBrowserClient ContractClient
@inject IClientBrowserClient ClientClient
-@inject ISnackbar Snackbar
-@inject IDialogService DialogService
+@inject IJSRuntime JS
@attribute [Authorize]
계약 관리
-
CRM & 세무관리
-
계약 관리
-
고객 계약과 월 정기수익을 함께 관리합니다.
+
CRM & 세무관리
+
계약 관리
+
고객 계약과 월 정기수익을 함께 관리합니다.
@if (mrr > 0)
{
-
- 월 정기수익:
- ₩@mrr.ToString("N0")
-
+
월 정기수익: ₩@mrr.ToString("N0")
}
-
- 새 계약 추가
-
+
-
+
@if (contracts is null)
{
-
+
}
else if (contracts.Count == 0)
{
-
-
- 계약이 없습니다.
-
+
계약이 없습니다.
}
else
{
-
-
-
-
-
- @if (clientMap.TryGetValue(context.Item.ClientId, out var clientName))
- {
-
- @clientName
-
- }
-
-
-
-
-
-
-
- @context.Item.StartDate.ToString("yyyy-MM-dd")
- @if (context.Item.EndDate.HasValue)
- {
- ~@context.Item.EndDate.Value.ToString("yyyy-MM-dd")
- }
-
-
-
-
- @{
- var isActive = !context.Item.EndDate.HasValue || context.Item.EndDate.Value >= DateTime.Today;
- }
- @if (isActive)
- {
- 활성
- }
- else
- {
- 만료
- }
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ | ID |
+ 고객 |
+ 계약번호 |
+ 서비스 유형 |
+ 월 수수료 |
+ 계약기간 |
+ 상태 |
+ 작업 |
+
+
+
+ @foreach (var item in contracts)
+ {
+ var isActive = !item.EndDate.HasValue || item.EndDate.Value >= DateTime.Today;
+
+ | @item.Id |
+ @(clientMap.TryGetValue(item.ClientId, out var clientName) ? clientName : "") |
+ @item.ContractNumber |
+ @item.ServiceType |
+ @(item.MonthlyFee?.ToString("C") ?? "—") |
+ @item.StartDate@if (item.EndDate.HasValue){~ @item.EndDate.Value} |
+ @(isActive ? "활성" : "만료") |
+ |
+
+ }
+
+
+
}
-
+
-
-
-
- 새 계약 추가
-
-
-
-
+
-
-
- 개인 기장대리
- 법인 기장대리
- 세무조정 대행
- 양도세 신고대리
- 상속·증여 자문
- 세무조사 대응
-
-
-
-
-
-
- 취소
- 저장
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@code {
- [CascadingParameter]
- private Task? AuthStateTask { get; set; }
-
+ [CascadingParameter] private Task? AuthStateTask { get; set; }
private List? contracts;
private List clients = [];
private Dictionary clientMap = new();
private decimal mrr = 0;
- private MudForm? form;
private bool isDialogOpen;
private ContractForm contractForm = new();
+ private string ClientIdText { get => contractForm.ClientId > 0 ? contractForm.ClientId.ToString() : ""; set => contractForm.ClientId = int.TryParse(value, out var id) ? id : 0; }
+ private string StartDateText { get => contractForm.StartDate?.ToString("yyyy-MM-dd") ?? ""; set => contractForm.StartDate = DateTime.TryParse(value, out var dt) ? dt : null; }
+ private string MonthlyFeeText { get => contractForm.MonthlyFee?.ToString() ?? ""; set => contractForm.MonthlyFee = decimal.TryParse(value, out var amount) ? amount : null; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
- if (firstRender)
+ if (firstRender && AuthStateTask != null)
{
- if (AuthStateTask != null)
+ var authState = await AuthStateTask;
+ if (authState.User.Identity?.IsAuthenticated == true)
{
- var authState = await AuthStateTask;
- if (authState.User.Identity?.IsAuthenticated == true)
- {
- await LoadData();
- StateHasChanged();
- }
+ await LoadData();
+ StateHasChanged();
}
}
}
@@ -172,99 +135,56 @@
}
catch (Exception ex)
{
- Snackbar.Add($"데이터 로드 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"데이터 로드 실패: {ex.Message}");
}
}
private void OpenCreateDialog()
{
- contractForm = new ContractForm
- {
- ClientId = clients.FirstOrDefault()?.Id,
- StartDate = DateTime.Today
- };
+ contractForm = new ContractForm { ClientId = clients.FirstOrDefault()?.Id ?? 0, StartDate = DateTime.Today };
isDialogOpen = true;
}
private async Task SaveContract()
{
- if (form != null)
- {
- await form.Validate();
- if (!form.IsValid)
- {
- Snackbar.Add("필수 항목을 입력해주세요.", Severity.Warning);
- return;
- }
- }
-
try
{
- if (contractForm.ClientId == null) return;
- var newId = await ContractClient.CreateAsync(
- contractForm.ClientId.Value,
- contractForm.ContractNumber,
- contractForm.ServiceType,
- contractForm.StartDate ?? DateTime.Now,
- contractForm.MonthlyFee);
+ if (contractForm.ClientId <= 0)
+ {
+ await JS.InvokeVoidAsync("alert", "고객을 선택하세요.");
+ return;
+ }
+ var newId = await ContractClient.CreateAsync(contractForm.ClientId, contractForm.ContractNumber, contractForm.ServiceType, contractForm.StartDate ?? DateTime.Today, contractForm.MonthlyFee);
if (newId > 0)
{
- Snackbar.Add("계약이 추가되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "계약이 추가되었습니다.");
CloseDialog();
await LoadData();
}
}
catch (Exception ex)
{
- Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"저장 실패: {ex.Message}");
}
}
private async Task DeleteContract(int id)
{
- var parameters = new DialogParameters
- {
- { "Title", "삭제 확인" },
- { "Message", "이 계약을 삭제하시겠습니까?" }
- };
-
- var dialog = await DialogService.ShowAsync("", parameters);
- var result = await dialog.Result;
-
- if (result?.Canceled ?? true)
- return;
-
+ if (!await JS.InvokeAsync("confirm", "이 계약을 삭제하시겠습니까?")) return;
try
{
await ContractClient.DeleteAsync(id);
- Snackbar.Add("계약이 삭제되었습니다.", Severity.Success);
+ await JS.InvokeVoidAsync("alert", "계약이 삭제되었습니다.");
await LoadData();
}
catch (Exception ex)
{
- Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error);
+ await JS.InvokeVoidAsync("alert", $"삭제 실패: {ex.Message}");
}
}
- private void CloseDialog()
- {
- isDialogOpen = false;
- contractForm = new();
- }
-
- private static string GetClientDisplayName(Client client)
- => !string.IsNullOrWhiteSpace(client.CompanyName)
- ? client.CompanyName
- : !string.IsNullOrWhiteSpace(client.Name)
- ? client.Name
- : $"Client #{client.Id}";
- private class ContractForm
- {
- public int? ClientId { get; set; }
- public string ContractNumber { get; set; } = "";
- public string ServiceType { get; set; } = "";
- public DateTime? StartDate { get; set; }
- public decimal? MonthlyFee { get; set; }
- }
+ private void CloseDialog() { isDialogOpen = false; contractForm = new(); }
+ private static string GetClientDisplayName(Client client) => !string.IsNullOrWhiteSpace(client.CompanyName) ? client.CompanyName : !string.IsNullOrWhiteSpace(client.Name) ? client.Name : $"Client #{client.Id}";
+ private sealed class ContractForm { public int ClientId { get; set; } public string ContractNumber { get; set; } = ""; public string ServiceType { get; set; } = ""; public DateTime? StartDate { get; set; } public decimal? MonthlyFee { get; set; } }
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/Dashboard.razor b/TaxBaik.Web/Components/Admin/Pages/Dashboard.razor
index 993a391..0b55d62 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Dashboard.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Dashboard.razor
@@ -8,207 +8,205 @@
-
Overview
-
대시보드
-
문의 흐름과 콘텐츠 상태를 한 화면에서 확인합니다.
+
Overview
+
대시보드
+
문의 흐름과 콘텐츠 상태를 한 화면에서 확인합니다.
-
- 새 포스트 작성
-
+
-
-
-
Nav.NavigateTo("/taxbaik/admin/inquiries"))' style="cursor: pointer;">
-
-
이번달 문의
-
- @summary.ThisMonthInquiries
- 💬
-
-
월간 상담 유입 (클릭 시 이동)
-
-
-
-
Nav.NavigateTo("/taxbaik/admin/inquiries?status=new"))' style="cursor: pointer;">
-
-
신규 문의
-
- @summary.NewInquiries
- ⚠️
-
-
처리 대기 (클릭 시 이동)
-
-
-
-
Nav.NavigateTo("/taxbaik/admin/blog"))' style="cursor: pointer;">
-
-
전체 포스트
-
- @summary.TotalPosts
- 📄
-
-
콘텐츠 자산 (클릭 시 이동)
-
-
-
-
Nav.NavigateTo("/taxbaik/admin/blog"))' style="cursor: pointer;">
-
-
발행된 포스트
-
- @summary.PublishedPosts
- 🌐
-
-
검색 노출 대상 (클릭 시 이동)
-
-
-
-
-@if (upcomingFilings.Count > 0)
+@if (summary is null)
{
-
-