feat: harden auth ops and deployment baseline
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
@page "/admin/blog/create"
|
||||
@using TaxBaik.Application.DTOs
|
||||
@using TaxBaik.Application.Services
|
||||
@using TaxBaik.Domain.Interfaces
|
||||
@attribute [Authorize]
|
||||
@inject BlogService BlogService
|
||||
@inject ICategoryRepository CategoryRepository
|
||||
@inject NavigationManager Navigation
|
||||
@inject Snackbar Snackbar
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<PageTitle>새 포스트 작성</PageTitle>
|
||||
|
||||
@@ -49,7 +50,7 @@
|
||||
</MudPaper>
|
||||
|
||||
@code {
|
||||
private MudForm form;
|
||||
private MudForm? form;
|
||||
private List<Domain.Entities.Category> categories = [];
|
||||
private CreatePostModel model = new();
|
||||
|
||||
@@ -60,18 +61,43 @@
|
||||
|
||||
private async Task SavePost()
|
||||
{
|
||||
// TODO: Implement BlogService.CreateAsync
|
||||
Navigation.NavigateTo("/taxbaik/admin/blog");
|
||||
if (form == null)
|
||||
return;
|
||||
|
||||
await form.Validate();
|
||||
if (!form.IsValid)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await BlogService.CreateAsync(new CreateBlogPostDto
|
||||
{
|
||||
Title = model.Title,
|
||||
Content = model.Content,
|
||||
CategoryId = model.CategoryId,
|
||||
Tags = model.Tags,
|
||||
SeoTitle = model.SeoTitle,
|
||||
SeoDescription = model.SeoDescription,
|
||||
IsPublished = model.IsPublished
|
||||
});
|
||||
|
||||
Snackbar.Add("포스트가 저장되었습니다.", Severity.Success);
|
||||
Navigation.NavigateTo("/taxbaik/admin/blog");
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
Snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private class CreatePostModel
|
||||
{
|
||||
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 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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@attribute [Authorize]
|
||||
@inject IApiClient ApiClient
|
||||
@inject DialogService DialogService
|
||||
@inject Snackbar Snackbar
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<PageTitle>블로그 관리</PageTitle>
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
<PropertyColumn Property="x => x.Title" Title="제목" />
|
||||
<PropertyColumn Property="x => x.IsPublished" Title="발행">
|
||||
<CellTemplate Context="cell">
|
||||
<MudCheckBox @bind-Checked="@cell.Item.IsPublished" />
|
||||
<MudCheckBox T="bool" Value="@cell.Item.IsPublished"
|
||||
ValueChanged="@(async (bool value) => await TogglePublish(cell.Item, value))" />
|
||||
</CellTemplate>
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="x => x.ViewCount" Title="조회수" />
|
||||
@@ -54,14 +55,37 @@
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
private async Task TogglePublish(int postId, bool isPublished)
|
||||
private async Task TogglePublish(TaxBaik.Domain.Entities.BlogPost post, bool isPublished)
|
||||
{
|
||||
// Publish status update via API
|
||||
var previous = post.IsPublished;
|
||||
post.IsPublished = isPublished;
|
||||
var result = await ApiClient.PutAsync<TaxBaik.Domain.Entities.BlogPost>($"blog/{post.Id}", new
|
||||
{
|
||||
post.Title,
|
||||
post.Content,
|
||||
post.CategoryId,
|
||||
post.Tags,
|
||||
post.SeoTitle,
|
||||
post.SeoDescription,
|
||||
post.ThumbnailUrl,
|
||||
IsPublished = isPublished,
|
||||
post.AuthorId
|
||||
});
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
post.IsPublished = previous;
|
||||
Snackbar.Add("발행 상태 변경에 실패했습니다.", Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
Snackbar.Add("발행 상태가 변경되었습니다.", Severity.Success);
|
||||
}
|
||||
|
||||
private async Task DeletePost(int postId)
|
||||
{
|
||||
await ApiClient.DeleteAsync($"blog/{postId}");
|
||||
Snackbar.Add("포스트가 삭제되었습니다.", Severity.Success);
|
||||
await LoadPosts();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
@page "/admin/dashboard"
|
||||
@using TaxBaik.Application.Services
|
||||
@using TaxBaik.Domain.Interfaces
|
||||
@attribute [Authorize]
|
||||
@inject IInquiryRepository InquiryRepository
|
||||
@inject InquiryService InquiryService
|
||||
@inject BlogService BlogService
|
||||
|
||||
<PageTitle>대시보드</PageTitle>
|
||||
@@ -80,13 +79,14 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var (inquiries, total) = await InquiryRepository.GetPagedAsync(1, 100);
|
||||
var (inquiries, _) = await InquiryService.GetPagedAsync(1, 100);
|
||||
recentInquiries = inquiries.OrderByDescending(x => x.CreatedAt).Take(5).ToList();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
thisMonthInquiries = inquiries.Count(x => x.CreatedAt.Year == now.Year && x.CreatedAt.Month == now.Month);
|
||||
newInquiries = inquiries.Count(x => x.Status == "new");
|
||||
totalPosts = 0; // TODO: get from blog service
|
||||
publishedPosts = 0; // TODO: get from blog service
|
||||
var stats = await BlogService.GetStatsAsync();
|
||||
totalPosts = stats.TotalPosts;
|
||||
publishedPosts = stats.PublishedPosts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
@page "/admin/inquiries/{InquiryId:int}"
|
||||
@using TaxBaik.Domain.Interfaces
|
||||
@using TaxBaik.Application.Services
|
||||
@attribute [Authorize]
|
||||
@inject IInquiryRepository InquiryRepository
|
||||
@inject InquiryService InquiryService
|
||||
@inject NavigationManager Navigation
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<PageTitle>문의 상세</PageTitle>
|
||||
|
||||
@@ -36,7 +37,7 @@
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudText Typo="Typo.subtitle1">상태</MudText>
|
||||
<MudSelect @bind-Value="inquiry.Status" Label="상태 변경">
|
||||
<MudSelect T="string" Value="inquiry.Status" ValueChanged="@((string status) => OnStatusChanged(status))" Label="상태 변경">
|
||||
<MudSelectItem Value="@("new")">신규</MudSelectItem>
|
||||
<MudSelectItem Value="@("contacted")">연락함</MudSelectItem>
|
||||
<MudSelectItem Value="@("completed")">완료</MudSelectItem>
|
||||
@@ -54,11 +55,27 @@ else
|
||||
[Parameter]
|
||||
public int InquiryId { get; set; }
|
||||
|
||||
private Domain.Entities.Inquiry inquiry;
|
||||
private Domain.Entities.Inquiry? inquiry;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var (inquiries, _) = await InquiryRepository.GetPagedAsync(1, 1000);
|
||||
inquiry = inquiries.FirstOrDefault(x => x.Id == InquiryId);
|
||||
inquiry = await InquiryService.GetByIdAsync(InquiryId);
|
||||
}
|
||||
|
||||
private async Task OnStatusChanged(string status)
|
||||
{
|
||||
if (inquiry == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await InquiryService.UpdateStatusAsync(inquiry.Id, status);
|
||||
inquiry.Status = status;
|
||||
Snackbar.Add("상태가 변경되었습니다.", Severity.Success);
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
Snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user