257 lines
9.3 KiB
Plaintext
257 lines
9.3 KiB
Plaintext
@page "/admin/contracts"
|
|
@using TaxBaik.Web.Services.AdminClients
|
|
@inject IContractBrowserClient ContractClient
|
|
@inject IClientBrowserClient ClientClient
|
|
@inject ISnackbar Snackbar
|
|
@inject IDialogService DialogService
|
|
@attribute [Authorize]
|
|
|
|
<PageTitle>계약 관리</PageTitle>
|
|
|
|
<section class="admin-page-hero">
|
|
<div>
|
|
<MudText Typo="Typo.caption" Class="admin-eyebrow">CRM & 세무관리</MudText>
|
|
<MudText Typo="Typo.h4" Class="admin-page-title">계약 관리</MudText>
|
|
<MudText Typo="Typo.body2" Class="admin-page-subtitle">고객 계약과 월 정기수익을 함께 관리합니다.</MudText>
|
|
@if (mrr > 0)
|
|
{
|
|
<MudText Typo="Typo.body2" Class="mt-2">
|
|
월 정기수익:
|
|
<MudChip Size="Size.Small" Color="Color.Primary" Variant="Variant.Filled">₩@mrr.ToString("N0")</MudChip>
|
|
</MudText>
|
|
}
|
|
</div>
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="OpenCreateDialog" StartIcon="@Icons.Material.Filled.Add">
|
|
새 계약 추가
|
|
</MudButton>
|
|
</section>
|
|
|
|
<MudPaper Class="admin-surface" Elevation="0">
|
|
@if (contracts is null)
|
|
{
|
|
<MudProgressLinear Indeterminate="true" />
|
|
}
|
|
else if (contracts.Count == 0)
|
|
{
|
|
<MudAlert Severity="Severity.Info" Class="mt-4">
|
|
<MudIcon Icon="@Icons.Material.Filled.Description" Class="me-2" />
|
|
계약이 없습니다.
|
|
</MudAlert>
|
|
}
|
|
else
|
|
{
|
|
<MudDataGrid T="Contract"
|
|
Items="@contracts"
|
|
Dense="true"
|
|
Hover="true"
|
|
Striped="true"
|
|
Virtualize="true"
|
|
RowsPerPage="30"
|
|
Class="admin-grid">
|
|
<Columns>
|
|
<PropertyColumn Property="x => x.Id" Title="ID" Sortable="true" />
|
|
<TemplateColumn Title="고객">
|
|
<CellTemplate>
|
|
@if (clientMap.TryGetValue(context.Item.ClientId, out var clientName))
|
|
{
|
|
<MudLink Href="@($"/taxbaik/admin/clients/{context.Item.ClientId}")" Color="Color.Primary">
|
|
@clientName
|
|
</MudLink>
|
|
}
|
|
</CellTemplate>
|
|
</TemplateColumn>
|
|
<PropertyColumn Property="x => x.ContractNumber" Title="계약번호" />
|
|
<PropertyColumn Property="x => x.ServiceType" Title="서비스 유형" />
|
|
<PropertyColumn Property="x => x.MonthlyFee" Title="월 수수료" Format="C" />
|
|
<TemplateColumn Title="계약기간">
|
|
<CellTemplate>
|
|
@context.Item.StartDate.ToString("yyyy-MM-dd")
|
|
@if (context.Item.EndDate.HasValue)
|
|
{
|
|
<span>~@context.Item.EndDate.Value.ToString("yyyy-MM-dd")</span>
|
|
}
|
|
</CellTemplate>
|
|
</TemplateColumn>
|
|
<TemplateColumn Title="상태">
|
|
<CellTemplate>
|
|
@{
|
|
var isActive = !context.Item.EndDate.HasValue || context.Item.EndDate.Value >= DateTime.Today;
|
|
}
|
|
@if (isActive)
|
|
{
|
|
<MudChip Size="Size.Small" Color="Color.Success" Variant="Variant.Filled">활성</MudChip>
|
|
}
|
|
else
|
|
{
|
|
<MudChip Size="Size.Small" Color="Color.Default" Variant="Variant.Outlined">만료</MudChip>
|
|
}
|
|
</CellTemplate>
|
|
</TemplateColumn>
|
|
<TemplateColumn Title="작업" Sortable="false">
|
|
<CellTemplate>
|
|
<MudButtonGroup Size="Size.Small" Variant="Variant.Outlined">
|
|
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error"
|
|
OnClick="@(async () => await DeleteContract(context.Item.Id))" />
|
|
</MudButtonGroup>
|
|
</CellTemplate>
|
|
</TemplateColumn>
|
|
</Columns>
|
|
</MudDataGrid>
|
|
}
|
|
</MudPaper>
|
|
|
|
<!-- Create Dialog -->
|
|
<MudDialog @bind-IsVisible="isDialogOpen" Options="new DialogOptions { MaxWidth = MaxWidth.Small, FullWidth = true }">
|
|
<TitleContent>
|
|
<MudText Typo="Typo.h6">새 계약 추가</MudText>
|
|
</TitleContent>
|
|
<DialogContent>
|
|
<MudForm @ref="form">
|
|
<MudSelect T="int" @bind-Value="contractForm.ClientId" Label="고객" Required="true" Variant="Variant.Outlined" FullWidth="true" Class="mb-4">
|
|
@foreach (var client in clients)
|
|
{
|
|
<MudSelectItem Value="@client.Id">@client.CompanyName</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
<MudTextField T="string" @bind-Value="contractForm.ContractNumber" Label="계약번호" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Required="true" />
|
|
<MudTextField T="string" @bind-Value="contractForm.ServiceType" Label="서비스 유형" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Required="true" />
|
|
<MudDatePicker @bind-Date="contractForm.StartDate" Label="계약 시작일" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" Required="true" />
|
|
<MudNumericField T="decimal?" @bind-Value="contractForm.MonthlyFee" Label="월 수수료" Variant="Variant.Outlined" FullWidth="true" Class="mb-4" />
|
|
</MudForm>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<MudButton OnClick="CloseDialog">취소</MudButton>
|
|
<MudButton Color="Color.Primary" OnClick="SaveContract">저장</MudButton>
|
|
</DialogActions>
|
|
</MudDialog>
|
|
|
|
@code {
|
|
[CascadingParameter]
|
|
private Task<AuthenticationState>? AuthStateTask { get; set; }
|
|
|
|
private List<Contract>? contracts;
|
|
private List<Client> clients = [];
|
|
private Dictionary<int, string> clientMap = new();
|
|
private decimal mrr = 0;
|
|
private MudForm? form;
|
|
private bool isDialogOpen;
|
|
private ContractForm contractForm = new();
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender)
|
|
{
|
|
if (AuthStateTask != null)
|
|
{
|
|
var authState = await AuthStateTask;
|
|
if (authState.User.Identity?.IsAuthenticated == true)
|
|
{
|
|
await LoadData();
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task LoadData()
|
|
{
|
|
try
|
|
{
|
|
contracts = await ContractClient.GetAllAsync();
|
|
var (clientItems, _) = await ClientClient.GetPagedAsync();
|
|
clients = clientItems.ToList();
|
|
clientMap = clients.ToDictionary(c => c.Id, c => c.CompanyName ?? "");
|
|
mrr = await ContractClient.GetMonthlyRecurringRevenueAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"데이터 로드 실패: {ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
|
|
private void OpenCreateDialog()
|
|
{
|
|
contractForm = new ContractForm
|
|
{
|
|
ClientId = clients.FirstOrDefault()?.Id ?? 0,
|
|
StartDate = DateTime.Today
|
|
};
|
|
isDialogOpen = true;
|
|
}
|
|
|
|
private async Task SaveContract()
|
|
{
|
|
if (form != null)
|
|
{
|
|
await form.Validate();
|
|
if (!form.IsValid)
|
|
{
|
|
Snackbar.Add("필수 항목을 입력해주세요.", Severity.Warning);
|
|
return;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
var newId = await ContractClient.CreateAsync(
|
|
contractForm.ClientId,
|
|
contractForm.ContractNumber,
|
|
contractForm.ServiceType,
|
|
contractForm.StartDate ?? DateTime.Now,
|
|
contractForm.MonthlyFee);
|
|
|
|
if (newId > 0)
|
|
{
|
|
Snackbar.Add("계약이 추가되었습니다.", Severity.Success);
|
|
CloseDialog();
|
|
await LoadData();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
|
|
private async Task DeleteContract(int id)
|
|
{
|
|
var parameters = new DialogParameters
|
|
{
|
|
{ "Title", "삭제 확인" },
|
|
{ "Message", "이 계약을 삭제하시겠습니까?" }
|
|
};
|
|
|
|
var dialog = await DialogService.ShowAsync<ConfirmDialog>("", parameters);
|
|
var result = await dialog.Result;
|
|
|
|
if (result?.Canceled ?? true)
|
|
return;
|
|
|
|
try
|
|
{
|
|
await ContractClient.DeleteAsync(id);
|
|
Snackbar.Add("계약이 삭제되었습니다.", Severity.Success);
|
|
await LoadData();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
|
|
private void CloseDialog()
|
|
{
|
|
isDialogOpen = false;
|
|
contractForm = new();
|
|
}
|
|
|
|
private class ContractForm
|
|
{
|
|
public int ClientId { get; set; }
|
|
public string ContractNumber { get; set; } = "";
|
|
public string ServiceType { get; set; } = "";
|
|
public DateTime? StartDate { get; set; }
|
|
public decimal? MonthlyFee { get; set; }
|
|
}
|
|
}
|