This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Admin.ClientLogs.IndexModel
|
||||
@{ ViewData["Title"] = "클라이언트 로그"; }
|
||||
<section class="container py-5"><h1 class="h2 fw-bold">클라이언트 로그</h1></section>
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("운영 로그", "클라이언트 로그", "고객별 작업 이력을 확인합니다.", null, null)' />
|
||||
<partial name="_ComingSoon" model="클라이언트 로그" />
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Admin.Clients.CreateModel
|
||||
@{
|
||||
ViewData["Title"] = "고객 등록";
|
||||
}
|
||||
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("고객 관리", "고객 등록", "FluentValidation과 Tabler 표준 폼으로 신규 고객을 등록합니다.", null, null)' />
|
||||
|
||||
<form method="post" class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<div asp-validation-summary="All" class="alert alert-danger"></div>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Name" class="form-label"></label>
|
||||
<input asp-for="Input.Name" class="form-control" />
|
||||
<span asp-validation-for="Input.Name" class="text-danger small"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.CompanyName" class="form-label"></label>
|
||||
<input asp-for="Input.CompanyName" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Phone" class="form-label"></label>
|
||||
<input asp-for="Input.Phone" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Email" class="form-label"></label>
|
||||
<input asp-for="Input.Email" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.Status" class="form-label"></label>
|
||||
<input asp-for="Input.Status" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.ServiceType" class="form-label"></label>
|
||||
<input asp-for="Input.ServiceType" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.TaxType" class="form-label"></label>
|
||||
<input asp-for="Input.TaxType" class="form-control" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label asp-for="Input.Source" class="form-label"></label>
|
||||
<input asp-for="Input.Source" class="form-control" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label asp-for="Input.Memo" class="form-label"></label>
|
||||
<textarea asp-for="Input.Memo" class="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-white d-flex justify-content-end gap-2">
|
||||
<a class="btn btn-outline-secondary" href="/admin/clients">취소</a>
|
||||
<button class="btn btn-primary" type="submit">저장</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.DTOs;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Web.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Admin.Clients;
|
||||
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class CreateModel(ClientService clientService) : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public CreateClientDto Input { get; set; } = new();
|
||||
|
||||
public IActionResult OnGet() => Page();
|
||||
|
||||
public async Task<IActionResult> OnPostAsync(CancellationToken ct)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return Page();
|
||||
|
||||
var id = await clientService.CreateAsync(Input, ct);
|
||||
return RedirectToPage("/Admin/Clients/Detail", new { id });
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,57 @@
|
||||
@page "{id:int}"
|
||||
@model TaxBaik.Web.Pages.Admin.Clients.DetailModel
|
||||
@{ ViewData["Title"] = "클라이언트 상세"; }
|
||||
<section class="container py-5"><h1 class="h2 fw-bold">클라이언트 상세</h1></section>
|
||||
@{
|
||||
ViewData["Title"] = "고객 상세";
|
||||
}
|
||||
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("고객 관리", "고객 상세", "고객의 기본 정보와 수정 경로를 확인합니다.", null, null)' />
|
||||
|
||||
@if (Model.Client is null)
|
||||
{
|
||||
<partial name="_EmptyState" model="조회한 고객을 찾을 수 없습니다." />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-8">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-3">고객명</dt>
|
||||
<dd class="col-sm-9">@Model.Client.Name</dd>
|
||||
<dt class="col-sm-3">회사명</dt>
|
||||
<dd class="col-sm-9">@Model.Client.CompanyName</dd>
|
||||
<dt class="col-sm-3">전화</dt>
|
||||
<dd class="col-sm-9">@Model.Client.Phone</dd>
|
||||
<dt class="col-sm-3">이메일</dt>
|
||||
<dd class="col-sm-9">@Model.Client.Email</dd>
|
||||
<dt class="col-sm-3">상태</dt>
|
||||
<dd class="col-sm-9"><partial name="_StatusBadge" model="Model.Client.Status" /></dd>
|
||||
<dt class="col-sm-3">서비스 유형</dt>
|
||||
<dd class="col-sm-9">@Model.Client.ServiceType</dd>
|
||||
<dt class="col-sm-3">세금 유형</dt>
|
||||
<dd class="col-sm-9">@Model.Client.TaxType</dd>
|
||||
<dt class="col-sm-3">유입 경로</dt>
|
||||
<dd class="col-sm-9">@Model.Client.Source</dd>
|
||||
<dt class="col-sm-3">등록일</dt>
|
||||
<dd class="col-sm-9">@Model.Client.CreatedAt.ToString("yyyy-MM-dd HH:mm")</dd>
|
||||
<dt class="col-sm-3">수정일</dt>
|
||||
<dd class="col-sm-9">@Model.Client.UpdatedAt.ToString("yyyy-MM-dd HH:mm")</dd>
|
||||
<dt class="col-sm-3">메모</dt>
|
||||
<dd class="col-sm-9">@Model.Client.Memo</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body d-grid gap-2">
|
||||
<a class="btn btn-primary" href="/admin/clients/@Model.Client.Id/edit">수정</a>
|
||||
<a class="btn btn-outline-secondary" href="/admin/clients">목록</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</section>
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Web.Services;
|
||||
namespace TaxBaik.Web.Pages.Admin.Clients;
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class DetailModel : PageModel { }
|
||||
|
||||
namespace TaxBaik.Web.Pages.Admin.Clients;
|
||||
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class DetailModel(ClientService clientService) : PageModel
|
||||
{
|
||||
public TaxBaik.Domain.Entities.Client? Client { get; private set; }
|
||||
|
||||
public async Task OnGetAsync(int id, CancellationToken ct)
|
||||
{
|
||||
Client = await clientService.GetByIdAsync(id, ct);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,62 @@
|
||||
@page "{id:int}"
|
||||
@model TaxBaik.Web.Pages.Admin.Clients.EditModel
|
||||
@{ ViewData["Title"] = "클라이언트 수정"; }
|
||||
<section class="container py-5"><h1 class="h2 fw-bold">클라이언트 수정</h1></section>
|
||||
@{
|
||||
ViewData["Title"] = "고객 수정";
|
||||
}
|
||||
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("고객 관리", "고객 수정", "기존 고객 정보를 불러와 Tabler 표준 폼으로 수정합니다.", null, null)' />
|
||||
|
||||
<form method="post" class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<div asp-validation-summary="All" class="alert alert-danger"></div>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Name" class="form-label"></label>
|
||||
<input asp-for="Input.Name" class="form-control" />
|
||||
<span asp-validation-for="Input.Name" class="text-danger small"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.CompanyName" class="form-label"></label>
|
||||
<input asp-for="Input.CompanyName" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Phone" class="form-label"></label>
|
||||
<input asp-for="Input.Phone" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Email" class="form-label"></label>
|
||||
<input asp-for="Input.Email" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.Status" class="form-label"></label>
|
||||
<input asp-for="Input.Status" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.ServiceType" class="form-label"></label>
|
||||
<input asp-for="Input.ServiceType" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.TaxType" class="form-label"></label>
|
||||
<input asp-for="Input.TaxType" class="form-control" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label asp-for="Input.Source" class="form-label"></label>
|
||||
<input asp-for="Input.Source" class="form-control" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label asp-for="Input.Memo" class="form-label"></label>
|
||||
<textarea asp-for="Input.Memo" class="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-white d-flex justify-content-end gap-2">
|
||||
<a class="btn btn-outline-secondary" href="/admin/clients/@Model.Id">취소</a>
|
||||
<button class="btn btn-primary" type="submit">저장</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<form method="post" asp-page-handler="Delete" class="mt-3" onsubmit="return confirm('정말 삭제하시겠습니까?');">
|
||||
<button class="btn btn-outline-danger" type="submit">삭제</button>
|
||||
</form>
|
||||
|
||||
@@ -1,7 +1,55 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.DTOs;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Web.Services;
|
||||
namespace TaxBaik.Web.Pages.Admin.Clients;
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class EditModel : PageModel { }
|
||||
|
||||
namespace TaxBaik.Web.Pages.Admin.Clients;
|
||||
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class EditModel(ClientService clientService) : PageModel
|
||||
{
|
||||
[BindProperty(SupportsGet = true)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public CreateClientDto Input { get; set; } = new();
|
||||
|
||||
public async Task<IActionResult> OnGetAsync(CancellationToken ct)
|
||||
{
|
||||
var client = await clientService.GetByIdAsync(Id, ct);
|
||||
if (client is null)
|
||||
return NotFound();
|
||||
|
||||
Input = new CreateClientDto
|
||||
{
|
||||
Name = client.Name,
|
||||
CompanyName = client.CompanyName,
|
||||
Phone = client.Phone,
|
||||
Email = client.Email,
|
||||
ServiceType = client.ServiceType,
|
||||
TaxType = client.TaxType,
|
||||
Status = client.Status,
|
||||
Source = client.Source,
|
||||
Memo = client.Memo
|
||||
};
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync(CancellationToken ct)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return Page();
|
||||
|
||||
await clientService.UpdateAsync(Id, Input, ct);
|
||||
return RedirectToPage("/Admin/Clients/Detail", new { id = Id });
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostDeleteAsync(CancellationToken ct)
|
||||
{
|
||||
await clientService.DeleteAsync(Id, ct);
|
||||
return RedirectToPage("/Admin/Clients/Index");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,23 +4,13 @@
|
||||
ViewData["Title"] = "고객 관리";
|
||||
}
|
||||
|
||||
<section class="container-fluid py-4">
|
||||
<div class="page-header d-print-none mb-4">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<div class="page-pretitle">CRM</div>
|
||||
<h2 class="page-title">고객 관리</h2>
|
||||
</div>
|
||||
<div class="col-auto ms-auto">
|
||||
<a class="btn btn-primary" href="/admin/customers/create">고객 등록</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("고객 관리", "고객 목록", "검색, 상태 필터, 페이지네이션 기준의 고객 목록입니다.", null, null)' />
|
||||
|
||||
<form method="get" class="card mb-3">
|
||||
<form method="get" class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-5">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" for="Search">검색</label>
|
||||
<input class="form-control" id="Search" name="search" value="@Model.Search" placeholder="이름, 전화, 회사명" />
|
||||
</div>
|
||||
@@ -40,17 +30,18 @@
|
||||
<option value="50" selected="@(Model.PageSize == 50)">50</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 d-grid">
|
||||
<div class="col-md-3 d-grid d-sm-flex gap-2">
|
||||
<button class="btn btn-primary" type="submit">검색</button>
|
||||
<a class="btn btn-outline-secondary" href="/admin/clients">초기화</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="card">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-vcenter card-table">
|
||||
<thead>
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>고객명</th>
|
||||
<th>회사명</th>
|
||||
@@ -61,27 +52,31 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (Model.Items.Count == 0)
|
||||
{
|
||||
<tr><td colspan="6"><partial name="_EmptyState" model="검색 결과가 없습니다." /></td></tr>
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in Model.Items)
|
||||
@if (Model.Items.Count == 0)
|
||||
{
|
||||
<tr>
|
||||
<td class="fw-semibold">@item.Name</td>
|
||||
<td>@item.CompanyName</td>
|
||||
<td>@item.Phone</td>
|
||||
<td><partial name="_StatusBadge" model="@(item.Status)" /></td>
|
||||
<td>@item.CreatedAt.ToString("yyyy-MM-dd")</td>
|
||||
<td class="text-end">
|
||||
<a class="btn btn-sm btn-outline-primary" href="/admin/customers/@item.Id">상세</a>
|
||||
<a class="btn btn-sm btn-outline-secondary" href="/admin/customers/@item.Id/edit">수정</a>
|
||||
<td colspan="6">
|
||||
<partial name="_EmptyState" model="검색 결과가 없습니다." />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in Model.Items)
|
||||
{
|
||||
<tr>
|
||||
<td class="fw-semibold">@item.Name</td>
|
||||
<td>@item.CompanyName</td>
|
||||
<td>@item.Phone</td>
|
||||
<td><partial name="_StatusBadge" model="item.Status" /></td>
|
||||
<td>@item.CreatedAt.ToString("yyyy-MM-dd")</td>
|
||||
<td class="text-end">
|
||||
<a class="btn btn-sm btn-outline-secondary" href="/admin/clients/@item.Id">상세</a>
|
||||
<a class="btn btn-sm btn-outline-primary" href="/admin/clients/@item.Id/edit">수정</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Admin.ConsultingActivities.IndexModel
|
||||
@{ ViewData["Title"] = "상담 활동"; }
|
||||
<section class="container py-5"><h1 class="h2 fw-bold">상담 활동</h1></section>
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("세무 운영", "상담 활동", "상담 히스토리와 후속 조치를 관리합니다.", null, null)' />
|
||||
<partial name="_ComingSoon" model="상담 활동" />
|
||||
</section>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Admin.Contracts.IndexModel
|
||||
@{ ViewData["Title"] = "계약"; }
|
||||
<section class="container py-5"><h1 class="h2 fw-bold">계약 관리</h1></section>
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("세무 운영", "계약 관리", "계약 상태와 만료일을 추적합니다.", null, null)' />
|
||||
<partial name="_ComingSoon" model="계약 관리" />
|
||||
</section>
|
||||
|
||||
@@ -1,66 +1,2 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Admin.Customers.CreateModel
|
||||
@{
|
||||
ViewData["Title"] = "고객 등록";
|
||||
}
|
||||
|
||||
<section class="container py-4 py-lg-5">
|
||||
<div class="mb-4">
|
||||
<p class="text-uppercase text-muted small mb-2">Customer Management</p>
|
||||
<h1 class="h2 fw-bold mb-1">고객 등록</h1>
|
||||
</div>
|
||||
|
||||
<form method="post" class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<div asp-validation-summary="All" class="alert alert-danger"></div>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Name" class="form-label"></label>
|
||||
<input asp-for="Input.Name" class="form-control" />
|
||||
<span asp-validation-for="Input.Name" class="text-danger small"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.CompanyName" class="form-label"></label>
|
||||
<input asp-for="Input.CompanyName" class="form-control" />
|
||||
<span asp-validation-for="Input.CompanyName" class="text-danger small"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Phone" class="form-label"></label>
|
||||
<input asp-for="Input.Phone" class="form-control" />
|
||||
<span asp-validation-for="Input.Phone" class="text-danger small"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Email" class="form-label"></label>
|
||||
<input asp-for="Input.Email" class="form-control" />
|
||||
<span asp-validation-for="Input.Email" class="text-danger small"></span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.Status" class="form-label"></label>
|
||||
<input asp-for="Input.Status" class="form-control" />
|
||||
<span asp-validation-for="Input.Status" class="text-danger small"></span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.ServiceType" class="form-label"></label>
|
||||
<input asp-for="Input.ServiceType" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.TaxType" class="form-label"></label>
|
||||
<input asp-for="Input.TaxType" class="form-control" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label asp-for="Input.Source" class="form-label"></label>
|
||||
<input asp-for="Input.Source" class="form-control" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label asp-for="Input.Memo" class="form-label"></label>
|
||||
<textarea asp-for="Input.Memo" class="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-white d-flex justify-content-end gap-2">
|
||||
<a class="btn btn-outline-secondary" href="/admin/customers">취소</a>
|
||||
<button class="btn btn-dark" type="submit">저장</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.DTOs;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Web.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Admin.Customers;
|
||||
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class CreateModel(ClientService clientService) : PageModel
|
||||
public class CreateModel : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public CreateClientDto Input { get; set; } = new();
|
||||
|
||||
public IActionResult OnGet() => Page();
|
||||
|
||||
public async Task<IActionResult> OnPostAsync(CancellationToken ct)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return Page();
|
||||
|
||||
await clientService.CreateAsync(Input, ct);
|
||||
return RedirectToPage("/Admin/Customers/Index");
|
||||
}
|
||||
public IActionResult OnGet() => Redirect("/admin/clients/create");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +1,2 @@
|
||||
@page "{id:int}"
|
||||
@model TaxBaik.Web.Pages.Admin.Customers.DetailModel
|
||||
@{
|
||||
ViewData["Title"] = "고객 상세";
|
||||
}
|
||||
|
||||
<section class="container py-4 py-lg-5">
|
||||
@if (Model.Client is null)
|
||||
{
|
||||
<div class="alert alert-warning">고객을 찾을 수 없습니다.</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-end gap-3 mb-4">
|
||||
<div>
|
||||
<p class="text-uppercase text-muted small mb-2">Customer Management</p>
|
||||
<h1 class="h2 fw-bold mb-1">@Model.Client.Name</h1>
|
||||
<p class="text-muted mb-0">고객 기본 정보와 변경 이력을 확인합니다.</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-secondary" href="/admin/customers/@Model.Client.Id/edit">수정</a>
|
||||
<a class="btn btn-dark" href="/admin/customers">목록</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-3">회사명</dt><dd class="col-sm-9">@Model.Client.CompanyName</dd>
|
||||
<dt class="col-sm-3">전화</dt><dd class="col-sm-9">@Model.Client.Phone</dd>
|
||||
<dt class="col-sm-3">이메일</dt><dd class="col-sm-9">@Model.Client.Email</dd>
|
||||
<dt class="col-sm-3">상태</dt><dd class="col-sm-9">@Model.Client.Status</dd>
|
||||
<dt class="col-sm-3">서비스 유형</dt><dd class="col-sm-9">@Model.Client.ServiceType</dd>
|
||||
<dt class="col-sm-3">세금 유형</dt><dd class="col-sm-9">@Model.Client.TaxType</dd>
|
||||
<dt class="col-sm-3">유입 경로</dt><dd class="col-sm-9">@Model.Client.Source</dd>
|
||||
<dt class="col-sm-3">메모</dt><dd class="col-sm-9">@Model.Client.Memo</dd>
|
||||
<dt class="col-sm-3">생성일</dt><dd class="col-sm-9">@Model.Client.CreatedAt.ToString("yyyy-MM-dd HH:mm")</dd>
|
||||
<dt class="col-sm-3">수정일</dt><dd class="col-sm-9">@Model.Client.UpdatedAt.ToString("yyyy-MM-dd HH:mm")</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Web.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Admin.Customers;
|
||||
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class DetailModel(ClientService clientService) : PageModel
|
||||
public class DetailModel : PageModel
|
||||
{
|
||||
public TaxBaik.Domain.Entities.Client? Client { get; private set; }
|
||||
|
||||
public async Task OnGetAsync(int id, CancellationToken ct)
|
||||
{
|
||||
Client = await clientService.GetByIdAsync(id, ct);
|
||||
}
|
||||
public IActionResult OnGet(int id) => Redirect($"/admin/clients/{id}");
|
||||
}
|
||||
|
||||
@@ -1,65 +1,2 @@
|
||||
@page "{id:int}"
|
||||
@model TaxBaik.Web.Pages.Admin.Customers.EditModel
|
||||
@{
|
||||
ViewData["Title"] = "고객 수정";
|
||||
}
|
||||
|
||||
<section class="container py-4 py-lg-5">
|
||||
<div class="mb-4">
|
||||
<p class="text-uppercase text-muted small mb-2">Customer Management</p>
|
||||
<h1 class="h2 fw-bold mb-1">고객 수정</h1>
|
||||
</div>
|
||||
|
||||
<form method="post" class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<div asp-validation-summary="All" class="alert alert-danger"></div>
|
||||
<input type="hidden" asp-for="Id" />
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Name" class="form-label"></label>
|
||||
<input asp-for="Input.Name" class="form-control" />
|
||||
<span asp-validation-for="Input.Name" class="text-danger small"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.CompanyName" class="form-label"></label>
|
||||
<input asp-for="Input.CompanyName" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Phone" class="form-label"></label>
|
||||
<input asp-for="Input.Phone" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Input.Email" class="form-label"></label>
|
||||
<input asp-for="Input.Email" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.Status" class="form-label"></label>
|
||||
<input asp-for="Input.Status" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.ServiceType" class="form-label"></label>
|
||||
<input asp-for="Input.ServiceType" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Input.TaxType" class="form-label"></label>
|
||||
<input asp-for="Input.TaxType" class="form-control" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label asp-for="Input.Source" class="form-label"></label>
|
||||
<input asp-for="Input.Source" class="form-control" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label asp-for="Input.Memo" class="form-label"></label>
|
||||
<textarea asp-for="Input.Memo" class="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-white d-flex justify-content-between gap-2">
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-outline-danger" type="submit" formaction="?handler=Delete" formmethod="post" onclick="return confirm('삭제하시겠습니까?');">삭제</button>
|
||||
<a class="btn btn-outline-secondary" href="/admin/customers/@Model.Id">취소</a>
|
||||
<button class="btn btn-dark" type="submit">저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@@ -1,56 +1,12 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.DTOs;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Web.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Admin.Customers;
|
||||
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class EditModel(ClientService clientService) : PageModel
|
||||
public class EditModel : PageModel
|
||||
{
|
||||
[BindProperty(SupportsGet = true)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public CreateClientDto Input { get; set; } = new();
|
||||
|
||||
public async Task<IActionResult> OnGetAsync(CancellationToken ct)
|
||||
{
|
||||
var client = await clientService.GetByIdAsync(Id, ct);
|
||||
if (client is null)
|
||||
return NotFound();
|
||||
|
||||
Input = new CreateClientDto
|
||||
{
|
||||
Name = client.Name,
|
||||
CompanyName = client.CompanyName,
|
||||
Phone = client.Phone,
|
||||
Email = client.Email,
|
||||
ServiceType = client.ServiceType,
|
||||
TaxType = client.TaxType,
|
||||
Status = client.Status,
|
||||
Source = client.Source,
|
||||
Memo = client.Memo
|
||||
};
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync(CancellationToken ct)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return Page();
|
||||
|
||||
await clientService.UpdateAsync(Id, Input, ct);
|
||||
return RedirectToPage("/Admin/Customers/Detail", new { id = Id });
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostDeleteAsync(CancellationToken ct)
|
||||
{
|
||||
await clientService.DeleteAsync(Id, ct);
|
||||
return RedirectToPage("/Admin/Customers/Index");
|
||||
}
|
||||
public IActionResult OnGet(int id) => Redirect($"/admin/clients/{id}/edit");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,93 +1,2 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Admin.Customers.IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "고객 목록";
|
||||
}
|
||||
|
||||
<section class="container py-4 py-lg-5">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-end gap-3 mb-4">
|
||||
<div>
|
||||
<p class="text-uppercase text-muted small mb-2">Customer Management</p>
|
||||
<h1 class="h2 fw-bold mb-1">고객 목록</h1>
|
||||
<p class="text-muted mb-0">검색, 상태 필터, 페이지네이션 기준의 고객 목록입니다.</p>
|
||||
</div>
|
||||
<a class="btn btn-dark" href="/admin/customers/create">고객 등록</a>
|
||||
</div>
|
||||
|
||||
<form method="get" class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" for="Search">검색</label>
|
||||
<input class="form-control" id="Search" name="Search" value="@Model.Search" placeholder="이름, 전화, 회사명" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="Status">상태</label>
|
||||
<select class="form-select" id="Status" name="Status">
|
||||
<option value="" selected="@(string.IsNullOrWhiteSpace(Model.Status))">전체</option>
|
||||
<option value="active" selected="@(Model.Status == "active")">active</option>
|
||||
<option value="inactive" selected="@(Model.Status == "inactive")">inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label" for="PageSize">페이지 크기</label>
|
||||
<select class="form-select" id="PageSize" name="PageSize">
|
||||
<option value="10" selected="@(Model.PageSize == 10)">10</option>
|
||||
<option value="20" selected="@(Model.PageSize == 20)">20</option>
|
||||
<option value="50" selected="@(Model.PageSize == 50)">50</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 d-grid d-sm-flex gap-2">
|
||||
<button class="btn btn-dark" type="submit">검색</button>
|
||||
<a class="btn btn-outline-secondary" href="/admin/customers">초기화</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>고객명</th>
|
||||
<th>회사명</th>
|
||||
<th>전화</th>
|
||||
<th>상태</th>
|
||||
<th>등록일</th>
|
||||
<th class="text-end">작업</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (Model.Items.Count == 0)
|
||||
{
|
||||
<tr>
|
||||
<td colspan="6">
|
||||
<partial name="_EmptyState" model="@("검색 결과가 없습니다.")" />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in Model.Items)
|
||||
{
|
||||
<tr>
|
||||
<td class="fw-semibold">@item.Name</td>
|
||||
<td>@item.CompanyName</td>
|
||||
<td>@item.Phone</td>
|
||||
<td><partial name="_StatusBadge" model="item.Status" /></td>
|
||||
<td>@item.CreatedAt.ToString("yyyy-MM-dd")</td>
|
||||
<td class="text-end">
|
||||
<a class="btn btn-sm btn-outline-secondary" href="/admin/customers/@item.Id">상세</a>
|
||||
<a class="btn btn-sm btn-outline-primary" href="/admin/customers/@item.Id/edit">수정</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<partial name="_Pagination" model="new { Page = Model.CurrentPage, TotalPages = Model.TotalPages, BuildPageUrl = new Func<int, string>(Model.BuildPageUrl) }" />
|
||||
</section>
|
||||
|
||||
@@ -1,35 +1,12 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Web.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Admin.Customers;
|
||||
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class IndexModel(ClientService clientService) : PageModel
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
public List<TaxBaik.Domain.Entities.Client> Items { get; private set; } = [];
|
||||
public int CurrentPage { get; private set; } = 1;
|
||||
public int TotalPages { get; private set; } = 1;
|
||||
public int TotalCount { get; private set; }
|
||||
|
||||
public string? Search { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public int PageSize { get; set; } = 10;
|
||||
|
||||
public async Task OnGetAsync(string? search, string? status, int page = 1, int pageSize = 10, CancellationToken ct = default)
|
||||
{
|
||||
Search = search;
|
||||
Status = status;
|
||||
PageSize = pageSize;
|
||||
CurrentPage = Math.Max(1, page);
|
||||
|
||||
var (items, total) = await clientService.GetPagedAsync(CurrentPage, pageSize, status, search, ct);
|
||||
Items = items.ToList();
|
||||
TotalCount = total;
|
||||
TotalPages = Math.Max(1, (int)Math.Ceiling(total / (double)pageSize));
|
||||
}
|
||||
|
||||
public string BuildPageUrl(int page) =>
|
||||
$"/admin/customers?search={Uri.EscapeDataString(Search ?? string.Empty)}&status={Uri.EscapeDataString(Status ?? string.Empty)}&page={page}&pageSize={PageSize}";
|
||||
public IActionResult OnGet() => Redirect("/admin/clients");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Admin.RevenueTrackings.IndexModel
|
||||
@{ ViewData["Title"] = "매출 추적"; }
|
||||
<section class="container py-5"><h1 class="h2 fw-bold">매출 추적</h1></section>
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("세무 운영", "매출 추적", "월별 매출 추이를 확인하는 분석 화면입니다.", null, null)' />
|
||||
<partial name="_ComingSoon" model="매출 추적" />
|
||||
</section>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Admin.SeasonSimulator.IndexModel
|
||||
@{ ViewData["Title"] = "시즌 시뮬레이터"; }
|
||||
<section class="container py-5"><h1 class="h2 fw-bold">시즌 시뮬레이터</h1></section>
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("운영 도구", "시즌 시뮬레이터", "정책 변경 시 영향을 미리 확인하는 도구입니다.", null, null)' />
|
||||
<partial name="_ComingSoon" model="시즌 시뮬레이터" />
|
||||
</section>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Admin.Settings.IndexModel
|
||||
@{ ViewData["Title"] = "설정"; }
|
||||
<section class="container py-5"><h1 class="h2 fw-bold">설정</h1></section>
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("환경 설정", "설정", "시스템 기본값과 관리자 운영 정책을 정리합니다.", null, null)' />
|
||||
<partial name="_ComingSoon" model="설정 기능" />
|
||||
</section>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Admin.TaxFilings.IndexModel
|
||||
@{ ViewData["Title"] = "세금 신고"; }
|
||||
<section class="container py-5"><h1 class="h2 fw-bold">세금 신고 관리</h1></section>
|
||||
<section class="container py-4 py-lg-5">
|
||||
<partial name="_AdminPageHeader" model='("세무 운영", "세금 신고", "신고 상태를 한 화면에서 관리합니다.", null, null)' />
|
||||
<partial name="_ComingSoon" model="세금 신고 관리" />
|
||||
</section>
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
</div>
|
||||
<div class="list-group list-group-flush list-group-transparent">
|
||||
<a class="list-group-item list-group-item-action" href="/admin/dashboard">대시보드</a>
|
||||
<a class="list-group-item list-group-item-action" href="/admin/customers">고객 관리</a>
|
||||
<a class="list-group-item list-group-item-action" href="/admin/clients">기존 고객</a>
|
||||
<a class="list-group-item list-group-item-action" href="/admin/clients">고객 관리</a>
|
||||
<a class="list-group-item list-group-item-action" href="/admin/blog">블로그</a>
|
||||
<a class="list-group-item list-group-item-action" href="/admin/inquiries">문의</a>
|
||||
<a class="list-group-item list-group-item-action" href="/admin/announcements">공지사항</a>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
@model string
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body text-center py-5">
|
||||
<div class="text-muted mb-2">준비 중</div>
|
||||
<h3 class="card-title mb-2">@Model</h3>
|
||||
<p class="text-muted mb-0">이 페이지는 현재 구조를 맞춘 상태이며, 다음 단계에서 기능을 채우면 됩니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user