feat: CRM Phase 1-2 완성 + 시즌 시뮬레이터 + 개인정보처리방침/이용약관
TaxBaik CI/CD / build-and-deploy (push) Successful in 55s
TaxBaik Browser E2E / browser-e2e (push) Failing after 1m53s

- WBS-CRM-02: 상담 이력 (consultations 테이블 V008, ClientDetail.razor)
- WBS-CRM-03: 문의→고객 전환 (V009 client_id FK, InquiryDetail 고객등록 버튼)
- WBS-CRM-04: 신고 일정 캘린더 (tax_filings 테이블 V010, TaxFilingList.razor)
- WBS-CRM-05: 문의 상태 5단계 확장 (V011, InquiryStatus enum, InquiryList 탭)
- WBS-MKT-04: 시즌 시뮬레이터 어드민 페이지 (SeasonSimulator.razor)
- WBS-UX-04: 개인정보처리방침 /taxbaik/privacy, 이용약관 /taxbaik/terms
- Dashboard.razor 마감 임박 신고 위젯 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 00:01:16 +09:00
parent 9c96f15f86
commit 79492184d0
35 changed files with 1447 additions and 104 deletions
@@ -20,7 +20,9 @@ public class InquiryRepository(IDbConnectionFactory connectionFactory) : BaseRep
{
using var conn = Conn();
return await conn.QueryFirstOrDefaultAsync<Inquiry>(
"SELECT id, name, phone, email, service_type, message, status, ip_address, created_at FROM inquiries WHERE id = @Id",
@"SELECT id, name, phone, email, service_type, message, status, ip_address,
client_id, admin_memo, created_at, updated_at
FROM inquiries WHERE id = @Id",
new { Id = id });
}
@@ -31,7 +33,8 @@ public class InquiryRepository(IDbConnectionFactory connectionFactory) : BaseRep
var offset = (page - 1) * pageSize;
using var reader = await conn.QueryMultipleAsync(
@"SELECT id, name, phone, email, service_type, message, status, ip_address, created_at
@"SELECT id, name, phone, email, service_type, message, status, ip_address,
client_id, admin_memo, created_at, updated_at
FROM inquiries
WHERE @Status::text IS NULL OR status = @Status
ORDER BY created_at DESC
@@ -74,6 +77,24 @@ public class InquiryRepository(IDbConnectionFactory connectionFactory) : BaseRep
public async Task UpdateStatusAsync(int id, string status, CancellationToken cancellationToken = default)
{
using var conn = Conn();
await conn.ExecuteAsync("UPDATE inquiries SET status = @Status WHERE id = @Id", new { Id = id, Status = status });
await conn.ExecuteAsync(
"UPDATE inquiries SET status = @Status, updated_at = NOW() WHERE id = @Id",
new { Id = id, Status = status });
}
public async Task UpdateAdminMemoAsync(int id, string? adminMemo, CancellationToken cancellationToken = default)
{
using var conn = Conn();
await conn.ExecuteAsync(
"UPDATE inquiries SET admin_memo = @AdminMemo, updated_at = NOW() WHERE id = @Id",
new { Id = id, AdminMemo = adminMemo });
}
public async Task LinkClientAsync(int inquiryId, int clientId, CancellationToken cancellationToken = default)
{
using var conn = Conn();
await conn.ExecuteAsync(
"UPDATE inquiries SET client_id = @ClientId, updated_at = NOW() WHERE id = @Id",
new { Id = inquiryId, ClientId = clientId });
}
}