refactor: Phase 7-2 - Inquiry page API-First (partial)
TaxBaik CI/CD / build-and-deploy (push) Successful in 48s

**Implementation:**
- InquiryBrowserClient: HTTP API client interface
  * GetPagedAsync(page, pageSize): Fetch inquiries
  * GetByIdAsync(id): Fetch single inquiry
  * UpdateStatusAsync(id, status): Change status

- Program.cs: Register InquiryBrowserClient
  * AddHttpClient with TokenRefreshHandler

- InquiryTable.razor: Refactored
  * Before: @inject InquiryService (direct service call)
  * After: @inject IInquiryBrowserClient (API call)
  * Status labels: Use InquiryStatusMapper
  * API calls via client instead of service

**Status:**
- Blog page:  Already API-First (ApiClient)
- Inquiry table:  API-First (IInquiryBrowserClient)
- Inquiry detail:  Pending (needs additional API endpoints)
  * UpdateAdminMemoAsync
  * LinkClientAsync
  * ConvertToClientAsync

**SOLID Applied:**
✓ S (Single Responsibility): InquiryBrowserClient handles only Inquiry API calls
✓ D (Dependency Inversion): Blazor depends on IInquiryBrowserClient abstraction
✓ O (Open/Closed): Client can be extended without Blazor changes

Next: Implement remaining API endpoints for InquiryDetail refactoring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 10:56:06 +09:00
parent 08e9e07458
commit 8149680487
3 changed files with 100 additions and 12 deletions
@@ -1,5 +1,5 @@
@using TaxBaik.Application.Services
@inject InquiryService InquiryService
@using TaxBaik.Web.Services
@inject IInquiryBrowserClient InquiryClient
<MudSimpleTable Striped="true" Dense="true" Class="admin-table mt-4">
<thead>
@@ -45,7 +45,7 @@
protected override async Task OnInitializedAsync()
{
var (items, _) = await InquiryService.GetPagedAsync(1, 100);
var (items, _) = await InquiryClient.GetPagedAsync(1, 100);
inquiries = items.ToList();
FilterInquiries();
}
@@ -69,18 +69,14 @@
private static Color GetStatusColor(string status) => status switch
{
"new" => Color.Warning,
"contacted" => Color.Info,
"completed" => Color.Success,
"consulting" => Color.Info,
"contracted" => Color.Success,
"rejected" => Color.Error,
"closed" => Color.Dark,
_ => Color.Default
};
private static string GetStatusLabel(string status) => status switch
{
"new" => "신규",
"contacted" => "연락함",
"completed" => "완료",
_ => status
};
private static string GetStatusLabel(string status) => InquiryStatusMapper.Labels.GetValueOrDefault(status, status);
protected override async Task OnParametersSetAsync()
{
+2
View File
@@ -75,6 +75,8 @@ builder.Services.AddHttpClient<IAdminDashboardClient, AdminDashboardClient>(clie
client.BaseAddress = new Uri("http://localhost:5001/taxbaik/api/");
})
.AddHttpMessageHandler<TokenRefreshHandler>();
builder.Services.AddHttpClient<IInquiryBrowserClient, InquiryBrowserClient>()
.AddHttpMessageHandler<TokenRefreshHandler>();
// UI & 캐시
builder.Services.AddMudServices();
@@ -0,0 +1,90 @@
namespace TaxBaik.Web.Services;
using System.Net.Http.Json;
using TaxBaik.Domain.Entities;
/// <summary>
/// Inquiry API Client for Admin Blazor
/// SOLID: Single Responsibility - Inquiry API calls only
/// Dependency Inversion - abstraction via interface
/// </summary>
public interface IInquiryBrowserClient
{
Task<(IEnumerable<Inquiry> Items, int Total)> GetPagedAsync(int page = 1, int pageSize = 20, CancellationToken ct = default);
Task<Inquiry?> GetByIdAsync(int id, CancellationToken ct = default);
Task<bool> UpdateStatusAsync(int id, string status, CancellationToken ct = default);
}
public class InquiryBrowserClient : IInquiryBrowserClient
{
private readonly HttpClient _http;
private readonly ILogger<InquiryBrowserClient> _logger;
public InquiryBrowserClient(HttpClient http, ILogger<InquiryBrowserClient> logger)
{
_http = http;
_logger = logger;
}
public async Task<(IEnumerable<Inquiry> Items, int Total)> GetPagedAsync(
int page = 1, int pageSize = 20, CancellationToken ct = default)
{
try
{
var result = await _http.GetFromJsonAsync<InquiryPagedResponse>(
$"http://localhost:5001/taxbaik/api/inquiry?page={page}&pageSize={pageSize}",
cancellationToken: ct);
return result != null
? (result.Data, result.Total)
: ([], 0);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to fetch inquiries");
throw;
}
}
public async Task<Inquiry?> GetByIdAsync(int id, CancellationToken ct = default)
{
try
{
return await _http.GetFromJsonAsync<Inquiry>(
$"http://localhost:5001/taxbaik/api/inquiry/{id}",
cancellationToken: ct);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to fetch inquiry {InquiryId}", id);
throw;
}
}
public async Task<bool> UpdateStatusAsync(int id, string status, CancellationToken ct = default)
{
try
{
var request = new { status };
var response = await _http.PutAsJsonAsync(
$"http://localhost:5001/taxbaik/api/inquiry/{id}/status",
request,
cancellationToken: ct);
return response.IsSuccessStatusCode;
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to update inquiry {InquiryId} status", id);
throw;
}
}
private class InquiryPagedResponse
{
public List<Inquiry> Data { get; set; } = [];
public int Total { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
}
}