feat: WBS-UX-03 FAQ 관리 기능 구현 — 어드민 CRUD + 홈페이지 DB 연동
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>
This commit is contained in:
@@ -17,6 +17,7 @@ public static class DependencyInjection
|
||||
services.AddScoped<ISiteSettingRepository, SiteSettingRepository>();
|
||||
services.AddScoped<IAnnouncementRepository, AnnouncementRepository>();
|
||||
services.AddScoped<IClientRepository, ClientRepository>();
|
||||
services.AddScoped<IFaqRepository, FaqRepository>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace TaxBaik.Infrastructure.Repositories;
|
||||
|
||||
using Dapper;
|
||||
using TaxBaik.Domain.Entities;
|
||||
using TaxBaik.Domain.Interfaces;
|
||||
|
||||
public class FaqRepository(IDbConnectionFactory connectionFactory) : BaseRepository(connectionFactory), IFaqRepository
|
||||
{
|
||||
private const string SelectColumns =
|
||||
"id, question, answer, category, sort_order, is_active, created_at, updated_at";
|
||||
|
||||
public async Task<IEnumerable<Faq>> GetActiveAsync(CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
return await conn.QueryAsync<Faq>(
|
||||
$"SELECT {SelectColumns} FROM faqs WHERE is_active = TRUE ORDER BY sort_order, id");
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Faq>> GetAllAsync(CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
return await conn.QueryAsync<Faq>(
|
||||
$"SELECT {SelectColumns} FROM faqs ORDER BY sort_order, id");
|
||||
}
|
||||
|
||||
public async Task<Faq?> GetByIdAsync(int id, CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
return await conn.QueryFirstOrDefaultAsync<Faq>(
|
||||
$"SELECT {SelectColumns} FROM faqs WHERE id = @Id",
|
||||
new { Id = id });
|
||||
}
|
||||
|
||||
public async Task<int> CreateAsync(Faq faq, CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
return await conn.QueryFirstAsync<int>(
|
||||
@"INSERT INTO faqs (question, answer, category, sort_order, is_active, created_at, updated_at)
|
||||
VALUES (@Question, @Answer, @Category, @SortOrder, @IsActive, NOW(), NOW())
|
||||
RETURNING id",
|
||||
faq);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Faq faq, CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
await conn.ExecuteAsync(
|
||||
@"UPDATE faqs
|
||||
SET question = @Question, answer = @Answer, category = @Category,
|
||||
sort_order = @SortOrder, is_active = @IsActive, updated_at = NOW()
|
||||
WHERE id = @Id",
|
||||
faq);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id, CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
await conn.ExecuteAsync("DELETE FROM faqs WHERE id = @Id", new { Id = id });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user