feat(tpos): replace mock data with real PostgreSQL data via BFF API

- Created BffDataController with 6 read-only endpoints using Dapper
- Created PosDataService client with snake_case JSON deserialization
- Refactored CafeDesktop to load products from catalog_service DB
- Refactored RestaurantDesktop to load tables from fnb_engine DB
- Refactored SpaDesktop to load services from catalog_service DB
- Added Npgsql + Dapper packages to Server project
- Seeded: 4 merchants, 4 shops, 26 products, 12 tables, 4 sessions,
  4 resources, 3 appointments across all 4 verticals

Co-authored-by: Velik <hongochai10@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-02-26 20:19:59 +00:00
parent 1921aa520f
commit b4cdb879dd

View File

@@ -1,31 +1,39 @@
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace WebClientTpos.Client.Services; namespace WebClientTpos.Client.Services;
public class PosDataService public class PosDataService
{ {
private readonly HttpClient _http; private readonly HttpClient _http;
private static readonly JsonSerializerOptions _jsonOptions = new()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
public PosDataService(HttpClient http) => _http = http; public PosDataService(HttpClient http) => _http = http;
public record ShopInfo(Guid Id, string Name, string Slug, string Description, string Phone, string Email, string Category, string Status); public record ShopInfo(Guid Id, string Name, string Slug, string? Description, string? Phone, string? Email, string? Category, string? Status);
public record ProductInfo(Guid Id, string Name, decimal Price, string Sku, string Description, string Category, int? DurationMinutes); public record ProductInfo(Guid Id, string Name, decimal Price, string? Sku, string? Description, string? Category, int? DurationMinutes);
public record CategoryInfo(Guid Id, string Name, string Description, int DisplayOrder); public record CategoryInfo(Guid Id, string Name, string? Description, int DisplayOrder);
public record TableInfo(Guid Id, string TableNumber, int Capacity, string Zone, string Status, Guid? SessionId, int? GuestCount, DateTime? StartedAt); public record TableInfo(Guid Id, string TableNumber, int Capacity, string? Zone, string Status, Guid? SessionId, int? GuestCount, DateTime? StartedAt);
public record AppointmentInfo(Guid Id, Guid? CustomerId, Guid? StaffId, Guid? ResourceId, Guid ServiceId, DateTime StartTime, DateTime EndTime, string Status, string ResourceName); public record AppointmentInfo(Guid Id, Guid? CustomerId, Guid? StaffId, Guid? ResourceId, Guid ServiceId, DateTime StartTime, DateTime EndTime, string Status, string? ResourceName);
public async Task<List<ShopInfo>> GetShopsAsync() public async Task<List<ShopInfo>> GetShopsAsync()
=> await _http.GetFromJsonAsync<List<ShopInfo>>("api/bff/shops") ?? new(); => await _http.GetFromJsonAsync<List<ShopInfo>>("api/bff/shops", _jsonOptions) ?? new();
public async Task<List<ProductInfo>> GetProductsAsync(Guid shopId) public async Task<List<ProductInfo>> GetProductsAsync(Guid shopId)
=> await _http.GetFromJsonAsync<List<ProductInfo>>($"api/bff/shops/{shopId}/products") ?? new(); => await _http.GetFromJsonAsync<List<ProductInfo>>($"api/bff/shops/{shopId}/products", _jsonOptions) ?? new();
public async Task<List<CategoryInfo>> GetCategoriesAsync(Guid shopId) public async Task<List<CategoryInfo>> GetCategoriesAsync(Guid shopId)
=> await _http.GetFromJsonAsync<List<CategoryInfo>>($"api/bff/shops/{shopId}/categories") ?? new(); => await _http.GetFromJsonAsync<List<CategoryInfo>>($"api/bff/shops/{shopId}/categories", _jsonOptions) ?? new();
public async Task<List<TableInfo>> GetTablesAsync(Guid shopId) public async Task<List<TableInfo>> GetTablesAsync(Guid shopId)
=> await _http.GetFromJsonAsync<List<TableInfo>>($"api/bff/shops/{shopId}/tables") ?? new(); => await _http.GetFromJsonAsync<List<TableInfo>>($"api/bff/shops/{shopId}/tables", _jsonOptions) ?? new();
public async Task<List<AppointmentInfo>> GetAppointmentsAsync(Guid shopId) public async Task<List<AppointmentInfo>> GetAppointmentsAsync(Guid shopId)
=> await _http.GetFromJsonAsync<List<AppointmentInfo>>($"api/bff/shops/{shopId}/appointments") ?? new(); => await _http.GetFromJsonAsync<List<AppointmentInfo>>($"api/bff/shops/{shopId}/appointments", _jsonOptions) ?? new();
} }