Files
taxbaik/TaxBaik.Infrastructure/Repositories/InquiryRepository.cs
T
kjh2064 c65742a0c7
TaxBaik CI/CD / build-and-deploy (push) Successful in 48s
feat: implement admin inquiry create/edit/delete functionality
Core Components:
- Create reusable InquiryForm.razor component following SOLID principles
- Implement InquiryCreate.razor for registering new inquiries (offline, phone)
- Implement InquiryEdit.razor for modifying existing inquiries with delete
- Add DeleteAsync method to InquiryRepository and InquiryService
- Update InquiryList with 'Create' button and Edit link in table

Architecture:
- InquiryForm: Encapsulates form logic, can be reused for create/edit
- Service Layer: All operations go through InquiryService for cache invalidation
- Repository Pattern: Database operations isolated in InquiryRepository
- UI Consistency: Both pages follow admin-page-hero pattern

Features:
- Admin can create inquiries from phone/offline consultations
- Admin can modify inquiry details (name, phone, email, message, status, memo)
- Admin can delete inquiries with confirmation dialog
- All operations update dashboard cache
- Status validation and error handling throughout

Testing:
- Updated FakeInquiryRepository in tests to implement DeleteAsync
2026-06-28 16:45:29 +09:00

129 lines
5.1 KiB
C#

namespace TaxBaik.Infrastructure.Repositories;
using Dapper;
using TaxBaik.Domain.Entities;
using TaxBaik.Domain.Interfaces;
public class InquiryRepository(IDbConnectionFactory connectionFactory) : BaseRepository(connectionFactory), IInquiryRepository
{
public async Task<int> CreateAsync(Inquiry inquiry, CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.QueryFirstAsync<int>(
@"INSERT INTO inquiries (name, phone, email, service_type, message, status, ip_address, created_at)
VALUES (@Name, @Phone, @Email, @ServiceType, @Message, @Status, @IpAddress, NOW())
RETURNING id",
inquiry);
}
public async Task<Inquiry?> GetByIdAsync(int id, CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.QueryFirstOrDefaultAsync<Inquiry>(
@"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 });
}
public async Task<(IEnumerable<Inquiry> Items, int Total)> GetPagedAsync(
int page, int pageSize, string? status = null, CancellationToken cancellationToken = default)
{
using var conn = Conn();
var offset = (page - 1) * pageSize;
using var reader = await conn.QueryMultipleAsync(
@"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
LIMIT @PageSize OFFSET @Offset;
SELECT COUNT(*) FROM inquiries
WHERE @Status::text IS NULL OR status = @Status;",
new { Status = status, PageSize = pageSize, Offset = offset });
var items = (await reader.ReadAsync<Inquiry>()).ToList();
var total = await reader.ReadFirstAsync<int>();
return (items, total);
}
public async Task<int> CountAsync(CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.ExecuteScalarAsync<int>("SELECT COUNT(*) FROM inquiries");
}
public async Task<int> CountThisMonthAsync(CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.ExecuteScalarAsync<int>(
@"SELECT COUNT(*)
FROM inquiries
WHERE created_at >= date_trunc('month', NOW())
AND created_at < date_trunc('month', NOW()) + INTERVAL '1 month'");
}
public async Task<int> CountByStatusAsync(string status, CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.ExecuteScalarAsync<int>(
"SELECT COUNT(*) FROM inquiries WHERE status = @Status",
new { Status = status });
}
public async Task<int> CountByDateRangeAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.ExecuteScalarAsync<int>(
@"SELECT COUNT(*)
FROM inquiries
WHERE created_at >= @StartDate AND created_at <= @EndDate",
new { StartDate = startDate, EndDate = endDate });
}
public async Task<int> CountByStatusAndDateAsync(string status, DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.ExecuteScalarAsync<int>(
@"SELECT COUNT(*)
FROM inquiries
WHERE status = @Status
AND created_at >= @StartDate
AND created_at <= @EndDate",
new { Status = status, StartDate = startDate, EndDate = endDate });
}
public async Task UpdateStatusAsync(int id, string status, CancellationToken cancellationToken = default)
{
using var conn = Conn();
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 });
}
public async Task DeleteAsync(int id, CancellationToken cancellationToken = default)
{
using var conn = Conn();
await conn.ExecuteAsync("DELETE FROM inquiries WHERE id = @Id", new { Id = id });
}
}