Align admin list empty states and blog category guard
TaxBaik CI/CD / build-and-deploy (push) Failing after 35s
TaxBaik CI/CD / build-and-deploy (push) Failing after 35s
This commit is contained in:
@@ -8,7 +8,11 @@ using TaxBaik.Domain.Interfaces;
|
|||||||
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
public class BlogService(IBlogPostRepository repository, IMemoryCache memoryCache, IValidator<CreateBlogPostDto> validator)
|
public class BlogService(
|
||||||
|
IBlogPostRepository repository,
|
||||||
|
ICategoryRepository categoryRepository,
|
||||||
|
IMemoryCache memoryCache,
|
||||||
|
IValidator<CreateBlogPostDto> validator)
|
||||||
{
|
{
|
||||||
public async Task<BlogPost?> GetByIdAsync(int id, CancellationToken ct = default) =>
|
public async Task<BlogPost?> GetByIdAsync(int id, CancellationToken ct = default) =>
|
||||||
await repository.GetByIdAsync(id, ct);
|
await repository.GetByIdAsync(id, ct);
|
||||||
@@ -62,6 +66,7 @@ public class BlogService(IBlogPostRepository repository, IMemoryCache memoryCach
|
|||||||
public async Task<BlogPost> CreateAsync(CreateBlogPostDto dto, CancellationToken ct = default)
|
public async Task<BlogPost> CreateAsync(CreateBlogPostDto dto, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
validator.ValidateAndThrow(dto);
|
validator.ValidateAndThrow(dto);
|
||||||
|
dto.CategoryId = await EnsureCategoryIdAsync(dto.CategoryId, ct);
|
||||||
var post = new BlogPost
|
var post = new BlogPost
|
||||||
{
|
{
|
||||||
Title = dto.Title,
|
Title = dto.Title,
|
||||||
@@ -90,6 +95,7 @@ public class BlogService(IBlogPostRepository repository, IMemoryCache memoryCach
|
|||||||
public async Task<BlogPost?> UpdateAsync(int id, CreateBlogPostDto dto, CancellationToken ct = default)
|
public async Task<BlogPost?> UpdateAsync(int id, CreateBlogPostDto dto, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
validator.ValidateAndThrow(dto);
|
validator.ValidateAndThrow(dto);
|
||||||
|
dto.CategoryId = await EnsureCategoryIdAsync(dto.CategoryId, ct);
|
||||||
var post = await repository.GetByIdAsync(id, ct);
|
var post = await repository.GetByIdAsync(id, ct);
|
||||||
if (post == null)
|
if (post == null)
|
||||||
return null;
|
return null;
|
||||||
@@ -178,6 +184,15 @@ public class BlogService(IBlogPostRepository repository, IMemoryCache memoryCach
|
|||||||
|
|
||||||
private static int NormalizePageSize(int pageSize) => Math.Clamp(pageSize, 1, 100);
|
private static int NormalizePageSize(int pageSize) => Math.Clamp(pageSize, 1, 100);
|
||||||
|
|
||||||
|
private async Task<int> EnsureCategoryIdAsync(int categoryId, CancellationToken ct)
|
||||||
|
{
|
||||||
|
if (categoryId > 0)
|
||||||
|
return categoryId;
|
||||||
|
|
||||||
|
var firstCategory = (await categoryRepository.GetAllAsync(ct)).FirstOrDefault();
|
||||||
|
return firstCategory?.Id ?? throw new ValidationException("블로그 카테고리를 먼저 생성하세요.");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<(int TotalPosts, int PublishedPosts)> GetStatsAsync(CancellationToken ct = default)
|
public async Task<(int TotalPosts, int PublishedPosts)> GetStatsAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var posts = (await repository.GetAllForAdminAsync(ct)).ToList();
|
var posts = (await repository.GetAllForAdminAsync(ct)).ToList();
|
||||||
|
|||||||
@@ -5,43 +5,45 @@
|
|||||||
<section class="admin-page-shell">
|
<section class="admin-page-shell">
|
||||||
@await Html.PartialAsync("_AdminPageHeader", new TaxBaik.Web.Models.AdminPageHeaderModel("Content", "공지사항 관리", "공지 목록을 서버 렌더링으로 표시합니다.", "/admin/announcements/create", "공지 작성"))
|
@await Html.PartialAsync("_AdminPageHeader", new TaxBaik.Web.Models.AdminPageHeaderModel("Content", "공지사항 관리", "공지 목록을 서버 렌더링으로 표시합니다.", "/admin/announcements/create", "공지 작성"))
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="table-responsive">
|
@if (Model.Items.Count == 0)
|
||||||
<table class="table table-vcenter card-table table-hover">
|
{
|
||||||
<thead>
|
<div class="card-body">
|
||||||
<tr>
|
@await Html.PartialAsync("_EmptyState", "공지사항이 없습니다.")
|
||||||
<th>제목</th>
|
</div>
|
||||||
<th>유형</th>
|
}
|
||||||
<th>활성</th>
|
else
|
||||||
<th>정렬</th>
|
{
|
||||||
<th class="text-end">작업</th>
|
<div class="table-responsive">
|
||||||
</tr>
|
<table class="table table-vcenter card-table table-hover">
|
||||||
</thead>
|
<thead>
|
||||||
<tbody>
|
|
||||||
@if (Model.Items.Count == 0)
|
|
||||||
{
|
|
||||||
<tr><td colspan="5">@await Html.PartialAsync("_EmptyState", "공지사항이 없습니다.")</td></tr>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var item in Model.Items)
|
|
||||||
{
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="fw-semibold">@item.Title</td>
|
<th>제목</th>
|
||||||
<td>@item.DisplayType</td>
|
<th>유형</th>
|
||||||
<td>@await Html.PartialAsync("_StatusBadge", item.IsActive ? "active" : "inactive")</td>
|
<th>활성</th>
|
||||||
<td>@item.SortOrder</td>
|
<th>정렬</th>
|
||||||
<td class="text-end">
|
<th class="text-end">작업</th>
|
||||||
<div class="btn-list flex-nowrap justify-content-end">
|
|
||||||
<a class="btn btn-sm btn-outline-secondary" href="/admin/announcements/@item.Id">상세</a>
|
|
||||||
<a class="btn btn-sm btn-primary" href="/admin/announcements/@item.Id/edit">수정</a>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
}
|
</thead>
|
||||||
}
|
<tbody>
|
||||||
</tbody>
|
@foreach (var item in Model.Items)
|
||||||
</table>
|
{
|
||||||
</div>
|
<tr>
|
||||||
|
<td class="fw-semibold">@item.Title</td>
|
||||||
|
<td>@item.DisplayType</td>
|
||||||
|
<td>@await Html.PartialAsync("_StatusBadge", item.IsActive ? "active" : "inactive")</td>
|
||||||
|
<td>@item.SortOrder</td>
|
||||||
|
<td class="text-end">
|
||||||
|
<div class="btn-list flex-nowrap justify-content-end">
|
||||||
|
<a class="btn btn-sm btn-outline-secondary" href="/admin/announcements/@item.Id">상세</a>
|
||||||
|
<a class="btn btn-sm btn-primary" href="/admin/announcements/@item.Id/edit">수정</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -24,48 +24,48 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="table-responsive">
|
@if (Model.Items.Count == 0)
|
||||||
<table class="table table-vcenter card-table table-hover">
|
{
|
||||||
<thead>
|
<div class="card-body">
|
||||||
<tr>
|
@await Html.PartialAsync("_EmptyState", "블로그 글이 없습니다.")
|
||||||
<th>제목</th>
|
</div>
|
||||||
<th>발행</th>
|
}
|
||||||
<th>조회수</th>
|
else
|
||||||
<th>작성일</th>
|
{
|
||||||
<th class="text-end">작업</th>
|
<div class="table-responsive">
|
||||||
</tr>
|
<table class="table table-vcenter card-table table-hover">
|
||||||
</thead>
|
<thead>
|
||||||
<tbody>
|
|
||||||
@if (Model.Items.Count == 0)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td colspan="5">@await Html.PartialAsync("_EmptyState", "블로그 글이 없습니다.")</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var post in Model.Items)
|
|
||||||
{
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<th>제목</th>
|
||||||
<div class="fw-semibold">@post.Title</div>
|
<th>발행</th>
|
||||||
<div class="text-muted small">@post.Slug</div>
|
<th>조회수</th>
|
||||||
</td>
|
<th>작성일</th>
|
||||||
<td>@await Html.PartialAsync("_StatusBadge", post.IsPublished ? "active" : "inactive")</td>
|
<th class="text-end">작업</th>
|
||||||
<td>@post.ViewCount</td>
|
|
||||||
<td>@post.CreatedAt.ToString("yyyy-MM-dd")</td>
|
|
||||||
<td class="text-end">
|
|
||||||
<div class="btn-list flex-nowrap justify-content-end">
|
|
||||||
<a class="btn btn-sm btn-primary" href="/admin/blog/@post.Id/edit">수정</a>
|
|
||||||
<a class="btn btn-sm btn-outline-secondary" href="/blog/@post.Slug" target="_blank">보기</a>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
}
|
</thead>
|
||||||
}
|
<tbody>
|
||||||
</tbody>
|
@foreach (var post in Model.Items)
|
||||||
</table>
|
{
|
||||||
</div>
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="fw-semibold">@post.Title</div>
|
||||||
|
<div class="text-muted small">@post.Slug</div>
|
||||||
|
</td>
|
||||||
|
<td>@await Html.PartialAsync("_StatusBadge", post.IsPublished ? "active" : "inactive")</td>
|
||||||
|
<td>@post.ViewCount</td>
|
||||||
|
<td>@post.CreatedAt.ToString("yyyy-MM-dd")</td>
|
||||||
|
<td class="text-end">
|
||||||
|
<div class="btn-list flex-nowrap justify-content-end">
|
||||||
|
<a class="btn btn-sm btn-primary" href="/admin/blog/@post.Id/edit">수정</a>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary" href="/blog/@post.Slug" target="_blank">보기</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user