Files
taxbaik/TaxBaik.Web/Components/Admin/Pages/Blog/BlogEdit.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

140 lines
5.1 KiB
Plaintext

@page "/admin/blog/{id:int}/edit"
@attribute [Authorize]
@using TaxBaik.Application.DTOs
@using TaxBaik.Application.Services
@using TaxBaik.Domain.Interfaces
@inject BlogService BlogService
@inject ICategoryRepository CategoryRepository
@inject NavigationManager Navigation
@inject IJSRuntime JS
<PageTitle>포스트 수정</PageTitle>
<section class="admin-page-hero">
<div>
<div class="admin-eyebrow">Content</div>
<h1 class="admin-page-title">포스트 수정</h1>
<p class="admin-page-subtitle">블로그 포스트를 수정합니다.</p>
</div>
<button type="button" class="site-button secondary" @onclick='() => Navigation.NavigateTo("/taxbaik/admin/blog")'>취소</button>
</section>
@if (isLoading)
{
<div class="admin-surface mt-4"><Skeleton Count="4" CssClass="taxbaik-skeleton-grid" /></div>
}
else if (post == null)
{
<div class="admin-surface mt-4">포스트를 찾을 수 없습니다.</div>
}
else
{
<div class="admin-surface mt-4">
<form class="admin-dialog-card" @onsubmit="SavePost" @onsubmit:preventDefault="true">
<label>제목 * <input class="admin-input" @bind="model.Title" /></label>
<label>카테고리
<select class="admin-input" @bind="CategoryIdText">
<option value="">선택하세요</option>
@foreach (var category in categories)
{
<option value="@category.Id.ToString()">@category.Name</option>
}
</select>
</label>
<label>본문 * <textarea class="admin-input" rows="10" @bind="model.Content"></textarea></label>
<label>태그 (쉼표로 구분) <input class="admin-input" @bind="model.Tags" /></label>
<label>SEO 제목 <input class="admin-input" @bind="model.SeoTitle" /></label>
<label>SEO 설명 <textarea class="admin-input" rows="3" @bind="model.SeoDescription"></textarea></label>
<label><input type="checkbox" @bind="model.IsPublished" /> 발행</label>
<div class="admin-dialog-actions">
<button type="submit" class="site-button primary">저장</button>
<button type="button" class="site-button secondary" @onclick="DeletePost">삭제</button>
</div>
</form>
</div>
}
@code {
[Parameter] public int Id { get; set; }
private Domain.Entities.BlogPost? post;
private List<Domain.Entities.Category> categories = [];
private EditPostModel model = new();
private bool isLoading = true;
private string CategoryIdText { get => model.CategoryId?.ToString() ?? ""; set => model.CategoryId = int.TryParse(value, out var id) ? id : null; }
protected override async Task OnInitializedAsync()
{
try
{
post = await BlogService.GetByIdAsync(Id);
if (post != null)
{
categories = (await CategoryRepository.GetAllAsync()).ToList();
MapPostToModel(post);
}
}
catch (Exception ex)
{
await JS.InvokeVoidAsync("alert", $"포스트 로드 실패: {ex.Message}");
}
finally
{
isLoading = false;
}
}
private void MapPostToModel(Domain.Entities.BlogPost post)
{
model.Title = post.Title;
model.Content = post.Content;
model.CategoryId = post.CategoryId;
model.Tags = post.Tags;
model.SeoTitle = post.SeoTitle;
model.SeoDescription = post.SeoDescription;
model.IsPublished = post.IsPublished;
}
private async Task SavePost()
{
if (post == null) return;
try
{
await BlogService.UpdateAsync(post.Id, new CreateBlogPostDto
{
Title = model.Title,
Content = model.Content,
CategoryId = model.CategoryId,
Tags = model.Tags,
SeoTitle = model.SeoTitle,
SeoDescription = model.SeoDescription,
IsPublished = model.IsPublished
});
await JS.InvokeVoidAsync("alert", "포스트가 저장되었습니다.");
Navigation.NavigateTo("/taxbaik/admin/blog");
}
catch (ValidationException ex)
{
await JS.InvokeVoidAsync("alert", ex.Message);
}
}
private async Task DeletePost()
{
if (post == null) return;
if (!await JS.InvokeAsync<bool>("confirm", "정말 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.")) return;
await BlogService.DeleteAsync(post.Id);
await JS.InvokeVoidAsync("alert", "포스트가 삭제되었습니다.");
Navigation.NavigateTo("/taxbaik/admin/blog");
}
private class EditPostModel
{
public string Title { get; set; } = "";
public string Content { get; set; } = "";
public int? CategoryId { get; set; }
public string? Tags { get; set; }
public string? SeoTitle { get; set; }
public string? SeoDescription { get; set; }
public bool IsPublished { get; set; }
}
}