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>
134 lines
4.9 KiB
Plaintext
134 lines
4.9 KiB
Plaintext
@page "/admin/faqs/create"
|
|
@page "/admin/faqs/{Id:int}/edit"
|
|
@attribute [Authorize]
|
|
@using TaxBaik.Application.Services
|
|
@using TaxBaik.Domain.Entities
|
|
@inject FaqService FaqService
|
|
@inject NavigationManager Navigation
|
|
@inject ISnackbar Snackbar
|
|
|
|
<PageTitle>@(Id.HasValue ? "FAQ 수정" : "FAQ 등록")</PageTitle>
|
|
|
|
<section class="admin-page-hero">
|
|
<div>
|
|
<MudText Typo="Typo.caption" Class="admin-eyebrow">홈페이지</MudText>
|
|
<MudText Typo="Typo.h4" Class="admin-page-title">@(Id.HasValue ? "FAQ 수정" : "FAQ 등록")</MudText>
|
|
</div>
|
|
<MudButton Variant="Variant.Outlined" Href="/taxbaik/admin/faqs"
|
|
StartIcon="@Icons.Material.Filled.ArrowBack">목록으로</MudButton>
|
|
</section>
|
|
|
|
<MudPaper Class="admin-surface" Elevation="0" Style="max-width:720px;">
|
|
@if (isLoading)
|
|
{
|
|
<MudProgressLinear Indeterminate="true" />
|
|
}
|
|
else
|
|
{
|
|
<MudForm @ref="form" @bind-IsValid="isValid">
|
|
<MudGrid Spacing="3">
|
|
<MudItem xs="12">
|
|
<MudTextField @bind-Value="faq.Question"
|
|
Label="질문 *" Required="true"
|
|
RequiredError="질문을 입력하세요."
|
|
Counter="300" MaxLength="300"
|
|
Lines="2" AutoGrow="true"
|
|
Placeholder="예: 기장료가 얼마인지 미리 알 수 있나요?" />
|
|
</MudItem>
|
|
<MudItem xs="12">
|
|
<MudTextField @bind-Value="faq.Answer"
|
|
Label="답변 *" Required="true"
|
|
RequiredError="답변을 입력하세요."
|
|
Lines="5" AutoGrow="true"
|
|
Placeholder="방문자에게 보여질 답변을 입력하세요." />
|
|
</MudItem>
|
|
<MudItem xs="12" md="6">
|
|
<MudSelect @bind-Value="faq.Category" Label="카테고리" T="string" Clearable="true">
|
|
@foreach (var cat in FaqService.Categories)
|
|
{
|
|
<MudSelectItem Value="@cat">@cat</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="3">
|
|
<MudNumericField @bind-Value="faq.SortOrder"
|
|
Label="정렬 순서"
|
|
HelperText="작을수록 위에 노출"
|
|
Min="0" Max="9999" />
|
|
</MudItem>
|
|
<MudItem xs="12" md="3" Class="d-flex align-center">
|
|
<MudSwitch T="bool" @bind-Value="faq.IsActive" Color="Color.Success"
|
|
Label="@(faq.IsActive ? "노출 중" : "비활성")" />
|
|
</MudItem>
|
|
|
|
<MudItem xs="12" Class="d-flex gap-2 mt-2">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary"
|
|
StartIcon="@Icons.Material.Filled.Save"
|
|
OnClick="@SaveAsync" Disabled="@isSaving">
|
|
@(isSaving ? "저장 중..." : "저장")
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Outlined" Href="/taxbaik/admin/faqs">
|
|
취소
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudForm>
|
|
}
|
|
</MudPaper>
|
|
|
|
@code {
|
|
[Parameter] public int? Id { get; set; }
|
|
|
|
private MudForm form = null!;
|
|
private Faq faq = new() { SortOrder = 10, IsActive = true };
|
|
private bool isValid;
|
|
private bool isLoading = true;
|
|
private bool isSaving;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
if (Id.HasValue)
|
|
{
|
|
var existing = await FaqService.GetByIdAsync(Id.Value);
|
|
if (existing is null)
|
|
{
|
|
Snackbar.Add("FAQ를 찾을 수 없습니다.", Severity.Error);
|
|
Navigation.NavigateTo("/taxbaik/admin/faqs");
|
|
return;
|
|
}
|
|
faq = existing;
|
|
}
|
|
isLoading = false;
|
|
}
|
|
|
|
private async Task SaveAsync()
|
|
{
|
|
await form.Validate();
|
|
if (!isValid) return;
|
|
|
|
isSaving = true;
|
|
try
|
|
{
|
|
if (Id.HasValue)
|
|
{
|
|
await FaqService.UpdateAsync(faq);
|
|
Snackbar.Add("FAQ가 수정되었습니다.", Severity.Success);
|
|
}
|
|
else
|
|
{
|
|
await FaqService.CreateAsync(faq);
|
|
Snackbar.Add("FAQ가 등록되었습니다.", Severity.Success);
|
|
}
|
|
Navigation.NavigateTo("/taxbaik/admin/faqs");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
|
|
}
|
|
finally
|
|
{
|
|
isSaving = false;
|
|
}
|
|
}
|
|
}
|