208 lines
8.2 KiB
Plaintext
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}";
|
|
}
|