4358b189c8
TaxBaik CI/CD / build-and-deploy (push) Successful in 3m2s
**Blazor Pages Refactored (9 pages):** ✅ ClientList.razor (Service → IClientBrowserClient) ✅ ClientEdit.razor (Service → IClientBrowserClient) ✅ TaxFilingList.razor (Service → ITaxFilingBrowserClient) ✅ FilingTable.razor (Service → ITaxFilingBrowserClient) ✅ FaqList.razor (Service → IFaqBrowserClient) ✅ FaqEdit.razor (Service → IFaqBrowserClient) ✅ AnnouncementList.razor (Service → IAnnouncementBrowserClient) ✅ AnnouncementEdit.razor (Service → IAnnouncementBrowserClient) ✅ Previously: Dashboard, InquiryTable, InquiryDetail **Pattern Applied Consistently:** - Removed all direct service injections (Service Layer) - Injected specialized Browser Clients (API Layer) - Error handling with Snackbar notifications - Try-catch for all API calls - Graceful fallbacks (empty lists on error) **Phase 7 Complete: 100% API-First Refactoring** All admin pages now use: ClientBrowserClient → /api/client (Clients) TaxFilingBrowserClient → /api/tax-filing (Tax Filings) FaqBrowserClient → /api/faq (FAQs) AnnouncementBrowserClient → /api/announcement (Announcements) InquiryBrowserClient → /api/inquiry (Inquiries) AdminDashboardClient → /api/admin-dashboard (Dashboard) **SOLID + Maintainability Achieved:** ✓ Single Responsibility: Each client = one domain ✓ Open/Closed: Extensible without modifying Blazor ✓ Dependency Inversion: Blazor → Abstractions, not services ✓ Interface Segregation: Fine-grained client interfaces ✓ Liskov Substitution: Interchangeable implementations Build: ✅ Success (0 errors) Status: Ready for Phase 6 (SignalR Integration) Next: NotificationHub for real-time dashboard updates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
148 lines
5.3 KiB
Plaintext
148 lines
5.3 KiB
Plaintext
@page "/admin/tax-filings"
|
|
@attribute [Authorize]
|
|
@using TaxBaik.Web.Services
|
|
@using TaxBaik.Domain.Entities
|
|
@inject ITaxFilingBrowserClient FilingClient
|
|
@inject IClientBrowserClient ClientClient
|
|
@inject ISnackbar Snackbar
|
|
|
|
<PageTitle>신고 일정 관리</PageTitle>
|
|
|
|
<section class="admin-page-hero">
|
|
<div>
|
|
<MudText Typo="Typo.caption" Class="admin-eyebrow">Tax Schedule</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="@(() => showAddForm = !showAddForm)"
|
|
StartIcon="@Icons.Material.Filled.Add">
|
|
일정 추가
|
|
</MudButton>
|
|
</section>
|
|
|
|
@if (showAddForm)
|
|
{
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-3">새 신고 일정</MudText>
|
|
<MudGrid Spacing="2">
|
|
<MudItem xs="12" sm="6" md="4">
|
|
<MudAutocomplete T="Domain.Entities.Client" @bind-Value="selectedClient"
|
|
Label="고객 검색 *"
|
|
SearchFunc="SearchClients"
|
|
ToStringFunc="@(c => c == null ? "" : $"{c.Name} {c.CompanyName ?? ""}")"
|
|
Variant="Variant.Outlined" />
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="4">
|
|
<MudSelect T="string" @bind-Value="newFilingType" Label="신고 유형 *" Variant="Variant.Outlined">
|
|
@foreach (var t in TaxFilingService.FilingTypes)
|
|
{
|
|
<MudSelectItem Value="@t">@t</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="4">
|
|
<MudDatePicker @bind-Date="newDueDate" Label="신고 기한 *" DateFormat="yyyy-MM-dd" />
|
|
</MudItem>
|
|
<MudItem xs="12">
|
|
<MudTextField T="string" @bind-Value="newMemo" Label="메모" Variant="Variant.Outlined" />
|
|
</MudItem>
|
|
</MudGrid>
|
|
<MudStack Row="true" Class="mt-3" Spacing="2">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="AddFiling">저장</MudButton>
|
|
<MudButton Variant="Variant.Outlined" OnClick="@(() => showAddForm = false)">취소</MudButton>
|
|
</MudStack>
|
|
</MudPaper>
|
|
}
|
|
|
|
<MudPaper Class="admin-surface" Elevation="0">
|
|
<MudTabs Rounded="true" Elevation="0" Class="admin-tabs">
|
|
<MudTabPanel Text="신고 예정">
|
|
<FilingTable Filings="@pending" OnStatusChange="Reload" />
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="신고 완료">
|
|
<FilingTable Filings="@filed" OnStatusChange="Reload" />
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="기한 초과">
|
|
<FilingTable Filings="@overdue" OnStatusChange="Reload" />
|
|
</MudTabPanel>
|
|
</MudTabs>
|
|
</MudPaper>
|
|
|
|
@code {
|
|
private List<Domain.Entities.TaxFiling> pending = [];
|
|
private List<Domain.Entities.TaxFiling> filed = [];
|
|
private List<Domain.Entities.TaxFiling> overdue = [];
|
|
|
|
private bool showAddForm;
|
|
private Domain.Entities.Client? selectedClient;
|
|
private string newFilingType = "";
|
|
private DateTime? newDueDate = DateTime.Today.AddDays(30);
|
|
private string newMemo = "";
|
|
|
|
protected override async Task OnInitializedAsync() => await Reload();
|
|
|
|
private async Task Reload()
|
|
{
|
|
try
|
|
{
|
|
var all = (await FilingClient.GetUpcomingAsync(365)).ToList();
|
|
pending = all.Where(x => x.Status == "pending").ToList();
|
|
filed = all.Where(x => x.Status == "filed").ToList();
|
|
overdue = all.Where(x => x.Status == "overdue").ToList();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"오류: {ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
|
|
private async Task<IEnumerable<Client>> SearchClients(string value)
|
|
{
|
|
try
|
|
{
|
|
var (items, _) = await ClientClient.GetPagedAsync(1, 20, search: value);
|
|
return items;
|
|
}
|
|
catch
|
|
{
|
|
return [];
|
|
}
|
|
}
|
|
|
|
private async Task AddFiling()
|
|
{
|
|
try
|
|
{
|
|
if (selectedClient == null)
|
|
{
|
|
Snackbar.Add("고객을 선택하세요.", Severity.Warning);
|
|
return;
|
|
}
|
|
var filing = new TaxFiling
|
|
{
|
|
ClientId = selectedClient.Id,
|
|
FilingType = newFilingType,
|
|
DueDate = newDueDate?.ToUniversalTime() ?? DateTime.UtcNow,
|
|
Status = "pending",
|
|
Memo = string.IsNullOrWhiteSpace(newMemo) ? null : newMemo
|
|
};
|
|
var result = await FilingClient.CreateAsync(filing);
|
|
if (result != null)
|
|
{
|
|
showAddForm = false;
|
|
Snackbar.Add("신고 일정이 추가되었습니다.", Severity.Success);
|
|
await Reload();
|
|
}
|
|
else
|
|
{
|
|
Snackbar.Add("추가 실패", Severity.Error);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"오류: {ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
}
|