Files
taxbaik/TaxBaik.Web.Client/Components/Admin/Pages/TaxFilings/TaxFilingList.razor
T
kjh2064 8202c3278b
TaxBaik CI/CD / build-and-deploy (push) Failing after 2m17s
refactor: complete WebAssembly migration - proper architecture
Phase 8: Complete WebAssembly 렌더 모드 전환 (정공법)

Migration Summary:
- ALL Admin components → TaxBaik.Web.Client
- Routes.razor, Pages/*, Layout/*, Shared/*, Forms/*
- App.razor → TaxBaik.WasmClient (호스트 컴포넌트)
- Shared utilities → TaxBaik.Application.Utils

Architecture:
 App.razor: TaxBaik.WasmClient (WebAssembly, 호스트)
 Routes + Pages: TaxBaik.WasmClient (WebAssembly)
 Layout + Shared + Forms: TaxBaik.WasmClient (WebAssembly)
 Services: TaxBaik.Web (API-First)

Key Changes:
- Namespaces: TaxBaik.Web.Components.Admin → TaxBaik.WasmClient.Components.Admin
- Shared utilities: TaxBaik.Application.Utils (single source of truth)
- Program.cs: MapRazorComponents<TaxBaik.WasmClient.Components.Admin.App>()
- _Imports.razor: Components/Admin 폴더에 재구성

Build Status:  0 errors, 0 warnings

Benefits:
- Stateless server (no Circuit memory)
- Client-side rendering (WebAssembly)
- Unlimited concurrent users (horizontal scaling)
- ERP-ready architecture

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-03 01:03:51 +09:00

154 lines
5.5 KiB
Plaintext

@page "/admin/tax-filings"
@attribute [Authorize]
@using TaxBaik.Web.Services
@using TaxBaik.Domain.Entities
@using TaxBaik.WasmClient.Components.Admin.Shared
@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">
<CommonCodeSelect @bind-Value="newFilingType" Group="FILING_TYPE" Label="신고 유형 *" />
</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();
protected override async Task OnParametersSetAsync()
{
}
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, 100, search: value);
return items;
}
catch
{
return [];
}
}
private static string GetClientDisplayName(Client client)
=> !string.IsNullOrWhiteSpace(client.CompanyName)
? client.CompanyName
: !string.IsNullOrWhiteSpace(client.Name)
? client.Name
: $"Client #{client.Id}";
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);
}
}
}