ccba017e3e
DB: - V007__CreateFaqs.sql: faqs 테이블 (question, answer, category, sort_order, is_active) + 기본 FAQ 4개 시드 Domain: - Faq 엔티티 - IFaqRepository (GetActiveAsync, GetAllAsync, CRUD) Infrastructure: - FaqRepository: sort_order 정렬, CRUD Application: - FaqService: Categories 상수, Validate (질문·답변 필수) Admin UI (Blazor): - FaqList.razor: 전체 목록, 활성/비활성 상태 칩, 삭제 확인 - FaqEdit.razor: 질문/답변/카테고리/순서/활성 토글 폼 - MainLayout: 홈페이지 그룹 하위에 FAQ 관리 메뉴 추가 홈페이지: - Index.cshtml 하드코딩 FAQ → ActiveFaqs DB 루프로 교체 - FAQ 없으면 섹션 전체 숨김 (빈 DB에 안전) - IndexModel: FaqService 주입, Task.WhenAll 병렬 로드 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
121 lines
4.5 KiB
Plaintext
121 lines
4.5 KiB
Plaintext
@page "/admin/faqs"
|
|
@attribute [Authorize]
|
|
@using TaxBaik.Application.Services
|
|
@using TaxBaik.Domain.Entities
|
|
@inject FaqService FaqService
|
|
@inject NavigationManager Navigation
|
|
@inject IDialogService DialogService
|
|
@inject ISnackbar Snackbar
|
|
|
|
<PageTitle>FAQ 관리</PageTitle>
|
|
|
|
<section class="admin-page-hero">
|
|
<div>
|
|
<MudText Typo="Typo.caption" Class="admin-eyebrow">홈페이지</MudText>
|
|
<MudText Typo="Typo.h4" Class="admin-page-title">FAQ 관리</MudText>
|
|
<MudText Typo="Typo.body2" Class="admin-page-subtitle">홈페이지 자주 묻는 질문을 등록하고 순서를 관리합니다.</MudText>
|
|
</div>
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary"
|
|
StartIcon="@Icons.Material.Filled.Add"
|
|
Href="/taxbaik/admin/faqs/create">
|
|
FAQ 등록
|
|
</MudButton>
|
|
</section>
|
|
|
|
<MudPaper Class="admin-surface" Elevation="0">
|
|
@if (faqs is null)
|
|
{
|
|
<MudProgressLinear Indeterminate="true" />
|
|
}
|
|
else if (!faqs.Any())
|
|
{
|
|
<div class="pa-6 text-center">
|
|
<MudIcon Icon="@Icons.Material.Filled.QuestionAnswer" Style="font-size:3rem; opacity:.3;" />
|
|
<MudText Class="mt-2 text-muted">등록된 FAQ가 없습니다.</MudText>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<MudSimpleTable Striped="true" Dense="true" Class="admin-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:60px;">순서</th>
|
|
<th>질문</th>
|
|
<th style="width:130px;">카테고리</th>
|
|
<th style="width:90px;">상태</th>
|
|
<th style="width:160px;"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var item in faqs)
|
|
{
|
|
<tr>
|
|
<td class="text-center">
|
|
<MudText Typo="Typo.body2">@item.SortOrder</MudText>
|
|
</td>
|
|
<td>
|
|
<MudText Typo="Typo.body2" Style="max-width:480px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">
|
|
@item.Question
|
|
</MudText>
|
|
</td>
|
|
<td>
|
|
@if (!string.IsNullOrEmpty(item.Category))
|
|
{
|
|
<MudChip Size="Size.Small" Color="Color.Default">@item.Category</MudChip>
|
|
}
|
|
</td>
|
|
<td>
|
|
@if (item.IsActive)
|
|
{
|
|
<MudChip Size="Size.Small" Color="Color.Success">노출 중</MudChip>
|
|
}
|
|
else
|
|
{
|
|
<MudChip Size="Size.Small" Color="Color.Default">비활성</MudChip>
|
|
}
|
|
</td>
|
|
<td>
|
|
<MudButtonGroup Size="Size.Small" Variant="Variant.Outlined">
|
|
<MudButton @onclick="@(() => Navigation.NavigateTo($"/taxbaik/admin/faqs/{item.Id}/edit"))">
|
|
수정
|
|
</MudButton>
|
|
<MudButton Color="Color.Error" @onclick="@(() => DeleteAsync(item))">
|
|
삭제
|
|
</MudButton>
|
|
</MudButtonGroup>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</MudSimpleTable>
|
|
<MudText Typo="Typo.caption" Class="pa-2 text-muted">
|
|
총 @(faqs.Count)개 · 노출 중 @(faqs.Count(f => f.IsActive))개
|
|
</MudText>
|
|
}
|
|
</MudPaper>
|
|
|
|
@code {
|
|
private List<Faq>? faqs;
|
|
|
|
protected override async Task OnInitializedAsync() => await LoadAsync();
|
|
|
|
private async Task LoadAsync()
|
|
{
|
|
faqs = (await FaqService.GetAllAsync()).ToList();
|
|
}
|
|
|
|
private async Task DeleteAsync(Faq item)
|
|
{
|
|
var confirmed = await DialogService.ShowMessageBox(
|
|
"FAQ 삭제",
|
|
$"'{item.Question}' 항목을 삭제하시겠습니까?",
|
|
yesText: "삭제", cancelText: "취소");
|
|
|
|
if (confirmed != true) return;
|
|
|
|
await FaqService.DeleteAsync(item.Id);
|
|
Snackbar.Add("FAQ가 삭제되었습니다.", Severity.Success);
|
|
await LoadAsync();
|
|
}
|
|
}
|