diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/CafeMobile.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/CafeMobile.razor index 025bdc58..21313e95 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/CafeMobile.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/CafeMobile.razor @@ -5,8 +5,23 @@ @page "/pos/cafe/mobile" @layout PosLayout @inherits PosBase +@inject WebClientTpos.Client.Services.PosDataService DataService
+ @if (_isLoading) + { +
+ Đang tải... +
+ } + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + { @* EN: Category tabs / VI: Tab danh mục *@
@foreach (var cat in _categories) @@ -89,32 +104,67 @@
} + } @code { - private readonly string[] _categories = { "Tất cả", "Cà phê", "Trà", "Sinh tố", "Đồ ăn" }; + // EN: Cafe shop ID / VI: ID cửa hàng cafe + private static readonly Guid CafeShopId = Guid.Parse("b0000001-0000-0000-0000-000000000001"); + + // EN: Loading state / VI: Trạng thái tải + private bool _isLoading = true; + private bool _loadError; + + // EN: Categories / VI: Danh mục + private string[] _categories = { "Tất cả" }; private string _selectedCategory = "Tất cả"; private bool _showCart; - private readonly List _products = new() - { - new("Cà phê sữa đá", 35_000, "Cà phê"), - new("Cà phê đen", 29_000, "Cà phê"), - new("Bạc xỉu", 39_000, "Cà phê"), - new("Cappuccino", 55_000, "Cà phê"), - new("Trà đào", 45_000, "Trà"), - new("Trà vải", 45_000, "Trà"), - new("Sinh tố bơ", 55_000, "Sinh tố"), - new("Sinh tố xoài", 49_000, "Sinh tố"), - new("Bánh mì", 25_000, "Đồ ăn"), - new("Croissant", 35_000, "Đồ ăn"), - }; + // EN: Product list / VI: Danh sách sản phẩm + private List _products = new(); + // EN: Cart items / VI: Mục giỏ hàng private readonly List _cartItems = new(); private IEnumerable FilteredProducts => _selectedCategory == "Tất cả" ? _products : _products.Where(p => p.Category == _selectedCategory); private decimal CartTotal => _cartItems.Sum(i => i.Price * i.Qty); + protected override async Task OnInitializedAsync() + { + try + { + var productsTask = DataService.GetProductsAsync(CafeShopId); + var categoriesTask = DataService.GetCategoriesAsync(CafeShopId); + await Task.WhenAll(productsTask, categoriesTask); + + var apiProducts = await productsTask; + var apiCategories = await categoriesTask; + + _products = apiProducts.Select(p => new Product( + p.Name, + p.Price, + p.Category ?? "Khác" + )).ToList(); + + var catNames = apiCategories.Select(c => c.Name).ToList(); + if (catNames.Count > 0) + _categories = new[] { "Tất cả" }.Concat(catNames).ToArray(); + else + { + var productCats = _products.Select(p => p.Category).Distinct().ToList(); + _categories = new[] { "Tất cả" }.Concat(productCats).ToArray(); + } + } + catch + { + _loadError = true; + } + finally + { + _isLoading = false; + } + } + private void AddToCart(Product product) { var existing = _cartItems.FirstOrDefault(i => i.Name == product.Name); diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/CafeTablet.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/CafeTablet.razor index 3da4510f..04067b65 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/CafeTablet.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/CafeTablet.razor @@ -5,9 +5,24 @@ @page "/pos/cafe/tablet" @layout PosLayout @inherits PosBase +@inject WebClientTpos.Client.Services.PosDataService DataService @* ═══ PRODUCT PANEL ═══ *@
+ @if (_isLoading) + { +
+ Đang tải... +
+ } + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + {
@foreach (var cat in _categories) { @@ -31,6 +46,7 @@
}
+ } @* ═══ CART SIDEBAR ═══ *@ @@ -72,29 +88,62 @@ @code { - private readonly string[] _categories = { "Tất cả", "Cà phê", "Trà", "Sinh tố", "Đồ ăn" }; + // EN: Cafe shop ID / VI: ID cửa hàng cafe + private static readonly Guid CafeShopId = Guid.Parse("b0000001-0000-0000-0000-000000000001"); + + // EN: Loading state / VI: Trạng thái tải + private bool _isLoading = true; + private bool _loadError; + + // EN: Categories / VI: Danh mục + private string[] _categories = { "Tất cả" }; private string _selectedCategory = "Tất cả"; - private readonly List _products = new() - { - new("Cà phê sữa đá", 35_000, "Cà phê"), - new("Cà phê đen", 29_000, "Cà phê"), - new("Bạc xỉu", 39_000, "Cà phê"), - new("Cappuccino", 55_000, "Cà phê"), - new("Latte", 55_000, "Cà phê"), - new("Trà đào", 45_000, "Trà"), - new("Trà vải", 45_000, "Trà"), - new("Sinh tố bơ", 55_000, "Sinh tố"), - new("Sinh tố xoài", 49_000, "Sinh tố"), - new("Bánh mì", 25_000, "Đồ ăn"), - new("Croissant", 35_000, "Đồ ăn"), - }; + // EN: Product list / VI: Danh sách sản phẩm + private List _products = new(); + // EN: Cart items / VI: Mục giỏ hàng private readonly List _cartItems = new(); private IEnumerable FilteredProducts => _selectedCategory == "Tất cả" ? _products : _products.Where(p => p.Category == _selectedCategory); private decimal CartTotal => _cartItems.Sum(i => i.Price * i.Qty); + protected override async Task OnInitializedAsync() + { + try + { + var productsTask = DataService.GetProductsAsync(CafeShopId); + var categoriesTask = DataService.GetCategoriesAsync(CafeShopId); + await Task.WhenAll(productsTask, categoriesTask); + + var apiProducts = await productsTask; + var apiCategories = await categoriesTask; + + _products = apiProducts.Select(p => new Product( + p.Name, + p.Price, + p.Category ?? "Khác" + )).ToList(); + + var catNames = apiCategories.Select(c => c.Name).ToList(); + if (catNames.Count > 0) + _categories = new[] { "Tất cả" }.Concat(catNames).ToArray(); + else + { + var productCats = _products.Select(p => p.Category).Distinct().ToList(); + _categories = new[] { "Tất cả" }.Concat(productCats).ToArray(); + } + } + catch + { + _loadError = true; + } + finally + { + _isLoading = false; + } + } + private void AddToCart(Product product) { var existing = _cartItems.FirstOrDefault(i => i.Name == product.Name); diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/BaristaQueue.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/BaristaQueue.razor index d010b4e8..a1672ee5 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/BaristaQueue.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/BaristaQueue.razor @@ -56,6 +56,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + // EN: Column definitions / VI: Định nghĩa cột private readonly List _columns = new() { diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/CafeJourney.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/CafeJourney.razor index ff48d0ec..6757348e 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/CafeJourney.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/CafeJourney.razor @@ -228,6 +228,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + private int _currentStep = 0; // EN: Journey steps / VI: Các bước hành trình diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/CustomerDisplay.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/CustomerDisplay.razor index f5d141f5..fd40bda6 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/CustomerDisplay.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/CustomerDisplay.razor @@ -70,6 +70,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + // EN: Demo order items / VI: Mục đơn hàng mẫu private readonly List _orderItems = new() { diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/DailyReport.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/DailyReport.razor index 99173dde..1ecaccbe 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/DailyReport.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/DailyReport.razor @@ -79,6 +79,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + // EN: Summary statistics / VI: Thống kê tổng quan private readonly List _stats = new() { diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/LoyaltyStamp.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/LoyaltyStamp.razor index e477e24b..e4ff54e5 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/LoyaltyStamp.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/LoyaltyStamp.razor @@ -95,6 +95,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + private string _phone = "0901234567"; private int _totalStamps = 10; diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/MenuManagement.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/MenuManagement.razor index 7a3fccef..d557d6b3 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/MenuManagement.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/MenuManagement.razor @@ -5,8 +5,23 @@ @page "/pos/cafe/menu-management" @layout PosLayout @inherits PosBase +@inject WebClientTpos.Client.Services.PosDataService DataService
+ @if (_isLoading) + { +
+ Đang tải... +
+ } + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + { @* EN: Header / VI: Tiêu đề *@
@@ -93,34 +108,61 @@ }
+ }
@code { + // EN: Cafe shop ID / VI: ID cửa hàng cafe + private static readonly Guid CafeShopId = Guid.Parse("b0000001-0000-0000-0000-000000000001"); + + // EN: Loading state / VI: Trạng thái tải + private bool _isLoading = true; + private bool _loadError; + private string _filterCategory = "Tất cả"; - private readonly string[] _filterCategories = { "Tất cả", "Cà phê", "Trà", "Sinh tố", "Đồ ăn" }; + private string[] _filterCategories = { "Tất cả" }; private IEnumerable FilteredItems => _filterCategory == "Tất cả" ? _items : _items.Where(i => i.Category == _filterCategory); - // EN: Demo menu items / VI: Danh sách menu mẫu - private readonly List _items = new() + // EN: Menu items loaded from DB / VI: Danh sách menu tải từ DB + private List _items = new(); + + protected override async Task OnInitializedAsync() { - new("Cà phê sữa đá", "Cà phê", 35_000), - new("Cà phê đen", "Cà phê", 29_000), - new("Bạc xỉu", "Cà phê", 39_000), - new("Espresso", "Cà phê", 45_000), - new("Cappuccino", "Cà phê", 55_000), - new("Latte", "Cà phê", 55_000), - new("Trà đào", "Trà", 45_000), - new("Trà vải", "Trà", 45_000), - new("Trà sen vàng", "Trà", 49_000), - new("Sinh tố bơ", "Sinh tố", 55_000) { Available = false }, - new("Sinh tố xoài", "Sinh tố", 49_000), - new("Sinh tố dâu", "Sinh tố", 49_000) { Available = false }, - new("Bánh mì", "Đồ ăn", 25_000), - new("Croissant", "Đồ ăn", 35_000), - new("Cookie", "Đồ ăn", 20_000), - }; + try + { + var productsTask = DataService.GetProductsAsync(CafeShopId); + var categoriesTask = DataService.GetCategoriesAsync(CafeShopId); + await Task.WhenAll(productsTask, categoriesTask); + + var apiProducts = await productsTask; + var apiCategories = await categoriesTask; + + _items = apiProducts.Select(p => new MenuItem( + p.Name, + p.Category ?? "Khác", + p.Price + )).ToList(); + + var catNames = apiCategories.Select(c => c.Name).ToList(); + if (catNames.Count > 0) + _filterCategories = new[] { "Tất cả" }.Concat(catNames).ToArray(); + else + { + var productCats = _items.Select(i => i.Category).Distinct().ToList(); + _filterCategories = new[] { "Tất cả" }.Concat(productCats).ToArray(); + } + } + catch + { + _loadError = true; + } + finally + { + _isLoading = false; + } + } private class MenuItem(string name, string category, decimal price) { diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/MilkFoamOptions.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/MilkFoamOptions.razor index 3935f8dd..c7aa85b0 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/MilkFoamOptions.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/MilkFoamOptions.razor @@ -123,6 +123,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + private string _productName = "Latte"; private decimal _basePrice = 45_000; private decimal _extraPrice = 0; diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/OrderCustomize.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/OrderCustomize.razor index 6274c52e..40847f07 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/OrderCustomize.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/OrderCustomize.razor @@ -110,6 +110,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + private string _productName = "Cà phê sữa đá"; private decimal _basePrice = 35_000; private decimal _extraPrice = 0; diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/QueueDisplay.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/QueueDisplay.razor index 8cea45d0..1451127a 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/QueueDisplay.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Cafe/Workflow/QueueDisplay.razor @@ -69,6 +69,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + // EN: Preparing orders / VI: Đơn đang pha private readonly List _preparingOrders = new() { diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeDesktop.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeDesktop.razor index 998ee465..8892e53d 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeDesktop.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeDesktop.razor @@ -11,9 +11,24 @@ @page "/pos/karaoke" @layout PosLayout @inherits PosBase +@inject WebClientTpos.Client.Services.PosDataService DataService @* ═══ ROOM MAP PANEL (LEFT) / PANEL SƠ ĐỒ PHÒNG (TRÁI) ═══ *@
+ @if (_isLoading) + { +
+ Đang tải... +
+ } + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + { @* EN: Floor/zone tabs / VI: Tab tầng/khu vực *@
@foreach (var zone in _zones) @@ -54,6 +69,7 @@
}
+ } @* ═══ SESSION PANEL (RIGHT) / PANEL PHIÊN HÁT (PHẢI) ═══ *@ @@ -131,9 +147,16 @@ @code { + // EN: Karaoke shop ID / VI: ID cửa hàng karaoke + private static readonly Guid KaraokeShopId = Guid.Parse("b0000003-0000-0000-0000-000000000003"); + + // EN: Loading state / VI: Trạng thái tải + private bool _isLoading = true; + private bool _loadError; + // EN: Zone filter / VI: Bộ lọc khu vực private string _activeZone = "Tất cả"; - private readonly string[] _zones = { "Tất cả", "Tầng 1", "Tầng 2", "VIP" }; + private string[] _zones = { "Tất cả" }; // EN: Selected room / VI: Phòng đang chọn private RoomInfo? SelectedRoom { get; set; } @@ -141,23 +164,8 @@ // EN: Demo room rate / VI: Giá phòng mẫu private readonly decimal _roomRate = 150_000; - // EN: Demo room data / VI: Dữ liệu phòng mẫu - private readonly List _rooms = new() - { - new("R01","Phòng 101",8,"Standard","available","Tầng 1",null), - new("R02","Phòng 102",6,"occupied","Standard","Tầng 1",DateTime.Now.AddHours(-1.5)), - new("R03","Phòng 103",10,"occupied","Standard","Tầng 1",DateTime.Now.AddMinutes(-45)), - new("R04","Phòng 104",4,"reserved","Standard","Tầng 1",null), - new("R05","Phòng 201",12,"available","VIP","Tầng 2",null), - new("R06","Phòng 202",8,"occupied","VIP","Tầng 2",DateTime.Now.AddHours(-2)), - new("R07","Phòng 203",6,"cleaning","Standard","Tầng 2",null), - new("R08","Phòng VIP 1",15,"available","Deluxe","VIP",null), - new("R09","Phòng VIP 2",20,"occupied","Deluxe","VIP",DateTime.Now.AddHours(-3)), - new("R10","Phòng VIP 3",15,"reserved","Deluxe","VIP",null), - }; - - private IEnumerable FilteredRooms => - _activeZone == "Tất cả" ? _rooms : _rooms.Where(r => r.Zone == _activeZone); + // EN: Room data loaded from DB / VI: Dữ liệu phòng tải từ DB + private List _rooms = new(); // EN: Demo F&B items / VI: Mục F&B mẫu private readonly List _demoFnbItems = new() @@ -166,6 +174,38 @@ new("Khô mực nướng", 85_000, 2), new("Nước ngọt", 20_000, 4), }; + private IEnumerable FilteredRooms => + _activeZone == "Tất cả" ? _rooms : _rooms.Where(r => r.Zone == _activeZone); + + protected override async Task OnInitializedAsync() + { + try + { + var tables = await DataService.GetTablesAsync(KaraokeShopId); + + _rooms = tables.Select(t => new RoomInfo( + t.Id.ToString(), + t.TableNumber, + t.Capacity, + t.Status, + t.Zone ?? "Standard", + t.Zone ?? "Tầng 1", + t.StartedAt + )).ToList(); + + var zoneNames = _rooms.Select(r => r.Zone).Distinct().ToList(); + _zones = new[] { "Tất cả" }.Concat(zoneNames).ToArray(); + } + catch + { + _loadError = true; + } + finally + { + _isLoading = false; + } + } + private void SelectRoom(RoomInfo room) => SelectedRoom = room; private static string GetStatusColor(string status) => status switch diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeMobile.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeMobile.razor index 7eb16a85..8ffcf815 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeMobile.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeMobile.razor @@ -5,8 +5,23 @@ @page "/pos/karaoke/mobile" @layout PosLayout @inherits PosBase +@inject WebClientTpos.Client.Services.PosDataService DataService
+ @if (_isLoading) + { +
+ Đang tải... +
+ } + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + { @* ═══ HEADER / TIÊU ĐỀ ═══ *@
Phòng Karaoke @@ -79,30 +94,56 @@ Gọi phục vụ
+ }
@code { + // EN: Karaoke shop ID / VI: ID cửa hàng karaoke + private static readonly Guid KaraokeShopId = Guid.Parse("b0000003-0000-0000-0000-000000000003"); + + // EN: Loading state / VI: Trạng thái tải + private bool _isLoading = true; + private bool _loadError; + // EN: Zone filter / VI: Bộ lọc khu vực private string _activeZone = "Tất cả"; - private readonly string[] _zones = { "Tất cả", "Tầng 1", "Tầng 2", "VIP" }; + private string[] _zones = { "Tất cả" }; - // EN: Demo rooms / VI: Phòng mẫu - private readonly List _rooms = new() - { - new("R01","Phòng 101",8,"Standard","available","Tầng 1",null), - new("R02","Phòng 102",6,"occupied","Standard","Tầng 1",DateTime.Now.AddHours(-1.5)), - new("R03","Phòng 103",10,"reserved","Standard","Tầng 1",null), - new("R04","Phòng 201",12,"available","VIP","Tầng 2",null), - new("R05","Phòng 202",8,"occupied","VIP","Tầng 2",DateTime.Now.AddMinutes(-45)), - new("R06","Phòng 203",6,"cleaning","Standard","Tầng 2",null), - new("R07","VIP 1",15,"available","Deluxe","VIP",null), - new("R08","VIP 2",20,"occupied","Deluxe","VIP",DateTime.Now.AddHours(-2)), - new("R09","VIP 3",15,"reserved","Deluxe","VIP",null), - }; + // EN: Room data loaded from DB / VI: Dữ liệu phòng tải từ DB + private List _rooms = new(); private IEnumerable FilteredRooms => _activeZone == "Tất cả" ? _rooms : _rooms.Where(r => r.Zone == _activeZone); + protected override async Task OnInitializedAsync() + { + try + { + var tables = await DataService.GetTablesAsync(KaraokeShopId); + + _rooms = tables.Select(t => new RoomInfo( + t.Id.ToString(), + t.TableNumber, + t.Capacity, + t.Zone ?? "Standard", + t.Status, + t.Zone ?? "Tầng 1", + t.StartedAt + )).ToList(); + + var zoneNames = _rooms.Select(r => r.Zone).Distinct().ToList(); + _zones = new[] { "Tất cả" }.Concat(zoneNames).ToArray(); + } + catch + { + _loadError = true; + } + finally + { + _isLoading = false; + } + } + private void OpenRoom(RoomInfo room) => NavigateTo("karaoke/room-session"); private static string GetStatusBg(string s) => s switch diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeTablet.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeTablet.razor index 6c532ec4..4a95d185 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeTablet.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/KaraokeTablet.razor @@ -5,9 +5,24 @@ @page "/pos/karaoke/tablet" @layout PosLayout @inherits PosBase +@inject WebClientTpos.Client.Services.PosDataService DataService @* ═══ ROOM MAP (LEFT) / SƠ ĐỒ PHÒNG (TRÁI) ═══ *@
+ @if (_isLoading) + { +
+ Đang tải... +
+ } + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + { @* EN: Zone filter tabs / VI: Tab lọc khu vực *@
@foreach (var zone in _zones) @@ -43,6 +58,7 @@
}
+ } @* ═══ SESSION SIDEBAR / THANH PHIÊN BÊN ═══ *@ @@ -123,26 +139,20 @@ @code { + // EN: Karaoke shop ID / VI: ID cửa hàng karaoke + private static readonly Guid KaraokeShopId = Guid.Parse("b0000003-0000-0000-0000-000000000003"); + + // EN: Loading state / VI: Trạng thái tải + private bool _isLoading = true; + private bool _loadError; + // EN: Zone filter / VI: Bộ lọc khu vực private string _activeZone = "Tất cả"; - private readonly string[] _zones = { "Tất cả", "Tầng 1", "Tầng 2", "VIP" }; + private string[] _zones = { "Tất cả" }; private RoomInfo? _selectedRoom; - // EN: Demo rooms / VI: Phòng mẫu - private readonly List _rooms = new() - { - new("R01","P.101",8,"Standard","available","Tầng 1",null), - new("R02","P.102",6,"occupied","Standard","Tầng 1",DateTime.Now.AddHours(-1)), - new("R03","P.103",10,"reserved","Standard","Tầng 1",null), - new("R04","P.201",12,"available","VIP","Tầng 2",null), - new("R05","P.202",8,"occupied","VIP","Tầng 2",DateTime.Now.AddMinutes(-90)), - new("R06","P.203",6,"cleaning","Standard","Tầng 2",null), - new("R07","VIP 1",15,"available","Deluxe","VIP",null), - new("R08","VIP 2",20,"occupied","Deluxe","VIP",DateTime.Now.AddHours(-2.5)), - }; - - private IEnumerable FilteredRooms => - _activeZone == "Tất cả" ? _rooms : _rooms.Where(r => r.Zone == _activeZone); + // EN: Room data loaded from DB / VI: Dữ liệu phòng tải từ DB + private List _rooms = new(); // EN: Demo F&B / VI: F&B mẫu private readonly List _demoFnb = new() @@ -151,6 +161,38 @@ new("Đậu phộng", 30_000, 2), }; + private IEnumerable FilteredRooms => + _activeZone == "Tất cả" ? _rooms : _rooms.Where(r => r.Zone == _activeZone); + + protected override async Task OnInitializedAsync() + { + try + { + var tables = await DataService.GetTablesAsync(KaraokeShopId); + + _rooms = tables.Select(t => new RoomInfo( + t.Id.ToString(), + t.TableNumber, + t.Capacity, + t.Zone ?? "Standard", + t.Status, + t.Zone ?? "Tầng 1", + t.StartedAt + )).ToList(); + + var zoneNames = _rooms.Select(r => r.Zone).Distinct().ToList(); + _zones = new[] { "Tất cả" }.Concat(zoneNames).ToArray(); + } + catch + { + _loadError = true; + } + finally + { + _isLoading = false; + } + } + private static string GetStatusBg(string s) => s switch { "available" => "rgba(34,197,94,.15)", "occupied" => "rgba(255,92,0,.18)", diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/HappyHour.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/HappyHour.razor index 3d881083..a662592f 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/HappyHour.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/HappyHour.razor @@ -100,6 +100,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + // EN: Time-based rates / VI: Giá theo khung giờ private readonly List _timeRates = new() { diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/KaraokeJourney.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/KaraokeJourney.razor index 36bbcb31..21a54cf6 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/KaraokeJourney.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/KaraokeJourney.razor @@ -267,6 +267,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + // EN: Active step index / VI: Chỉ số bước hiện tại private int _activeStep; private int _guestCount = 8; diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/MemberCard.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/MemberCard.razor index 93a52604..51fff6d6 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/MemberCard.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/MemberCard.razor @@ -152,6 +152,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + private string _searchTerm = "0901234567"; private RewardInfo? _selectedReward; diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/OrderFnb.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/OrderFnb.razor index dfdbf7da..aac4fe4b 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/OrderFnb.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/OrderFnb.razor @@ -5,6 +5,7 @@ @page "/pos/karaoke/order-fnb" @layout PosLayout @inherits PosBase +@inject WebClientTpos.Client.Services.PosDataService DataService @* ═══ PRODUCT PANEL (LEFT) / PANEL SẢN PHẨM (TRÁI) ═══ *@
@@ -19,6 +20,20 @@ Phòng VIP 2
+ @if (_isLoading) + { +
+ Đang tải... +
+ } + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + { @* EN: Category tabs / VI: Tab danh mục *@
@foreach (var cat in _categories) @@ -44,6 +59,7 @@
} + } @* ═══ ORDER PANEL (RIGHT) / PANEL ĐƠN HÀNG (PHẢI) ═══ *@ @@ -97,27 +113,19 @@ @code { + // EN: Karaoke shop ID / VI: ID cửa hàng karaoke + private static readonly Guid KaraokeShopId = Guid.Parse("b0000003-0000-0000-0000-000000000003"); + + // EN: Loading state / VI: Trạng thái tải + private bool _isLoading = true; + private bool _loadError; + // EN: Category filter / VI: Bộ lọc danh mục private string _activeCategory = "Tất cả"; - private readonly string[] _categories = { "Tất cả", "Đồ uống", "Đồ ăn", "Mồi" }; + private string[] _categories = { "Tất cả" }; - // EN: Product catalog / VI: Danh mục sản phẩm - private readonly List _products = new() - { - // EN: Beverages / VI: Đồ uống - new("Bia Tiger", 35_000, "Đồ uống"), new("Bia Heineken", 40_000, "Đồ uống"), - new("Bia Sài Gòn", 25_000, "Đồ uống"), new("Coca-Cola", 20_000, "Đồ uống"), - new("Nước suối", 10_000, "Đồ uống"), new("Nước cam ép", 35_000, "Đồ uống"), - new("Trà đào", 30_000, "Đồ uống"), new("Nước dừa", 25_000, "Đồ uống"), - // EN: Food / VI: Đồ ăn - new("Cơm chiên", 55_000, "Đồ ăn"), new("Mì xào", 50_000, "Đồ ăn"), - new("Gà rán", 65_000, "Đồ ăn"), new("Pizza mini", 75_000, "Đồ ăn"), - new("Khoai tây chiên", 40_000, "Đồ ăn"), new("Xúc xích nướng", 45_000, "Đồ ăn"), - // EN: Snacks / VI: Mồi - new("Khô mực nướng", 85_000, "Mồi"), new("Đậu phộng rang", 30_000, "Mồi"), - new("Trái cây dĩa", 120_000, "Mồi"), new("Bò khô", 60_000, "Mồi"), - new("Mực rim", 70_000, "Mồi"), new("Bánh tráng trộn", 35_000, "Mồi"), - }; + // EN: Product list loaded from DB / VI: Danh sách sản phẩm tải từ DB + private List _products = new(); // EN: Order items / VI: Mục đơn hàng private readonly List _orderItems = new(); @@ -125,6 +133,42 @@ private IEnumerable FilteredProducts => _activeCategory == "Tất cả" ? _products : _products.Where(p => p.Category == _activeCategory); + protected override async Task OnInitializedAsync() + { + try + { + var productsTask = DataService.GetProductsAsync(KaraokeShopId); + var categoriesTask = DataService.GetCategoriesAsync(KaraokeShopId); + await Task.WhenAll(productsTask, categoriesTask); + + var apiProducts = await productsTask; + var apiCategories = await categoriesTask; + + _products = apiProducts.Select(p => new Product( + p.Name, + p.Price, + p.Category ?? "Khác" + )).ToList(); + + var catNames = apiCategories.Select(c => c.Name).ToList(); + if (catNames.Count > 0) + _categories = new[] { "Tất cả" }.Concat(catNames).ToArray(); + else + { + var productCats = _products.Select(p => p.Category).Distinct().ToList(); + _categories = new[] { "Tất cả" }.Concat(productCats).ToArray(); + } + } + catch + { + _loadError = true; + } + finally + { + _isLoading = false; + } + } + private void AddToOrder(Product p) { var existing = _orderItems.FirstOrDefault(i => i.Name == p.Name); diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/PeakWarning.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/PeakWarning.razor index 9c88fc13..60613cc0 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/PeakWarning.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/PeakWarning.razor @@ -127,6 +127,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + // EN: Estimate hours / VI: Số giờ ước tính private int _estimateHours = 2; diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomExtend.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomExtend.razor index 591e6cf1..e777dabb 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomExtend.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomExtend.razor @@ -147,6 +147,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + // EN: Extension options / VI: Tùy chọn gia hạn private readonly List _extendOptions = new() { diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomMap.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomMap.razor index a0bcf77d..0870e343 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomMap.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomMap.razor @@ -5,6 +5,7 @@ @page "/pos/karaoke/room-map" @layout PosLayout @inherits PosBase +@inject WebClientTpos.Client.Services.PosDataService DataService
@* ═══ TOOLBAR / THANH CÔNG CỤ ═══ *@ @@ -36,6 +37,20 @@
+ @if (_isLoading) + { +
+ Đang tải... +
+ } + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + { @* ═══ FLOOR TABS / TAB TẦNG ═══ *@
@foreach (var floor in _floors) @@ -90,30 +105,55 @@ Đã đặt: @_rooms.Count(r => r.Status == "reserved") Đang dọn: @_rooms.Count(r => r.Status == "cleaning")
+ } @code { + // EN: Karaoke shop ID / VI: ID cửa hàng karaoke + private static readonly Guid KaraokeShopId = Guid.Parse("b0000003-0000-0000-0000-000000000003"); + + // EN: Loading state / VI: Trạng thái tải + private bool _isLoading = true; + private bool _loadError; + // EN: Floor filter / VI: Bộ lọc tầng - private string _activeFloor = "Tầng 1"; - private readonly string[] _floors = { "Tầng 1", "Tầng 2", "Tầng 3" }; + private string _activeFloor = ""; + private string[] _floors = Array.Empty(); private RoomInfo? _selectedRoom; - // EN: Demo rooms / VI: Phòng mẫu - private readonly List _rooms = new() + // EN: Room data loaded from DB / VI: Dữ liệu phòng tải từ DB + private List _rooms = new(); + + protected override async Task OnInitializedAsync() { - new("R01","P.101",8,"Standard","available","Tầng 1","Khu A",null), - new("R02","P.102",6,"occupied","Standard","Tầng 1","Khu A","1:30"), - new("R03","P.103",10,"Standard","reserved","Tầng 1","Khu A",null), - new("R04","P.104",8,"Standard","available","Tầng 1","Khu B",null), - new("R05","P.105",6,"Standard","cleaning","Tầng 1","Khu B",null), - new("R06","P.201",12,"VIP","occupied","Tầng 2","Khu VIP","2:15"), - new("R07","P.202",10,"VIP","available","Tầng 2","Khu VIP",null), - new("R08","P.203",8,"VIP","occupied","Tầng 2","Khu VIP","0:45"), - new("R09","P.204",6,"Standard","available","Tầng 2","Khu thường",null), - new("R10","P.301",20,"Deluxe","occupied","Tầng 3","Khu Deluxe","3:00"), - new("R11","P.302",15,"Deluxe","available","Tầng 3","Khu Deluxe",null), - new("R12","P.303",15,"Deluxe","reserved","Tầng 3","Khu Deluxe",null), - }; + try + { + var tables = await DataService.GetTablesAsync(KaraokeShopId); + + _rooms = tables.Select(t => new RoomInfo( + t.Id.ToString(), + t.TableNumber, + t.Capacity, + t.Zone ?? "Standard", + t.Status, + t.Zone ?? "Tầng 1", + t.Zone ?? "Khu A", + t.StartedAt.HasValue ? (DateTime.Now - t.StartedAt.Value).ToString(@"h\:mm") : null + )).ToList(); + + _floors = _rooms.Select(r => r.Floor).Distinct().ToArray(); + if (_floors.Length > 0) + _activeFloor = _floors[0]; + } + catch + { + _loadError = true; + } + finally + { + _isLoading = false; + } + } private IEnumerable GetZonesForFloor(string floor) => _rooms.Where(r => r.Floor == floor).Select(r => r.Zone).Distinct(); diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomReset.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomReset.razor index 97b2d8e5..7c15cd5f 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomReset.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomReset.razor @@ -120,6 +120,8 @@ @code { + // EN: Static UI configuration — does not require DB data / VI: Cấu hình UI tĩnh — không cần dữ liệu từ DB + // EN: Checklist items / VI: Các mục kiểm tra private readonly List _checkItems = new() { diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomSelect.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomSelect.razor index 5cedfd7f..cc01ef71 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomSelect.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomSelect.razor @@ -5,10 +5,25 @@ @page "/pos/karaoke/room-select" @layout PosLayout @inherits PosBase +@inject WebClientTpos.Client.Services.PosDataService DataService
@* ═══ ROOM SELECTION (LEFT) / CHỌN PHÒNG (TRÁI) ═══ *@
+ @if (_isLoading) + { +
+ Đang tải... +
+ } + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + { @* EN: Header / VI: Tiêu đề *@
- @item.Qty - -
+
+ Đang tải...
} + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + { + @foreach (var item in _fnbItems) + { +
+
+ @item.Name + @FormatPrice(item.Price) +
+
+ + @item.Qty + +
+
+ } + }
@code { + // EN: Karaoke shop ID / VI: ID cửa hàng karaoke + private static readonly Guid KaraokeShopId = Guid.Parse("b0000003-0000-0000-0000-000000000003"); + + // EN: Loading state / VI: Trạng thái tải + private bool _isLoading = true; + private bool _loadError; + // EN: UI toggles / VI: Bật tắt giao diện private bool _showExtend; private bool _showEnd; - // EN: Demo F&B items / VI: Mục F&B mẫu - private readonly List _fnbItems = new() + // EN: F&B items loaded from DB / VI: Mục F&B tải từ DB + private List _fnbItems = new(); + + protected override async Task OnInitializedAsync() { - new("Bia Tiger lon", 35_000, 6), new("Trái cây dĩa lớn", 150_000, 1), - new("Khô mực nướng", 85_000, 2), new("Coca-Cola", 20_000, 4), - new("Đậu phộng rang", 30_000, 2), new("Nước suối", 10_000, 3), - }; + try + { + var products = await DataService.GetProductsAsync(KaraokeShopId); + + _fnbItems = products.Take(6).Select(p => new FnbItem(p.Name, p.Price, 1)).ToList(); + } + catch + { + _loadError = true; + } + finally + { + _isLoading = false; + } + } private class FnbItem(string name, decimal price, int qty) { diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/ServiceDisplay.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/ServiceDisplay.razor index d6cafbcd..fabbdb14 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/ServiceDisplay.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/ServiceDisplay.razor @@ -5,6 +5,7 @@ @page "/pos/karaoke/service-display" @layout PosLayout @inherits PosBase +@inject WebClientTpos.Client.Services.PosDataService DataService
@* ═══ HEADER / TIÊU ĐỀ ═══ *@ @@ -37,6 +38,20 @@
+ @if (_isLoading) + { +
+ Đang tải... +
+ } + else if (_loadError) + { +
+ Không thể tải dữ liệu +
+ } + else + { @* ═══ SERVICE QUEUE / HÀNG ĐỢI PHỤC VỤ ═══ *@
@* EN: Summary cards / VI: Thẻ tóm tắt *@ @@ -120,13 +135,24 @@ }
+ } @code { + // EN: Karaoke shop ID / VI: ID cửa hàng karaoke + private static readonly Guid KaraokeShopId = Guid.Parse("b0000003-0000-0000-0000-000000000003"); + + // EN: Loading state / VI: Trạng thái tải + private bool _isLoading = true; + private bool _loadError; + // EN: Filter / VI: Bộ lọc private string _activeFilter = "Tất cả"; private readonly string[] _filters = { "Tất cả", "Chờ xử lý", "Đang xử lý", "Hoàn thành" }; + // EN: Room names loaded from DB for display / VI: Tên phòng tải từ DB để hiển thị + private List _roomNames = new(); + // EN: Service requests / VI: Yêu cầu phục vụ private readonly List _requests = new() { @@ -140,6 +166,23 @@ new("VIP 3","Dọn dẹp","Thay khăn lạnh","19:40","40 phút trước","completed"), }; + protected override async Task OnInitializedAsync() + { + try + { + var tables = await DataService.GetTablesAsync(KaraokeShopId); + _roomNames = tables.Select(t => t.TableNumber).ToList(); + } + catch + { + _loadError = true; + } + finally + { + _isLoading = false; + } + } + private IEnumerable FilteredRequests => _activeFilter switch { "Chờ xử lý" => _requests.Where(r => r.Status == "pending"),