개선: 배포 검증과 관리자 UX 안정화
This commit is contained in:
@@ -10,6 +10,13 @@
|
||||
Href="/taxbaik/admin/blog/create">새 포스트</MudButton>
|
||||
</div>
|
||||
|
||||
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
|
||||
<MudText Typo="Typo.subtitle1">@($"전체 포스트 {totalPosts}개")</MudText>
|
||||
<MudText Typo="Typo.body2">페이지 @currentPage / @totalPages</MudText>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
|
||||
<MudDataGrid Items="@posts" Striped="true" Hoverable="true" Loading="@isLoading">
|
||||
<Columns>
|
||||
<PropertyColumn Property="x => x.Title" Title="제목" />
|
||||
@@ -32,9 +39,18 @@
|
||||
</Columns>
|
||||
</MudDataGrid>
|
||||
|
||||
<MudStack Row="true" Justify="Justify.Center" Class="mt-4" Spacing="2">
|
||||
<MudButton Variant="Variant.Outlined" Disabled="@(currentPage <= 1 || isLoading)" @onclick="PreviousPage">이전</MudButton>
|
||||
<MudButton Variant="Variant.Outlined" Disabled="@(currentPage >= totalPages || isLoading)" @onclick="NextPage">다음</MudButton>
|
||||
</MudStack>
|
||||
|
||||
@code {
|
||||
private List<TaxBaik.Domain.Entities.BlogPost> posts = [];
|
||||
private bool isLoading = true;
|
||||
private int currentPage = 1;
|
||||
private int totalPages = 1;
|
||||
private int totalPosts = 0;
|
||||
private const int PageSize = 20;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -46,13 +62,38 @@
|
||||
isLoading = true;
|
||||
try
|
||||
{
|
||||
var items = await ApiClient.GetAsync<List<TaxBaik.Domain.Entities.BlogPost>>("blog/admin/all");
|
||||
posts = items ?? [];
|
||||
var result = await ApiClient.GetAsync<PagedBlogResponse>($"blog/admin?page={currentPage}&pageSize={PageSize}");
|
||||
posts = result?.Data ?? [];
|
||||
totalPosts = result?.Total ?? 0;
|
||||
totalPages = Math.Max(1, (int)Math.Ceiling(totalPosts / (double)PageSize));
|
||||
}
|
||||
catch
|
||||
{
|
||||
posts = [];
|
||||
totalPosts = 0;
|
||||
totalPages = 1;
|
||||
}
|
||||
catch { }
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
private async Task PreviousPage()
|
||||
{
|
||||
if (currentPage <= 1)
|
||||
return;
|
||||
|
||||
currentPage--;
|
||||
await LoadPosts();
|
||||
}
|
||||
|
||||
private async Task NextPage()
|
||||
{
|
||||
if (currentPage >= totalPages)
|
||||
return;
|
||||
|
||||
currentPage++;
|
||||
await LoadPosts();
|
||||
}
|
||||
|
||||
private async Task TogglePublish(TaxBaik.Domain.Entities.BlogPost post, bool isPublished)
|
||||
{
|
||||
var previous = post.IsPublished;
|
||||
@@ -86,4 +127,10 @@
|
||||
Snackbar.Add("포스트가 삭제되었습니다.", Severity.Success);
|
||||
await LoadPosts();
|
||||
}
|
||||
|
||||
private class PagedBlogResponse
|
||||
{
|
||||
public List<TaxBaik.Domain.Entities.BlogPost> Data { get; set; } = [];
|
||||
public int Total { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@page "/admin/dashboard"
|
||||
@using TaxBaik.Application.Services
|
||||
@inject InquiryService InquiryService
|
||||
@inject BlogService BlogService
|
||||
@inject AdminDashboardService DashboardService
|
||||
|
||||
<PageTitle>대시보드</PageTitle>
|
||||
|
||||
@@ -11,28 +10,28 @@
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudPaper Class="pa-4" Elevation="1">
|
||||
<MudText Typo="Typo.subtitle2">이번달 문의</MudText>
|
||||
<MudText Typo="Typo.h4">@thisMonthInquiries</MudText>
|
||||
<MudText Typo="Typo.h4">@summary.ThisMonthInquiries</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudPaper Class="pa-4" Elevation="1">
|
||||
<MudText Typo="Typo.subtitle2">신규 문의</MudText>
|
||||
<MudText Typo="Typo.h4">@newInquiries</MudText>
|
||||
<MudText Typo="Typo.h4">@summary.NewInquiries</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudPaper Class="pa-4" Elevation="1">
|
||||
<MudText Typo="Typo.subtitle2">전체 포스트</MudText>
|
||||
<MudText Typo="Typo.h4">@totalPosts</MudText>
|
||||
<MudText Typo="Typo.h4">@summary.TotalPosts</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudPaper Class="pa-4" Elevation="1">
|
||||
<MudText Typo="Typo.subtitle2">발행된 포스트</MudText>
|
||||
<MudText Typo="Typo.h4">@publishedPosts</MudText>
|
||||
<MudText Typo="Typo.h4">@summary.PublishedPosts</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
@@ -50,7 +49,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var inquiry in recentInquiries)
|
||||
@foreach (var inquiry in summary.RecentInquiries)
|
||||
{
|
||||
<tr>
|
||||
<td>@inquiry.Name</td>
|
||||
@@ -70,22 +69,10 @@
|
||||
</MudPaper>
|
||||
|
||||
@code {
|
||||
private int thisMonthInquiries = 0;
|
||||
private int newInquiries = 0;
|
||||
private int totalPosts = 0;
|
||||
private int publishedPosts = 0;
|
||||
private List<Domain.Entities.Inquiry> recentInquiries = [];
|
||||
private AdminDashboardSummary summary = new(0, 0, 0, 0, []);
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
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");
|
||||
var stats = await BlogService.GetStatsAsync();
|
||||
totalPosts = stats.TotalPosts;
|
||||
publishedPosts = stats.PublishedPosts;
|
||||
summary = await DashboardService.GetSummaryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
await ApiClient.SetAuthToken(response.Token);
|
||||
await AuthStateProvider.LoginAsync(response.Token);
|
||||
NavigationManager.NavigateTo("/taxbaik/admin/dashboard", forceLoad: false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@page "/admin/settings"
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@using System.Collections.Generic
|
||||
@using TaxBaik.Web.Services
|
||||
@using TaxBaik.Domain.Interfaces
|
||||
@inject IApiClient ApiClient
|
||||
@@ -58,12 +59,65 @@
|
||||
private string newPassword = "";
|
||||
private string confirmNewPassword = "";
|
||||
private bool isChangingPassword;
|
||||
private bool isLoadingSettings;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadSettingsAsync();
|
||||
}
|
||||
|
||||
private async Task LoadSettingsAsync()
|
||||
{
|
||||
isLoadingSettings = true;
|
||||
|
||||
try
|
||||
{
|
||||
var settings = await ApiClient.GetAsync<Dictionary<string, string>>("site-settings");
|
||||
if (settings is null || settings.Count == 0)
|
||||
return;
|
||||
|
||||
if (settings.TryGetValue("PhoneNumber", out var loadedPhone) && !string.IsNullOrWhiteSpace(loadedPhone))
|
||||
phone = loadedPhone;
|
||||
|
||||
if (settings.TryGetValue("EmailAddress", out var loadedEmail) && !string.IsNullOrWhiteSpace(loadedEmail))
|
||||
email = loadedEmail;
|
||||
|
||||
if (settings.TryGetValue("KakaoChannelUrl", out var loadedKakao) && !string.IsNullOrWhiteSpace(loadedKakao))
|
||||
kakaoUrl = loadedKakao;
|
||||
|
||||
if (settings.TryGetValue("InstagramUrl", out var loadedInstagram) && !string.IsNullOrWhiteSpace(loadedInstagram))
|
||||
instagramUrl = loadedInstagram;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Snackbar.Add("사이트 설정을 불러오지 못했습니다.", Severity.Warning);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isLoadingSettings = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveSettings()
|
||||
{
|
||||
// TODO: Save settings to database
|
||||
Snackbar.Add("설정 저장 기능은 아직 구현되지 않았습니다.", Severity.Info);
|
||||
await Task.CompletedTask;
|
||||
if (isLoadingSettings)
|
||||
return;
|
||||
|
||||
var response = await ApiClient.PutAsync<SaveSettingsResponse>("site-settings", new
|
||||
{
|
||||
Phone = phone,
|
||||
Email = email,
|
||||
KakaoUrl = kakaoUrl,
|
||||
InstagramUrl = instagramUrl
|
||||
});
|
||||
|
||||
if (response?.Message is null)
|
||||
{
|
||||
Snackbar.Add("설정 저장에 실패했습니다.", Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
Snackbar.Add(response.Message, Severity.Success);
|
||||
}
|
||||
|
||||
private async Task ChangePassword()
|
||||
@@ -118,4 +172,9 @@
|
||||
{
|
||||
public string Message { get; set; } = "";
|
||||
}
|
||||
|
||||
private class SaveSettingsResponse
|
||||
{
|
||||
public string Message { get; set; } = "";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user