Files
taxbaik/TaxBaik.Web/Components/Admin/Pages/TaxFilings/TaxFilingList.razor
T
kjh2064 1b173376ee
TaxBaik CI/CD / build-and-deploy (push) Failing after 1m53s
refactor: admin ui를 fluent v5와 html 기반으로 전환
2026-06-29 22:37:40 +09:00

208 lines
8.2 KiB
Plaintext

@page "/admin/tax-filings"
@attribute [Authorize]
@using TaxBaik.Web.Services
@using TaxBaik.Domain.Entities
@inject ITaxFilingBrowserClient FilingClient
@inject IClientBrowserClient ClientClient
@inject IJSRuntime JS
<PageTitle>신고 일정 관리</PageTitle>
<section class="admin-page-hero">
<div>
<div class="admin-eyebrow">Tax Schedule</div>
<h1 class="admin-page-title">신고 일정</h1>
<p class="admin-page-subtitle">고객별 세금 신고 마감일을 관리하고 완료 처리합니다.</p>
</div>
<button type="button" class="site-button primary" @onclick="@(() => showAddForm = !showAddForm)">일정 추가</button>
</section>
@if (showAddForm)
{
<div class="admin-surface mb-4">
<h3 class="admin-section-title">새 신고 일정</h3>
<form class="admin-dialog-card" @onsubmit="AddFiling" @onsubmit:preventDefault="true">
<label>고객 검색
<select class="admin-input" @bind="SelectedClientIdText">
<option value="">선택하세요</option>
@foreach (var client in clients)
{
<option value="@client.Id">@GetClientDisplayName(client)</option>
}
</select>
</label>
<label>신고 유형
<select class="admin-input" @bind="newFilingType">
<option value="">선택하세요</option>
@foreach (var t in TaxFilingService.FilingTypes)
{
<option value="@t">@t</option>
}
</select>
</label>
<label>신고 기한 <input class="admin-input" type="text" placeholder="yyyy-MM-dd" @bind="DueDateText" /></label>
<label>메모 <textarea class="admin-input" rows="3" @bind="newMemo"></textarea></label>
<div class="admin-dialog-actions">
<button type="submit" class="site-button primary">저장</button>
<button type="button" class="site-button secondary" @onclick='() => showAddForm = false'>취소</button>
</div>
</form>
</div>
}
<div class="admin-surface">
<div class="admin-tabbar">
<button type="button" class="admin-tab @(activeTab == "pending" ? "active" : "")" @onclick='() => activeTab = "pending"'>신고 예정</button>
<button type="button" class="admin-tab @(activeTab == "filed" ? "active" : "")" @onclick='() => activeTab = "filed"'>신고 완료</button>
<button type="button" class="admin-tab @(activeTab == "overdue" ? "active" : "")" @onclick='() => activeTab = "overdue"'>기한 초과</button>
</div>
@if (CurrentFilings.Count == 0)
{
<div class="muted">항목이 없습니다.</div>
}
else
{
<div class="admin-table-wrap">
<table class="admin-table">
<thead>
<tr>
<th>고객</th>
<th>신고 유형</th>
<th>기한</th>
<th>D-day</th>
<th>메모</th>
<th>처리</th>
</tr>
</thead>
<tbody>
@foreach (var filing in CurrentFilings)
{
var dday = (filing.DueDate.Date - DateTime.Today).Days;
<tr>
<td>@filing.ClientName</td>
<td>@filing.FilingType</td>
<td>@filing.DueDate.ToString("yyyy-MM-dd")</td>
<td>
@if (dday < 0)
{
<span class="status-pill danger">D+@(-dday)</span>
}
else if (dday <= 7)
{
<span class="status-pill warning">D-@dday</span>
}
else
{
<span>D-@dday</span>
}
</td>
<td>@(filing.Memo ?? "")</td>
<td>
<div class="admin-row-actions">
@if (filing.Status == "pending")
{
<button type="button" class="site-button secondary" @onclick="@(() => MarkFiled(filing))">완료</button>
}
<button type="button" class="admin-icon-button danger" @onclick="@(() => DeleteFiling(filing.Id))">✕</button>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
@code {
private List<TaxFiling> allFilings = [];
private List<Client> clients = [];
private bool showAddForm;
private string activeTab = "pending";
private int selectedClientId;
private string newFilingType = "";
private DateTime? newDueDate = DateTime.Today.AddDays(30);
private string newMemo = "";
private string SelectedClientIdText { get => selectedClientId > 0 ? selectedClientId.ToString() : ""; set => selectedClientId = int.TryParse(value, out var id) ? id : 0; }
private string DueDateText { get => newDueDate?.ToString("yyyy-MM-dd") ?? ""; set => newDueDate = DateTime.TryParse(value, out var dt) ? dt : null; }
private List<TaxFiling> CurrentFilings => activeTab switch
{
"filed" => allFilings.Where(x => x.Status == "filed").ToList(),
"overdue" => allFilings.Where(x => x.Status == "overdue").ToList(),
_ => allFilings.Where(x => x.Status == "pending").ToList()
};
protected override async Task OnInitializedAsync() => await Reload();
private async Task Reload()
{
try
{
allFilings = (await FilingClient.GetUpcomingAsync(365)).ToList();
var (clientItems, _) = await ClientClient.GetPagedAsync(pageSize: 1000);
clients = clientItems.ToList();
}
catch (Exception ex)
{
await JS.InvokeVoidAsync("alert", $"오류: {ex.Message}");
}
}
private async Task AddFiling()
{
try
{
if (selectedClientId <= 0)
{
await JS.InvokeVoidAsync("alert", "고객을 선택하세요.");
return;
}
var filing = new TaxFiling
{
ClientId = selectedClientId,
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;
await JS.InvokeVoidAsync("alert", "신고 일정이 추가되었습니다.");
await Reload();
}
else
{
await JS.InvokeVoidAsync("alert", "추가 실패");
}
}
catch (Exception ex)
{
await JS.InvokeVoidAsync("alert", $"오류: {ex.Message}");
}
}
private async Task MarkFiled(TaxFiling filing)
{
filing.Status = "filed";
await FilingClient.UpdateAsync(filing.Id, filing);
await JS.InvokeVoidAsync("alert", "신고 완료 처리되었습니다.");
await Reload();
}
private async Task DeleteFiling(int id)
{
if (!await JS.InvokeAsync<bool>("confirm", "삭제하시겠습니까?")) return;
await FilingClient.DeleteAsync(id);
await JS.InvokeVoidAsync("alert", "삭제되었습니다.");
await Reload();
}
private static string GetClientDisplayName(Client client)
=> !string.IsNullOrWhiteSpace(client.CompanyName) ? client.CompanyName : !string.IsNullOrWhiteSpace(client.Name) ? client.Name : $"Client #{client.Id}";
}