From 12be9737d94eb200d8afe85ea16c36351c83438c Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Sat, 28 Feb 2026 04:23:11 +0700 Subject: [PATCH] feat(web-client-tpos): replace hardcoded store pages with real API data - Rewrite StoreList.razor with real data from PosDataService - Rewrite StoreDetail.razor with real shop data from BFF - Rewrite StoreSettings.razor with editable form bound to real data - Add GetShopByIdAsync to PosDataService and BFF endpoint - Add UpdateShopAsync to MerchantApiService - Add ShopUpdateDto to MerchantDtos - Fix BFF DB connection: configurable via env vars (BFF_DB_HOST) - Add BFF_DB env vars to docker-compose.yml --- .../Pages/Admin/Store/StoreDetail.razor | 356 ++++++++------ .../Pages/Admin/Store/StoreList.razor | 418 ++++++++-------- .../Pages/Admin/Store/StoreSettings.razor | 465 +++++++++++------- .../Services/MerchantApiService.cs | 23 + .../Services/PosDataService.cs | 3 + .../Controllers/BffDataController.cs | 29 +- .../WebClientTpos.Shared/DTOs/MerchantDtos.cs | 13 + deployments/local/docker-compose.yml | 6 + 8 files changed, 756 insertions(+), 557 deletions(-) diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreDetail.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreDetail.razor index e2260fc0..6545cda8 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreDetail.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreDetail.razor @@ -1,14 +1,15 @@ @page "/admin/stores/{Id}" @layout AdminLayout @inherits AdminBase +@inject PosDataService DataService +@using WebClientTpos.Client.Services @* - EN: Store detail — overview with KPIs, revenue chart, recent orders, and staff. - VI: Chi tiết cửa hàng — tổng quan KPI, biểu đồ doanh thu, đơn gần đây, nhân viên. - Design: pencil-design/src/pages/tPOS/admin/store-detail.pen + EN: Store detail — overview with KPIs (real data from API). + VI: Chi tiết cửa hàng — tổng quan KPI (dữ liệu thực từ API). *@ -@_storeName — GoodGo Admin +@(_shop?.Name ?? "Cửa hàng") — GoodGo Admin @* ═══ TOP BAR ═══ *@
@@ -17,15 +18,18 @@
-

@_storeName

-

@_storeType • @_storeAddress

+

@(_shop?.Name ?? "Đang tải...")

+

@(_shop?.Category ?? "—") • @(_shop?.Slug ?? "—")

-
- - Đang mở -
+ @if (_shop != null) + { +
+ + @GetStatusLabel(_shop.Status) +
+ }
- -
-
-
- + } + else + { + @* ── Store Info Card ── *@ +
+
+
+
-
-
132K
-
Giá trị TB / đơn
-
- -
-
-
- -
-
-
4.7
-
Đánh giá TB
-
-
- - @* ── BOTTOM: Revenue Chart + Right Column ── *@ -
- - @* LEFT: Revenue Chart Placeholder *@ -
-
-

- - Doanh thu 7 ngày gần nhất -

-
- - - -
-
-
- @* Chart placeholder — bar chart *@ -
- @foreach (var bar in _chartData) +
+
@_shop.Name
+
@_shop.Slug • @(_shop.Category ?? "—")
+ @if (!string.IsNullOrEmpty(_shop.Description)) { -
-
- @bar.Label +
@_shop.Description
+ } +
+
+ @if (!string.IsNullOrEmpty(_shop.Phone)) + { +
+ @_shop.Phone +
+ } + @if (!string.IsNullOrEmpty(_shop.Email)) + { +
+ @_shop.Email
}
- @* RIGHT COLUMN *@ -
+ @* ── KPI ROW (placeholder — chưa có dữ liệu order/revenue) ── *@ +
+
+
+
+ +
+
+
--
+
Doanh thu tháng
+
- @* Recent Orders *@ -
+
+
+
+ +
+
+
--
+
Đơn hàng tháng
+
+ +
+
+
+ +
+
+
--
+
Giá trị TB / đơn
+
+ +
+
+
+ +
+
+
--
+
Đánh giá TB
+
+
+ + @* ── BOTTOM: Chart + Right Column ── *@ +
+ + @* LEFT: Revenue Chart Placeholder *@ +

- - Đơn gần nhất + + Doanh thu 7 ngày gần nhất

- Tất cả → +
+ + +
-
-
-
- -
-
- #2847 - 185K -
-
Hoàn thành • 2 phút trước
-
-
-
- -
-
- #2846 - 92K -
-
Đang pha chế • 5 phút trước
-
-
-
- -
-
- #2845 - 240K -
-
Hoàn thành • 12 phút trước
-
-
+
+
+ +

Chưa có dữ liệu doanh thu

+

Dữ liệu sẽ hiển thị khi có đơn hàng

- @* Staff On Shift *@ -
-
-

- - Nhân viên đang ca (@_staffOnShift.Count) -

+ @* RIGHT COLUMN *@ +
+ + @* Recent Orders Placeholder *@ +
+
+

+ + Đơn gần nhất +

+
+
+ +

Chưa có đơn hàng nào

+
-
-
- @foreach (var staff in _staffOnShift) + + @* Shop Quick Info *@ +
+
+

+ + Thông tin +

+
+
+
+ Trạng thái + @GetStatusLabel(_shop.Status) +
+
+ Ngành hàng + @(_shop.Category ?? "—") +
+
+ Slug + @(_shop.Slug) +
+ @if (!string.IsNullOrEmpty(_shop.Phone)) { -
-
@staff.Initials
-
- @staff.Name - @staff.Role • từ @staff.StartTime -
+
+ Điện thoại + @_shop.Phone +
+ } + @if (!string.IsNullOrEmpty(_shop.Email)) + { +
+ Email + @_shop.Email
}
-
+ }
@code { - [Parameter] public string Id { get; set; } = "1"; + [Parameter] public string Id { get; set; } = ""; - private string _storeName = "Coffee House Q1"; - private string _storeType = "Café"; - private string _storeAddress = "123 Nguyễn Huệ, Q1, TP.HCM"; + private PosDataService.ShopInfo? _shop; - private record ChartBar(string Label, int Percent); - private readonly List _chartData = new() + protected override async Task OnInitializedAsync() { - new("T2", 65), new("T3", 78), new("T4", 55), new("T5", 82), - new("T6", 90), new("T7", 95), new("CN", 72) + IsLoading = true; + try + { + if (Guid.TryParse(Id, out var shopId)) + { + _shop = await DataService.GetShopByIdAsync(shopId); + } + } + catch + { + _shop = null; + } + finally + { + IsLoading = false; + } + } + + private static string GetStatusBadgeClass(string? status) => status?.ToLowerInvariant() switch + { + "published" or "active" => "online", + "draft" or "setup" => "setup", + "inactive" or "paused" => "paused", + _ => "setup" }; - private record StaffInfo(string Name, string Initials, string Role, string StartTime); - private readonly List _staffOnShift = new() + private static string GetStatusLabel(string? status) => status?.ToLowerInvariant() switch { - new("Trần Minh", "TM", "Barista", "07:00"), - new("Lê Thảo", "LT", "Thu ngân", "07:30"), - new("Nguyễn Hà", "NH", "Phục vụ", "08:00"), + "published" or "active" => "Đang mở", + "draft" or "setup" => "Thiết lập", + "inactive" or "paused" => "Tạm dừng", + "closed" => "Đã đóng", + _ => status ?? "—" + }; + + private static string GetShopIcon(string? category) => category?.ToLowerInvariant() switch + { + "foodbeverage" or "café" or "cafe" or "coffee" => "coffee", + "restaurant" or "nhà hàng" => "utensils", + "entertainment" or "karaoke" => "mic", + "beauty" or "spa" => "sparkles", + "retail" => "shopping-bag", + _ => "store" }; } diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreList.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreList.razor index f9b2086e..5caa285f 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreList.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreList.razor @@ -1,11 +1,12 @@ @page "/admin/stores" @layout AdminLayout @inherits AdminBase +@inject PosDataService DataService +@using WebClientTpos.Client.Services @* - EN: Store list — all stores with tabs, search, filter. - VI: Danh sách cửa hàng — tất cả cửa hàng với tabs, tìm kiếm, lọc. - Design: pencil-design/src/pages/tPOS/admin/store-list.pen + EN: Store list — all stores with tabs, search, filter (real data from API). + VI: Danh sách cửa hàng — tất cả cửa hàng với tabs, tìm kiếm, lọc (dữ liệu thực từ API). *@ Quản lý cửa hàng — GoodGo Admin @@ -17,13 +18,9 @@

Tạo và quản lý tất cả cửa hàng trong hệ thống

-
@* ═══ STORE LIST ═══ *@
- @* Store 1: Coffee House Q1 — Active *@ -
-
-
- -
-
-
- Coffee House Q1 -
- - Hoạt động + @if (IsLoading) + { +
+
+

Đang tải danh sách cửa hàng...

+
+ } + else if (!FilteredShops.Any()) + { +
+ +

+ @if (_shops.Count == 0) + { + @:Chưa có cửa hàng nào + } + else + { + @:Không tìm thấy cửa hàng + } +

+

+ @if (_shops.Count == 0) + { + @:Bắt đầu bằng việc tạo cửa hàng đầu tiên. + } + else + { + @:Thử thay đổi bộ lọc hoặc từ khóa tìm kiếm. + } +

+ @if (_shops.Count == 0) + { + + + Tạo cửa hàng ngay + + } +
+ } + else + { + @foreach (var shop in FilteredShops) + { + var isPaused = IsPaused(shop.Status); +
+
+
+ +
+
+
+ @shop.Name +
+ + @GetStatusLabel(shop.Status) +
+
+ @(shop.Category ?? "—") • @(shop.Slug)
- Café • 123 Nguyễn Huệ, Q1, TP.HCM -
-
-
-
-
45.2M
-
Doanh thu
-
-
-
342
-
Đơn hàng
-
-
-
5
-
Nhân viên
-
-
-
48
-
Sản phẩm
-
-
-
- - -
-
- - @* Store 2: Nhà hàng Q3 — Active *@ -
-
-
- -
-
-
- Nhà hàng Q3 -
- - Hoạt động +
+
+
--
+
Doanh thu
+
+
+
--
+
Đơn hàng
+
+
+
--
+
Nhân viên
+
+
+
--
+
Sản phẩm
- Restaurant • 456 Lê Văn Sỹ, Q3, TP.HCM -
-
-
-
-
62.8M
-
Doanh thu
-
-
-
185
-
Đơn hàng
-
-
-
8
-
Nhân viên
-
-
-
72
-
Sản phẩm
-
-
-
- - -
-
- - @* Store 3: Café Thủ Đức — Active *@ -
-
-
- -
-
-
- Café Thủ Đức -
- - Hoạt động -
+
+ @if (IsSetup(shop.Status)) + { + + } + else if (isPaused) + { + + } + else + { + + + }
- Café • 12 Võ Văn Ngân, Thủ Đức
-
-
-
-
20.5M
-
Doanh thu
-
-
-
156
-
Đơn hàng
-
-
-
3
-
Nhân viên
-
-
-
35
-
Sản phẩm
-
-
-
- - -
-
- - @* Store 4: Karaoke Star Q7 — Setup *@ -
-
-
- -
-
-
- Karaoke Star Q7 -
- - Thiết lập -
-
- Karaoke • 789 Nguyễn Thị Thập, Q7 -
-
-
-
-
--
-
Doanh thu
-
-
-
--
-
Đơn hàng
-
-
-
0
-
Nhân viên
-
-
-
0
-
Sản phẩm
-
-
-
- -
-
- - @* Store 5: Spa Garden Q2 — Paused *@ -
-
-
- -
-
-
- Spa Garden Q2 -
- - Tạm dừng -
-
- Spa • 321 Nguyễn Đình Chiểu, Q2 -
-
-
-
-
12.1M
-
Doanh thu
-
-
-
89
-
Đơn hàng
-
-
-
2
-
Nhân viên
-
-
-
24
-
Sản phẩm
-
-
-
- -
-
+ } + }
@code { + private List _shops = new(); private string _activeTab = "all"; + + private IEnumerable FilteredShops => _shops + .Where(s => _activeTab == "all" + || (_activeTab == "active" && IsActive(s.Status)) + || (_activeTab == "setup" && IsSetup(s.Status)) + || (_activeTab == "paused" && IsPaused(s.Status))) + .Where(s => string.IsNullOrWhiteSpace(SearchQuery) + || (s.Name?.Contains(SearchQuery, StringComparison.OrdinalIgnoreCase) ?? false) + || (s.Slug?.Contains(SearchQuery, StringComparison.OrdinalIgnoreCase) ?? false) + || (s.Category?.Contains(SearchQuery, StringComparison.OrdinalIgnoreCase) ?? false)); + + protected override async Task OnInitializedAsync() + { + IsLoading = true; + try + { + _shops = await DataService.GetShopsAsync(); + } + catch + { + _shops = new(); + } + finally + { + IsLoading = false; + } + } + + private static bool IsActive(string? status) => status?.Equals("Published", StringComparison.OrdinalIgnoreCase) == true + || status?.Equals("active", StringComparison.OrdinalIgnoreCase) == true; + private static bool IsSetup(string? status) => status?.Equals("Draft", StringComparison.OrdinalIgnoreCase) == true + || status?.Equals("setup", StringComparison.OrdinalIgnoreCase) == true; + private static bool IsPaused(string? status) => status?.Equals("Inactive", StringComparison.OrdinalIgnoreCase) == true + || status?.Equals("paused", StringComparison.OrdinalIgnoreCase) == true; + + private static string GetStatusBadgeClass(string? status) => status?.ToLowerInvariant() switch + { + "published" or "active" => "online", + "draft" or "setup" => "setup", + "inactive" or "paused" => "paused", + _ => "setup" + }; + + private static string GetStatusLabel(string? status) => status?.ToLowerInvariant() switch + { + "published" or "active" => "Hoạt động", + "draft" or "setup" => "Thiết lập", + "inactive" or "paused" => "Tạm dừng", + "closed" => "Đã đóng", + _ => status ?? "—" + }; + + private static string GetShopIcon(string? category) => category?.ToLowerInvariant() switch + { + "foodbeverage" or "café" or "cafe" or "coffee" => "coffee", + "restaurant" or "nhà hàng" => "utensils", + "entertainment" or "karaoke" => "mic", + "beauty" or "spa" => "sparkles", + "retail" => "shopping-bag", + _ => "store" + }; + + private static string GetCategoryColor(string? category) => category?.ToLowerInvariant() switch + { + "foodbeverage" or "café" or "cafe" => "var(--admin-orange-primary)", + "restaurant" => "#3B82F6", + "entertainment" or "karaoke" => "#8B5CF6", + "beauty" or "spa" => "#EC4899", + "retail" => "#22C55E", + _ => "var(--admin-orange-primary)" + }; + + private static string GetCategoryBgColor(string? category) => category?.ToLowerInvariant() switch + { + "foodbeverage" or "café" or "cafe" => "rgba(255,92,0,0.125)", + "restaurant" => "rgba(59,130,246,0.125)", + "entertainment" or "karaoke" => "rgba(139,92,246,0.125)", + "beauty" or "spa" => "rgba(236,72,153,0.125)", + "retail" => "rgba(34,197,94,0.125)", + _ => "rgba(255,92,0,0.125)" + }; } diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreSettings.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreSettings.razor index 169e2232..58bd137c 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreSettings.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreSettings.razor @@ -1,11 +1,13 @@ @page "/admin/stores/{Id}/settings" @layout AdminLayout @inherits AdminBase +@inject PosDataService DataService +@using WebClientTpos.Client.Services +@using WebClientTpos.Shared.DTOs @* - EN: Store settings — tabs for general, payment, receipts, integrations. - VI: Cài đặt cửa hàng — tabs cho chung, thanh toán, hóa đơn, tích hợp. - Design: pencil-design/src/pages/tPOS/admin/store-settings.pen + EN: Store settings — tabs for general, payment, receipts, integrations (real data from API). + VI: Cài đặt cửa hàng — tabs cho chung, thanh toán, hóa đơn, tích hợp (dữ liệu thực từ API). *@ Cài đặt cửa hàng — GoodGo Admin @@ -18,13 +20,32 @@

Cài đặt cửa hàng

-

Coffee House Q1 — Quản lý cài đặt

+

@(_shop?.Name ?? "Đang tải...") — Quản lý cài đặt

-
@@ -52,198 +73,284 @@ @* ═══ CONTENT ═══ *@
- @if (_tab == "general") + @if (IsLoading) { - @* General Settings *@ -
-
-

Thông tin cửa hàng

-
-
-
-
- - -
-
- - -
-
-
- - -
-
-
- - -
-
- - -
-
-
-
- - @* Operating Hours *@ -
-
-

Giờ hoạt động

-
-
-
-
- - -
-
- - -
-
-
-
- - @* Danger Zone *@ -
-
-

- - Vùng nguy hiểm -

-
-
-
-
-
Tạm dừng cửa hàng
-
Cửa hàng sẽ ngừng hoạt động tạm thời
-
- -
-
-
-
-
Xóa cửa hàng
-
Không thể hoàn tác, tất cả dữ liệu sẽ bị xóa
-
- -
-
+
+
+

Đang tải cài đặt cửa hàng...

} - - @if (_tab == "payment") + else if (_shop == null) { - @* Payment Methods *@ -
-
-

Phương thức thanh toán

+
+ +

Không tìm thấy cửa hàng

+ +
+ } + else + { + @if (_tab == "general") + { + @* General Settings *@ +
+
+

Thông tin cửa hàng

+
+
+
+
+ + +
+
+ + + Không thể thay đổi slug sau khi tạo +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
-
- @foreach (var pm in _paymentMethods) - { -
+ + @* Danger Zone *@ +
+
+

+ + Vùng nguy hiểm +

+
+
+
+
+
Tạm dừng cửa hàng
+
Cửa hàng sẽ ngừng hoạt động tạm thời
+
+ +
+
+
+
+
Đóng cửa hàng
+
Không thể hoàn tác, cửa hàng sẽ bị đóng vĩnh viễn
+
+ +
+
+
+ } + + @if (_tab == "payment") + { + @* Payment Methods *@ +
+
+

Phương thức thanh toán

+
+
+ @foreach (var pm in _paymentMethods) + { +
+
+ +
+
@pm.Name
+
@pm.Desc
+
+
+ +
+ } +
+
+ } + + @if (_tab == "receipt") + { +
+
+

Mẫu hóa đơn

+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ } + + @if (_tab == "integrations") + { +
+
+

Tích hợp bên thứ ba

+
+
+
- +
-
@pm.Name
-
@pm.Desc
+
GrabFood
+
Đồng bộ đơn hàng delivery
- +
- } -
-
- } - - @if (_tab == "receipt") - { -
-
-

Mẫu hóa đơn

-
-
-
- - -
-
- - -
-
- - -
-
-
- } - - @if (_tab == "integrations") - { -
-
-

Tích hợp bên thứ ba

-
-
-
-
- -
-
GrabFood
-
Đồng bộ đơn hàng delivery
+
+
+ +
+
ShopeeFood
+
Đồng bộ đơn hàng delivery
+
+
- Đã kết nối -
-
-
- -
-
ShopeeFood
-
Đồng bộ đơn hàng delivery
+
+
+ +
+
Kế toán (Misa)
+
Xuất hóa đơn điện tử
+
+
- -
-
-
- -
-
Kế toán (Misa)
-
Xuất hóa đơn điện tử
-
-
-
-
+ } }
@code { - [Parameter] public string Id { get; set; } = "1"; + [Inject] private MerchantApiService MerchantApi { get; set; } = default!; + [Parameter] public string Id { get; set; } = ""; + + private PosDataService.ShopInfo? _shop; private string _tab = "general"; + private bool _isSaving = false; + private string? _successMessage; + private string? _errorMessage; + + // EN: Editable fields pre-filled from shop data + // VI: Các trường có thể sửa, điền sẵn từ dữ liệu shop + private string _editName = ""; + private string _editDescription = ""; + private string _editPhone = ""; + private string _editEmail = ""; + + protected override async Task OnInitializedAsync() + { + IsLoading = true; + try + { + if (Guid.TryParse(Id, out var shopId)) + { + _shop = await DataService.GetShopByIdAsync(shopId); + if (_shop != null) + { + _editName = _shop.Name; + _editDescription = _shop.Description ?? ""; + _editPhone = _shop.Phone ?? ""; + _editEmail = _shop.Email ?? ""; + } + } + } + catch + { + _shop = null; + } + finally + { + IsLoading = false; + } + } + + private async Task SaveSettings() + { + if (_isSaving || _shop == null) return; + _isSaving = true; + _successMessage = null; + _errorMessage = null; + StateHasChanged(); + + var dto = new ShopUpdateDto + { + Name = _editName, + Description = _editDescription, + Phone = _editPhone, + Email = _editEmail + }; + + var (success, error) = await MerchantApi.UpdateShopAsync(_shop.Id, dto); + if (success) + { + _successMessage = "Đã lưu thành công!"; + // EN: Reload shop data to reflect changes + // VI: Tải lại dữ liệu shop để phản ánh thay đổi + _shop = await DataService.GetShopByIdAsync(_shop.Id); + } + else + { + _errorMessage = error ?? "Không thể lưu thay đổi"; + } + + _isSaving = false; + } + + private static string GetStatusLabel(string? status) => status?.ToLowerInvariant() switch + { + "published" or "active" => "Đang mở", + "draft" or "setup" => "Thiết lập", + "inactive" or "paused" => "Tạm dừng", + "closed" => "Đã đóng", + _ => status ?? "—" + }; private record PaymentMethod(string Name, string Icon, string Color, string Desc, bool Enabled); private readonly PaymentMethod[] _paymentMethods = new[] @@ -254,6 +361,4 @@ new PaymentMethod("Chuyển khoản", "send", "#F59E0B", "Chuyển khoản ngân hàng", false), new PaymentMethod("Thẻ quà tặng", "gift", "#EC4899", "Gift card & voucher", false), }; - - private void SaveSettings() { /* Save logic */ } } diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/MerchantApiService.cs b/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/MerchantApiService.cs index e1b391aa..1e6e2903 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/MerchantApiService.cs +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/MerchantApiService.cs @@ -76,6 +76,29 @@ public class MerchantApiService } } + /// + /// EN: Update an existing shop. + /// VI: Cập nhật shop hiện tại. + /// + public async Task<(bool Success, string? Error)> UpdateShopAsync(Guid shopId, ShopUpdateDto dto) + { + try + { + await AttachTokenAsync(); + var response = await _http.PutAsJsonAsync($"/api/merchants/api/v1/shops/{shopId}", dto); + + if (response.IsSuccessStatusCode) + return (true, null); + + var error = await ParseErrorAsync(response); + return (false, error); + } + catch (Exception ex) + { + return (false, $"Lỗi kết nối: {ex.Message}"); + } + } + /// /// EN: Attach Bearer token to HttpClient for authorized requests. /// VI: Gắn Bearer token vào HttpClient cho các request cần xác thực. diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/PosDataService.cs b/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/PosDataService.cs index ea3c3f05..4c3dff83 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/PosDataService.cs +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/PosDataService.cs @@ -25,6 +25,9 @@ public class PosDataService public async Task> GetShopsAsync() => await _http.GetFromJsonAsync>("api/bff/shops", _jsonOptions) ?? new(); + public async Task GetShopByIdAsync(Guid shopId) + => await _http.GetFromJsonAsync($"api/bff/shops/{shopId}", _jsonOptions); + public async Task> GetProductsAsync(Guid shopId) => await _http.GetFromJsonAsync>($"api/bff/shops/{shopId}/products", _jsonOptions) ?? new(); diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/BffDataController.cs b/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/BffDataController.cs index b4f93124..81884ebd 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/BffDataController.cs +++ b/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/BffDataController.cs @@ -8,8 +8,15 @@ namespace WebClientTpos.Server.Controllers; [Route("api/bff")] public class BffDataController : ControllerBase { + // EN: DB host configurable via env var (Docker: "postgres", dev: "localhost") + // VI: DB host cấu hình qua env var (Docker: "postgres", dev: "localhost") + private static readonly string _dbHost = Environment.GetEnvironmentVariable("BFF_DB_HOST") ?? "localhost"; + private static readonly string _dbPort = Environment.GetEnvironmentVariable("BFF_DB_PORT") ?? "5432"; + private static readonly string _dbUser = Environment.GetEnvironmentVariable("BFF_DB_USER") ?? "goodgo"; + private static readonly string _dbPass = Environment.GetEnvironmentVariable("BFF_DB_PASS") ?? "goodgo_dev_2024"; + private static string ConnStr(string db) => - $"Host=localhost;Port=5432;Database={db};Username=goodgo;Password=goodgo_dev_2024"; + $"Host={_dbHost};Port={_dbPort};Database={db};Username={_dbUser};Password={_dbPass}"; [HttpGet("shops")] public async Task GetShops() @@ -27,6 +34,26 @@ public class BffDataController : ControllerBase return Ok(shops); } + [HttpGet("shops/{shopId:guid}")] + public async Task GetShopById(Guid shopId) + { + await using var conn = new NpgsqlConnection(ConnStr("merchant_service")); + var shop = await conn.QueryFirstOrDefaultAsync( + @"SELECT s.id, s.name, s.slug, s.description, s.phone, s.email, + s.open_time, s.close_time, + bc.name as category, st.name as status + FROM shops s + JOIN business_categories bc ON s.category_id = bc.id + JOIN shop_statuses st ON s.status_id = st.id + WHERE s.id = @ShopId AND s.is_deleted = false", + new { ShopId = shopId }); + + if (shop == null) + return NotFound(new { message = "Shop not found" }); + + return Ok(shop); + } + [HttpGet("shops/{shopId}/products")] public async Task GetProducts(Guid shopId) { diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Shared/DTOs/MerchantDtos.cs b/apps/web-client-tpos-net/src/WebClientTpos.Shared/DTOs/MerchantDtos.cs index 9fcc18af..460bbed3 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Shared/DTOs/MerchantDtos.cs +++ b/apps/web-client-tpos-net/src/WebClientTpos.Shared/DTOs/MerchantDtos.cs @@ -63,3 +63,16 @@ public class ShopCreateResult public string Slug { get; set; } = string.Empty; public string Status { get; set; } = string.Empty; } + +/// +/// EN: DTO for updating an existing shop. +/// VI: DTO cho cập nhật shop. +/// +public class ShopUpdateDto +{ + public string? Name { get; set; } + public string? Description { get; set; } + public string? Phone { get; set; } + public string? Email { get; set; } + public string? Website { get; set; } +} diff --git a/deployments/local/docker-compose.yml b/deployments/local/docker-compose.yml index 86053b0e..a659482e 100644 --- a/deployments/local/docker-compose.yml +++ b/deployments/local/docker-compose.yml @@ -1288,6 +1288,12 @@ services: - ReverseProxy__Clusters__merchant-cluster__Destinations__destination1__Address=http://merchant-service-net:8080 - ReverseProxy__Clusters__catalog-cluster__Destinations__destination1__Address=http://catalog-service-net:8080 - ReverseProxy__Clusters__order-cluster__Destinations__destination1__Address=http://order-service-net:8080 + # EN: BFF Data Controller — Direct DB queries for dashboard data + # VI: BFF Data Controller — Truy vấn DB trực tiếp cho dữ liệu dashboard + - BFF_DB_HOST=postgres + - BFF_DB_PORT=5432 + - BFF_DB_USER=goodgo + - BFF_DB_PASS=goodgo-local-2024 ports: - "3001:8080" depends_on: