refactor(tpos): replace mock data with PosDataService in 25 Cafe/Karaoke Razor files
- Cafe DB files (3): CafeTablet, CafeMobile, MenuManagement - Inject PosDataService, load products/categories via GetProductsAsync/GetCategoriesAsync - Add loading/error UI states, replace hardcoded product lists - Karaoke DB files (8): KaraokeDesktop, KaraokeTablet, KaraokeMobile, OrderFnb, RoomMap, RoomSelect, RoomSession, ServiceDisplay - Inject PosDataService, load rooms via GetTablesAsync, products via GetProductsAsync - Map TableNumber→room name, Zone→floor, Capacity→people, Status→room status - Add loading/error UI states - Static comment files (14): BaristaQueue, CafeJourney, CustomerDisplay, DailyReport, LoyaltyStamp, MilkFoamOptions, OrderCustomize, QueueDisplay, HappyHour, KaraokeJourney, MemberCard, PeakWarning, RoomExtend, RoomReset - Added bilingual static UI configuration comment at top of @code block All changes follow the CafeDesktop.razor refactoring pattern. Build passes with 0 errors. Co-authored-by: Velik <hongochai10@users.noreply.github.com>
This commit is contained in:
@@ -5,8 +5,23 @@
|
||||
@page "/pos/cafe/mobile"
|
||||
@layout PosLayout
|
||||
@inherits PosBase
|
||||
@inject WebClientTpos.Client.Services.PosDataService DataService
|
||||
|
||||
<div style="display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden;">
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* EN: Category tabs / VI: Tab danh mục *@
|
||||
<div class="pos-category-tabs" style="padding:8px 12px;">
|
||||
@foreach (var cat in _categories)
|
||||
@@ -89,32 +104,67 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@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<Product> _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<Product> _products = new();
|
||||
|
||||
// EN: Cart items / VI: Mục giỏ hàng
|
||||
private readonly List<CartItem> _cartItems = new();
|
||||
private IEnumerable<Product> 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);
|
||||
|
||||
@@ -5,9 +5,24 @@
|
||||
@page "/pos/cafe/tablet"
|
||||
@layout PosLayout
|
||||
@inherits PosBase
|
||||
@inject WebClientTpos.Client.Services.PosDataService DataService
|
||||
|
||||
@* ═══ PRODUCT PANEL ═══ *@
|
||||
<div class="pos-product-panel">
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="pos-category-tabs">
|
||||
@foreach (var cat in _categories)
|
||||
{
|
||||
@@ -31,6 +46,7 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@* ═══ CART SIDEBAR ═══ *@
|
||||
@@ -72,29 +88,62 @@
|
||||
</div>
|
||||
|
||||
@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<Product> _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<Product> _products = new();
|
||||
|
||||
// EN: Cart items / VI: Mục giỏ hàng
|
||||
private readonly List<CartItem> _cartItems = new();
|
||||
private IEnumerable<Product> 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);
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
</div>
|
||||
|
||||
@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<QueueColumn> _columns = new()
|
||||
{
|
||||
|
||||
@@ -228,6 +228,8 @@
|
||||
</style>
|
||||
|
||||
@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
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
</div>
|
||||
|
||||
@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<DisplayItem> _orderItems = new()
|
||||
{
|
||||
|
||||
@@ -79,6 +79,8 @@
|
||||
</div>
|
||||
|
||||
@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<StatCard> _stats = new()
|
||||
{
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
</div>
|
||||
|
||||
@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;
|
||||
|
||||
|
||||
@@ -5,8 +5,23 @@
|
||||
@page "/pos/cafe/menu-management"
|
||||
@layout PosLayout
|
||||
@inherits PosBase
|
||||
@inject WebClientTpos.Client.Services.PosDataService DataService
|
||||
|
||||
<div style="display:flex;flex-direction:column;height:100%;padding:20px;gap:16px;">
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* EN: Header / VI: Tiêu đề *@
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;">
|
||||
<div>
|
||||
@@ -93,34 +108,61 @@
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@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<MenuItem> 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<MenuItem> _items = new()
|
||||
// EN: Menu items loaded from DB / VI: Danh sách menu tải từ DB
|
||||
private List<MenuItem> _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)
|
||||
{
|
||||
|
||||
@@ -123,6 +123,8 @@
|
||||
</div>
|
||||
|
||||
@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;
|
||||
|
||||
@@ -110,6 +110,8 @@
|
||||
</div>
|
||||
|
||||
@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;
|
||||
|
||||
@@ -69,6 +69,8 @@
|
||||
</style>
|
||||
|
||||
@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<QueueOrder> _preparingOrders = new()
|
||||
{
|
||||
|
||||
@@ -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) ═══ *@
|
||||
<div class="pos-product-panel" style="padding:16px;overflow-y:auto;">
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* EN: Floor/zone tabs / VI: Tab tầng/khu vực *@
|
||||
<div class="pos-category-tabs">
|
||||
@foreach (var zone in _zones)
|
||||
@@ -54,6 +69,7 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@* ═══ SESSION PANEL (RIGHT) / PANEL PHIÊN HÁT (PHẢI) ═══ *@
|
||||
@@ -131,9 +147,16 @@
|
||||
</div>
|
||||
|
||||
@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<RoomInfo> _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<RoomInfo> 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<RoomInfo> _rooms = new();
|
||||
|
||||
// EN: Demo F&B items / VI: Mục F&B mẫu
|
||||
private readonly List<FnbItem> _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<RoomInfo> 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
|
||||
|
||||
@@ -5,8 +5,23 @@
|
||||
@page "/pos/karaoke/mobile"
|
||||
@layout PosLayout
|
||||
@inherits PosBase
|
||||
@inject WebClientTpos.Client.Services.PosDataService DataService
|
||||
|
||||
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;">
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* ═══ HEADER / TIÊU ĐỀ ═══ *@
|
||||
<div style="padding:12px 16px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0;">
|
||||
<span style="font-size:18px;font-weight:700;">Phòng Karaoke</span>
|
||||
@@ -79,30 +94,56 @@
|
||||
<i data-lucide="bell" style="width:16px;height:16px;display:inline;"></i> Gọi phục vụ
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@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<RoomInfo> _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<RoomInfo> _rooms = new();
|
||||
|
||||
private IEnumerable<RoomInfo> 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
|
||||
|
||||
@@ -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) ═══ *@
|
||||
<div class="pos-product-panel" style="padding:16px;overflow-y:auto;">
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* EN: Zone filter tabs / VI: Tab lọc khu vực *@
|
||||
<div class="pos-category-tabs">
|
||||
@foreach (var zone in _zones)
|
||||
@@ -43,6 +58,7 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@* ═══ SESSION SIDEBAR / THANH PHIÊN BÊN ═══ *@
|
||||
@@ -123,26 +139,20 @@
|
||||
</div>
|
||||
|
||||
@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<RoomInfo> _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<RoomInfo> 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<RoomInfo> _rooms = new();
|
||||
|
||||
// EN: Demo F&B / VI: F&B mẫu
|
||||
private readonly List<FnbItem> _demoFnb = new()
|
||||
@@ -151,6 +161,38 @@
|
||||
new("Đậu phộng", 30_000, 2),
|
||||
};
|
||||
|
||||
private IEnumerable<RoomInfo> 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)",
|
||||
|
||||
@@ -100,6 +100,8 @@
|
||||
</div>
|
||||
|
||||
@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<TimeRate> _timeRates = new()
|
||||
{
|
||||
|
||||
@@ -267,6 +267,8 @@
|
||||
</div>
|
||||
|
||||
@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;
|
||||
|
||||
@@ -152,6 +152,8 @@
|
||||
</div>
|
||||
|
||||
@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;
|
||||
|
||||
|
||||
@@ -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) ═══ *@
|
||||
<div class="pos-product-panel">
|
||||
@@ -19,6 +20,20 @@
|
||||
<span style="font-size:12px;color:var(--pos-text-tertiary);margin-left:auto;">Phòng VIP 2</span>
|
||||
</div>
|
||||
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;flex:1;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;flex:1;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* EN: Category tabs / VI: Tab danh mục *@
|
||||
<div class="pos-category-tabs">
|
||||
@foreach (var cat in _categories)
|
||||
@@ -44,6 +59,7 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@* ═══ ORDER PANEL (RIGHT) / PANEL ĐƠN HÀNG (PHẢI) ═══ *@
|
||||
@@ -97,27 +113,19 @@
|
||||
</div>
|
||||
|
||||
@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<Product> _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<Product> _products = new();
|
||||
|
||||
// EN: Order items / VI: Mục đơn hàng
|
||||
private readonly List<OrderItem> _orderItems = new();
|
||||
@@ -125,6 +133,42 @@
|
||||
private IEnumerable<Product> 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);
|
||||
|
||||
@@ -127,6 +127,8 @@
|
||||
</div>
|
||||
|
||||
@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;
|
||||
|
||||
|
||||
@@ -147,6 +147,8 @@
|
||||
</div>
|
||||
|
||||
@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<ExtendOption> _extendOptions = new()
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
@page "/pos/karaoke/room-map"
|
||||
@layout PosLayout
|
||||
@inherits PosBase
|
||||
@inject WebClientTpos.Client.Services.PosDataService DataService
|
||||
|
||||
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;">
|
||||
@* ═══ TOOLBAR / THANH CÔNG CỤ ═══ *@
|
||||
@@ -36,6 +37,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;flex:1;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;flex:1;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* ═══ FLOOR TABS / TAB TẦNG ═══ *@
|
||||
<div class="pos-category-tabs">
|
||||
@foreach (var floor in _floors)
|
||||
@@ -90,30 +105,55 @@
|
||||
<span>Đã đặt: <b style="color:#3B82F6;">@_rooms.Count(r => r.Status == "reserved")</b></span>
|
||||
<span>Đang dọn: <b style="color:#F59E0B;">@_rooms.Count(r => r.Status == "cleaning")</b></span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@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<string>();
|
||||
private RoomInfo? _selectedRoom;
|
||||
|
||||
// EN: Demo rooms / VI: Phòng mẫu
|
||||
private readonly List<RoomInfo> _rooms = new()
|
||||
// EN: Room data loaded from DB / VI: Dữ liệu phòng tải từ DB
|
||||
private List<RoomInfo> _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<string> GetZonesForFloor(string floor) =>
|
||||
_rooms.Where(r => r.Floor == floor).Select(r => r.Zone).Distinct();
|
||||
|
||||
@@ -120,6 +120,8 @@
|
||||
</div>
|
||||
|
||||
@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<CheckItem> _checkItems = new()
|
||||
{
|
||||
|
||||
@@ -5,10 +5,25 @@
|
||||
@page "/pos/karaoke/room-select"
|
||||
@layout PosLayout
|
||||
@inherits PosBase
|
||||
@inject WebClientTpos.Client.Services.PosDataService DataService
|
||||
|
||||
<div style="flex:1;display:flex;overflow:hidden;">
|
||||
@* ═══ ROOM SELECTION (LEFT) / CHỌN PHÒNG (TRÁI) ═══ *@
|
||||
<div class="pos-product-panel" style="padding:16px;overflow-y:auto;">
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* EN: Header / VI: Tiêu đề *@
|
||||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px;">
|
||||
<button style="background:var(--pos-bg-interactive);border:none;color:var(--pos-text-primary);
|
||||
@@ -70,6 +85,7 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@* ═══ SESSION SETUP (RIGHT) / THIẾT LẬP PHIÊN (PHẢI) ═══ *@
|
||||
@@ -150,26 +166,54 @@
|
||||
</div>
|
||||
|
||||
@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: Filters / VI: Bộ lọc
|
||||
private string _activeType = "Tất cả";
|
||||
private readonly string[] _types = { "Tất cả", "Standard", "VIP", "Deluxe" };
|
||||
private string[] _types = { "Tất cả", "Standard", "VIP", "Deluxe" };
|
||||
private readonly int[] _capacities = { 4, 8, 12, 15 };
|
||||
private int? _selectedCapacity;
|
||||
private RoomInfo? _selectedRoom;
|
||||
private int _selectedHours = 2;
|
||||
private int _guests = 4;
|
||||
|
||||
// EN: Available rooms / VI: Phòng trống
|
||||
private readonly List<RoomInfo> _rooms = new()
|
||||
// EN: Available rooms loaded from DB / VI: Phòng trống tải từ DB
|
||||
private List<RoomInfo> _rooms = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
new("R01","Phòng 101",8,"Standard",80_000,"Tầng 1"),
|
||||
new("R04","Phòng 104",4,"Standard",60_000,"Tầng 1"),
|
||||
new("R05","Phòng 201",12,"VIP",150_000,"Tầng 2"),
|
||||
new("R07","Phòng 203",6,"Standard",80_000,"Tầng 2"),
|
||||
new("R08","VIP 1",15,"Deluxe",200_000,"VIP"),
|
||||
new("R11","VIP 3",20,"Deluxe",250_000,"VIP"),
|
||||
new("R09","Phòng 204",6,"Standard",70_000,"Tầng 2"),
|
||||
};
|
||||
try
|
||||
{
|
||||
var tables = await DataService.GetTablesAsync(KaraokeShopId);
|
||||
|
||||
_rooms = tables
|
||||
.Where(t => t.Status == "available")
|
||||
.Select(t => new RoomInfo(
|
||||
t.Id.ToString(),
|
||||
t.TableNumber,
|
||||
t.Capacity,
|
||||
t.Zone ?? "Standard",
|
||||
100_000,
|
||||
t.Zone ?? "Tầng 1"
|
||||
)).ToList();
|
||||
|
||||
var roomTypes = _rooms.Select(r => r.Type).Distinct().ToList();
|
||||
_types = new[] { "Tất cả" }.Concat(roomTypes).ToArray();
|
||||
}
|
||||
catch
|
||||
{
|
||||
_loadError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<RoomInfo> FilteredRooms
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
@page "/pos/karaoke/room-session"
|
||||
@layout PosLayout
|
||||
@inherits PosBase
|
||||
@inject WebClientTpos.Client.Services.PosDataService DataService
|
||||
|
||||
<div style="flex:1;display:flex;overflow:hidden;">
|
||||
@* ═══ SESSION DETAILS (LEFT) / CHI TIẾT PHIÊN (TRÁI) ═══ *@
|
||||
@@ -108,20 +109,35 @@
|
||||
</div>
|
||||
|
||||
<div class="pos-cart-items">
|
||||
@foreach (var item in _fnbItems)
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div class="pos-cart-item">
|
||||
<div class="pos-cart-item__info">
|
||||
<span class="pos-cart-item__name">@item.Name</span>
|
||||
<span class="pos-cart-item__price">@FormatPrice(item.Price)</span>
|
||||
</div>
|
||||
<div class="pos-cart-item__qty">
|
||||
<button @onclick="() => item.Qty = Math.Max(0, item.Qty - 1)">−</button>
|
||||
<span style="font-size:14px;font-weight:600;min-width:20px;text-align:center;">@item.Qty</span>
|
||||
<button @onclick="() => item.Qty++">+</button>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;justify-content:center;padding:32px;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;padding:32px;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var item in _fnbItems)
|
||||
{
|
||||
<div class="pos-cart-item">
|
||||
<div class="pos-cart-item__info">
|
||||
<span class="pos-cart-item__name">@item.Name</span>
|
||||
<span class="pos-cart-item__price">@FormatPrice(item.Price)</span>
|
||||
</div>
|
||||
<div class="pos-cart-item__qty">
|
||||
<button @onclick="() => item.Qty = Math.Max(0, item.Qty - 1)">−</button>
|
||||
<span style="font-size:14px;font-weight:600;min-width:20px;text-align:center;">@item.Qty</span>
|
||||
<button @onclick="() => item.Qty++">+</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="pos-cart-footer">
|
||||
@@ -145,17 +161,37 @@
|
||||
</div>
|
||||
|
||||
@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<FnbItem> _fnbItems = new()
|
||||
// EN: F&B items loaded from DB / VI: Mục F&B tải từ DB
|
||||
private List<FnbItem> _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)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
@page "/pos/karaoke/service-display"
|
||||
@layout PosLayout
|
||||
@inherits PosBase
|
||||
@inject WebClientTpos.Client.Services.PosDataService DataService
|
||||
|
||||
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;">
|
||||
@* ═══ HEADER / TIÊU ĐỀ ═══ *@
|
||||
@@ -37,6 +38,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (_isLoading)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;flex:1;color:var(--pos-text-tertiary);">
|
||||
Đang tải...
|
||||
</div>
|
||||
}
|
||||
else if (_loadError)
|
||||
{
|
||||
<div style="display:flex;align-items:center;justify-content:center;flex:1;color:var(--pos-text-tertiary);">
|
||||
Không thể tải dữ liệu
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* ═══ SERVICE QUEUE / HÀNG ĐỢI PHỤC VỤ ═══ *@
|
||||
<div style="flex:1;overflow-y:auto;padding:16px;">
|
||||
@* EN: Summary cards / VI: Thẻ tóm tắt *@
|
||||
@@ -120,13 +135,24 @@
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@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<string> _roomNames = new();
|
||||
|
||||
// EN: Service requests / VI: Yêu cầu phục vụ
|
||||
private readonly List<ServiceRequest> _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<ServiceRequest> FilteredRequests => _activeFilter switch
|
||||
{
|
||||
"Chờ xử lý" => _requests.Where(r => r.Status == "pending"),
|
||||
|
||||
Reference in New Issue
Block a user