-
-
-
T
-
-
TaxBaik
-
관리자 로그인
+
+
+ 관리자 로그인
+
+
-
-
-
-
+
+
@code {
private bool isLoading = false;
diff --git a/TaxBaik.Web/Components/Admin/Pages/RevenueTrackings.razor b/TaxBaik.Web/Components/Admin/Pages/RevenueTrackings.razor
index f5922fb..dfa1c3f 100644
--- a/TaxBaik.Web/Components/Admin/Pages/RevenueTrackings.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/RevenueTrackings.razor
@@ -2,125 +2,145 @@
@using TaxBaik.Web.Services.AdminClients
@inject IRevenueTrackingBrowserClient RevenueClient
@inject IClientBrowserClient ClientClient
-@inject IJSRuntime JS
+@inject ISnackbar Snackbar
+@inject IDialogService DialogService
@attribute [Authorize]
수익 추적 관리
+
-
CRM & 세무관리
-
수익 추적 관리
-
청구, 납부, 미수금 상태를 한 화면에서 관리합니다.
+
CRM & 세무관리
+
수익 추적 관리
+
청구, 납부, 미수금 상태를 한 화면에서 관리합니다.
-
+
+ 새 청구 추가
+
-
+
@if (revenues is null)
{
-
+
}
else if (revenues.Count == 0)
{
- 청구 기록이 없습니다.
+
+
+ 청구 기록이 없습니다.
+
}
else
{
-
-
-
-
- | ID |
- 고객 |
- 청구번호 |
- 청구일 |
- 청구액 |
- 납부여부 |
- 작업 |
-
-
-
- @foreach (var item in revenues)
- {
-
- | @item.Id |
- @clientMap.GetValueOrDefault(item.ClientId, $"Client #{item.ClientId}") |
- @item.InvoiceNumber |
- @item.InvoiceDate.ToString("yyyy-MM-dd") |
- @item.Amount.ToString("C") |
- @(item.PaymentStatus == "paid" ? "납부" : "미납") |
-
-
- @if (item.PaymentStatus != "paid")
- {
-
- }
-
-
- |
-
- }
-
-
-
+
+
+
+
+
+ @if (clientMap.TryGetValue(context.Item.ClientId, out var clientName))
+ {
+
+ @clientName
+
+ }
+
+
+
+
+
+
+
+ @if (context.Item.PaymentStatus == "paid")
+ {
+ 납부
+ }
+ else
+ {
+ 미납
+ }
+
+
+
+
+
+ @if (context.Item.PaymentStatus != "paid")
+ {
+
+ }
+
+
+
+
+
+
}
-
+
-
+
+
+
+
+
+ 기장 수수료
+ 세무조정료
+ 세무상담료
+ 신고 대행료
+ 자문 수수료
+
+
+
+
+
+ 취소
+ 저장
+
+
@code {
- [CascadingParameter] private Task
? AuthStateTask { get; set; }
+ [CascadingParameter]
+ private Task? AuthStateTask { get; set; }
+
private List? revenues;
private List clients = [];
private Dictionary clientMap = new();
+ private MudForm? form;
private bool isDialogOpen;
private RevenueForm revenueForm = new();
- private string ClientIdText { get => revenueForm.ClientId > 0 ? revenueForm.ClientId.ToString() : ""; set => revenueForm.ClientId = int.TryParse(value, out var id) ? id : 0; }
- private string InvoiceDateText { get => revenueForm.InvoiceDate?.ToString("yyyy-MM-dd") ?? ""; set => revenueForm.InvoiceDate = DateTime.TryParse(value, out var dt) ? dt : null; }
- private string AmountText { get => revenueForm.Amount?.ToString() ?? ""; set => revenueForm.Amount = decimal.TryParse(value, out var amt) ? amt : null; }
- private string DueDateText { get => revenueForm.DueDate?.ToString("yyyy-MM-dd") ?? ""; set => revenueForm.DueDate = DateTime.TryParse(value, out var dt) ? dt : null; }
-
protected override async Task OnAfterRenderAsync(bool firstRender)
{
- if (firstRender && AuthStateTask != null)
+ if (firstRender)
{
- var authState = await AuthStateTask;
- if (authState.User.Identity?.IsAuthenticated == true)
+ if (AuthStateTask != null)
{
- await LoadData();
- StateHasChanged();
+ var authState = await AuthStateTask;
+ if (authState.User.Identity?.IsAuthenticated == true)
+ {
+ await LoadData();
+ StateHasChanged();
+ }
}
}
}
@@ -136,36 +156,53 @@
}
catch (Exception ex)
{
- await JS.InvokeVoidAsync("alert", $"데이터 로드 실패: {ex.Message}");
+ Snackbar.Add($"데이터 로드 실패: {ex.Message}", Severity.Error);
}
}
private void OpenCreateDialog()
{
- revenueForm = new RevenueForm { ClientId = clients.FirstOrDefault()?.Id ?? 0, InvoiceDate = DateTime.Today, DueDate = DateTime.Today.AddDays(14) };
+ revenueForm = new RevenueForm
+ {
+ ClientId = clients.FirstOrDefault()?.Id ?? 0,
+ InvoiceDate = DateTime.Today,
+ DueDate = DateTime.Today.AddDays(14)
+ };
isDialogOpen = true;
}
private async Task SaveRevenue()
{
- if (revenueForm.ClientId <= 0 || string.IsNullOrWhiteSpace(revenueForm.InvoiceNumber) || revenueForm.Amount is null)
+ if (form != null)
{
- await JS.InvokeVoidAsync("alert", "필수 항목을 입력해주세요.");
- return;
+ await form.Validate();
+ if (!form.IsValid)
+ {
+ Snackbar.Add("필수 항목을 입력해주세요.", Severity.Warning);
+ return;
+ }
}
+
try
{
- var newId = await RevenueClient.CreateAsync(revenueForm.ClientId, revenueForm.InvoiceNumber, revenueForm.InvoiceDate ?? DateTime.Today, revenueForm.Amount.Value, revenueForm.ServiceType, revenueForm.DueDate);
+ var newId = await RevenueClient.CreateAsync(
+ revenueForm.ClientId,
+ revenueForm.InvoiceNumber,
+ revenueForm.InvoiceDate ?? DateTime.Now,
+ revenueForm.Amount,
+ revenueForm.ServiceType,
+ revenueForm.DueDate);
+
if (newId > 0)
{
- await JS.InvokeVoidAsync("alert", "청구가 추가되었습니다.");
+ Snackbar.Add("청구가 추가되었습니다.", Severity.Success);
CloseDialog();
await LoadData();
}
}
catch (Exception ex)
{
- await JS.InvokeVoidAsync("alert", $"저장 실패: {ex.Message}");
+ Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
}
}
@@ -174,31 +211,60 @@
try
{
await RevenueClient.MarkPaidAsync(id, DateTime.Now);
- await JS.InvokeVoidAsync("alert", "납부가 처리되었습니다.");
+ Snackbar.Add("납부가 처리되었습니다.", Severity.Success);
await LoadData();
}
catch (Exception ex)
{
- await JS.InvokeVoidAsync("alert", $"처리 실패: {ex.Message}");
+ Snackbar.Add($"처리 실패: {ex.Message}", Severity.Error);
}
}
private async Task DeleteRevenue(int id)
{
- if (!await JS.InvokeAsync("confirm", "이 청구를 삭제하시겠습니까?")) return;
+ var parameters = new DialogParameters
+ {
+ { "Title", "삭제 확인" },
+ { "Message", "이 청구를 삭제하시겠습니까?" }
+ };
+
+ var dialog = await DialogService.ShowAsync("", parameters);
+ var result = await dialog.Result;
+
+ if (result?.Canceled ?? true)
+ return;
+
try
{
await RevenueClient.DeleteAsync(id);
- await JS.InvokeVoidAsync("alert", "청구가 삭제되었습니다.");
+ Snackbar.Add("청구가 삭제되었습니다.", Severity.Success);
await LoadData();
}
catch (Exception ex)
{
- await JS.InvokeVoidAsync("alert", $"삭제 실패: {ex.Message}");
+ Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error);
}
}
- private void CloseDialog() { isDialogOpen = false; revenueForm = 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 RevenueForm { public int ClientId { get; set; } public string InvoiceNumber { get; set; } = ""; public DateTime? InvoiceDate { get; set; } public decimal? Amount { get; set; } public string? ServiceType { get; set; } public DateTime? DueDate { get; set; } }
+ private void CloseDialog()
+ {
+ isDialogOpen = false;
+ revenueForm = new();
+ }
+
+ private static string GetClientDisplayName(Client client)
+ => !string.IsNullOrWhiteSpace(client.CompanyName)
+ ? client.CompanyName
+ : !string.IsNullOrWhiteSpace(client.Name)
+ ? client.Name
+ : $"Client #{client.Id}";
+ private class RevenueForm
+ {
+ public int ClientId { get; set; }
+ public string InvoiceNumber { get; set; } = "";
+ public DateTime? InvoiceDate { get; set; }
+ public decimal Amount { get; set; }
+ public string? ServiceType { get; set; }
+ public DateTime? DueDate { get; set; }
+ }
}
diff --git a/TaxBaik.Web/Components/Admin/Pages/SeasonSimulator.razor b/TaxBaik.Web/Components/Admin/Pages/SeasonSimulator.razor
index 0b5a2c6..b85f961 100644
--- a/TaxBaik.Web/Components/Admin/Pages/SeasonSimulator.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/SeasonSimulator.razor
@@ -7,92 +7,162 @@
-
Season Preview
-
시즌 시뮬레이터
-
날짜를 선택해 홈페이지 시즌 화면이 어떻게 보이는지 미리 확인합니다.
+
Season Preview
+
시즌 시뮬레이터
+
날짜를 선택해 홈페이지 시즌 화면이 어떻게 보이는지 미리 확인합니다.
-
-
+
+
-
- 홈페이지 미리보기
- @(simulationDate?.ToString("yyyy년 MM월 dd일") ?? "날짜를 선택하세요")
- @if (activeSeason != null)
- {
- @activeSeason.Name 시즌 활성
-
- @if (activeSeason.DaysUntilDeadline <= 7 && activeSeason.DaysUntilDeadline >= 0)
- {
-
D-@activeSeason.DaysUntilDeadline 마감 임박
- }
-
@activeSeason.HeroHeadline
-
@activeSeason.HeroSubtext
-
@activeSeason.CtaText
-
-
-
활성 시즌 키@activeSeason.Key
-
마감까지@(activeSeason.DaysUntilDeadline >= 0 ? $"D-{activeSeason.DaysUntilDeadline}" : $"마감 후 @(-activeSeason.DaysUntilDeadline)일")
-
포커스 서비스@activeSeason.FocusService
-
블로그 카테고리@activeSeason.RelatedCategorySlug
-
긴박감 배지 문구@activeSeason.UrgencyBadge
-
- }
- else
- {
- 선택한 날짜는 시즌 비활성 기간입니다. 홈페이지는 기본 Hero를 표시합니다.
-
-
사업자 세금, 부동산,
가족자산까지
-
세무사·부동산중개사·보험설계사 자격 보유 | 온라인 맞춤 상담
-
무료 상담 신청
-
- }
-
-
+
+
+
+ @(simulationDate?.ToString("yyyy년 MM월 dd일") ?? "날짜를 선택하세요") 홈페이지 미리보기
+
+ @if (activeSeason != null)
+ {
+
+ @activeSeason.Name 시즌 활성
+
+
+
+
+ @if (activeSeason.DaysUntilDeadline <= 7 && activeSeason.DaysUntilDeadline >= 0)
+ {
+
+ D-@activeSeason.DaysUntilDeadline 마감 임박
+
+ }
+
+ @activeSeason.HeroHeadline
+
+
+ @activeSeason.HeroSubtext
+
+
+
+ @activeSeason.CtaText
+
+
+ 서비스 안내
+
+
+
-
-
연간 시즌 타임라인
-
-
-
-
- | 기간 |
- 시즌 |
- 블로그 카테고리 |
- 상태 |
-
-
-
- @foreach (var s in TaxSeasonCalendar.Seasons)
- {
- var isActive = activeSeason?.Key == s.Key;
+
+
+ 활성 시즌 키
+ @activeSeason.Key
+
+
+ 마감까지
+
+ @if (activeSeason.DaysUntilDeadline >= 0)
+ {
+
+ D-@activeSeason.DaysUntilDeadline
+
+ }
+ else
+ {
+ 마감 후 @(-activeSeason.DaysUntilDeadline)일
+ }
+
+
+
+ 포커스 서비스
+ @activeSeason.FocusService
+
+
+ 블로그 카테고리
+ @activeSeason.RelatedCategorySlug
+
+
+ 긴박감 배지 문구
+ @activeSeason.UrgencyBadge
+
+
+ }
+ else
+ {
+
+ 선택한 날짜(@(simulationDate?.ToString("MM월 dd일") ?? "-"))는 시즌 비활성 기간입니다.
+ 홈페이지는 기본 Hero를 표시합니다.
+
+
+
+ 사업자 세금, 부동산,
가족자산까지
+
+
+ 세무사·부동산중개사·보험설계사 자격 보유 | 온라인 맞춤 상담
+
+
+ 무료 상담 신청
+
+
+ }
+
+
+
+ 연간 시즌 타임라인
+
+
- | @s.StartMonth/@s.StartDay ~ @s.EndMonth/@s.EndDay |
- @s.Name |
- @s.RelatedCategorySlug |
- @(isActive ? "활성" : "비활성") |
+ 기간 |
+ 시즌 |
+ 블로그 카테고리 |
+ 상태 |
- }
-
-
-
-
+
+
+ @foreach (var s in TaxSeasonCalendar.Seasons)
+ {
+ var isActive = activeSeason?.Key == s.Key;
+
+ |
+ @s.StartMonth/@s.StartDay ~ @s.EndMonth/@s.EndDay
+ |
+ @s.Name |
+ @s.RelatedCategorySlug |
+
+ @if (isActive)
+ {
+ 활성
+ }
+ else
+ {
+ 비활성
+ }
+ |
+
+ }
+
+
+
+
+
@code {
private DateTime? simulationDate = DateTime.Today;
private CurrentSeasonDto? activeSeason;
- private string SimulationDateText { get => simulationDate?.ToString("yyyy-MM-dd") ?? ""; set { simulationDate = DateTime.TryParse(value, out var dt) ? dt : null; ComputeSeason(); } }
protected override void OnInitialized() => ComputeSeason();
@@ -113,7 +183,10 @@
var endYearCalc = (season.EndMonth < season.StartMonth) ? date.Year + 1 : date.Year;
var deadline = new DateTime(endYearCalc, season.EndMonth, season.EndDay);
var ddays = (deadline.Date - date.Date).Days;
- var badge = ddays <= 7 && ddays >= 0 ? season.UrgencyBadge.Replace("{n}", ddays.ToString()) : season.UrgencyBadge;
+
+ var badge = ddays <= 7 && ddays >= 0
+ ? season.UrgencyBadge.Replace("{n}", ddays.ToString())
+ : season.UrgencyBadge;
activeSeason = new CurrentSeasonDto
{
diff --git a/TaxBaik.Web/Components/Admin/Pages/Settings/SiteSettings.razor b/TaxBaik.Web/Components/Admin/Pages/Settings/SiteSettings.razor
index 501d4ce..0368119 100644
--- a/TaxBaik.Web/Components/Admin/Pages/Settings/SiteSettings.razor
+++ b/TaxBaik.Web/Components/Admin/Pages/Settings/SiteSettings.razor
@@ -5,58 +5,78 @@
@using TaxBaik.Web.Services
@using TaxBaik.Domain.Interfaces
@inject IApiClient ApiClient
-@inject IJSRuntime JS
+@inject ISnackbar Snackbar
설정
-
-
-
System
-
설정
-
공개 사이트 연락처와 관리자 계정 보안을 관리합니다.
-
-
-
-
-