-
-
+ @switch (_section)
+ {
+ // ═══ OVERVIEW ═══
+ case "overview":
+
-
@_sectionTitle
-
- @_sectionDescription
-
- @if (_hasQuickStats)
+ break;
+
+ // ═══ MENU / PRODUCTS ═══
+ case "menu":
+ case "products":
+ @if (!_products.Any())
{
-
- @foreach (var stat in _quickStats)
+ @RenderEmpty("coffee", "#F59E0B", "Chưa có sản phẩm", "Thêm sản phẩm để bắt đầu bán hàng")
+ }
+ else
+ {
+
+ @foreach (var p in _products)
{
-
-
@stat.Value
-
@stat.Label
+
+
+
+
@p.Name
+
@(p.CategoryName ?? "—")
+
@p.Price.ToString("N0")₫
+
}
}
-
- Tính năng này sẽ được kích hoạt khi có dữ liệu từ hệ thống
-
-
-
+ break;
+
+ // ═══ INVENTORY ═══
+ case "inventory":
+ @if (!_inventory.Any())
+ {
+ @RenderEmpty("warehouse", "#3B82F6", "Chưa có tồn kho", "Tồn kho sẽ hiển thị khi có sản phẩm")
+ }
+ else
+ {
+
+
@_inventory.Count(i => i.Quantity > 10)Còn hàng
+
@_inventory.Count(i => i.Quantity > 0 && i.Quantity <= 10)Sắp hết
+
@_inventory.Count(i => i.Quantity <= 0)Hết hàng
+
+
+
+
+ | Sản phẩm |
+ Số lượng |
+ Mức nhập lại |
+
+ @foreach (var item in _inventory)
+ {
+
+ | @(item.ProductName ?? item.ProductId.ToString()[..8]) |
+ @item.Quantity |
+ @item.ReorderLevel |
+
+ }
+
+
+
+ }
+ break;
+
+ // ═══ FINANCE ═══
+ case "finance":
+
+
@FormatVND(_orders.Sum(o => o.TotalAmount))Tổng doanh thu
+
+
@FormatVND(_orders.Any() ? _orders.Average(o => o.TotalAmount) : 0)TB/đơn
+
+ @if (!_orders.Any())
+ {
+ @RenderEmpty("bar-chart-3", "#22C55E", "Chưa có dữ liệu tài chính", "Dữ liệu sẽ tự động cập nhật khi có đơn hàng")
+ }
+ else
+ {
+
+
+
+
+ | ID |
+ Số tiền |
+ Trạng thái |
+ Ngày |
+
+ @foreach (var o in _orders.Take(20))
+ {
+
+ | @o.Id.ToString()[..8] |
+ @FormatVND(o.TotalAmount) |
+ @(o.Status ?? "—") |
+ @o.CreatedAt.ToString("dd/MM HH:mm") |
+
+ }
+
+
+
+ }
+ break;
+
+ // ═══ STAFF ═══
+ case "staff":
+ @if (!_staff.Any())
+ {
+ @RenderEmpty("users", "#8B5CF6", "Chưa có nhân viên", "Thêm nhân viên để quản lý cửa hàng")
+ }
+ else
+ {
+
+
@_staff.Count(s => s.Status == "Active")Đang hoạt động
+
@_staff.CountTổng nhân viên
+
+
+
+
+ | Mã NV |
+ Vai trò |
+ Trạng thái |
+ Cửa hàng |
+
+ @foreach (var s in _staff)
+ {
+
+ | @(s.EmployeeCode ?? s.Id.ToString()[..6]) |
+ @(s.Role ?? "—") |
+ @(s.Status ?? "—") |
+ @(s.ShopName ?? "—") |
+
+ }
+
+
+
+ }
+ break;
+
+ // ═══ CUSTOMERS ═══
+ case "customers":
+ @if (!_members.Any())
+ {
+ @RenderEmpty("heart", "#EF4444", "Chưa có khách hàng", "Khách hàng sẽ hiển thị khi có giao dịch")
+ }
+ else
+ {
+
+
@_members.CountTổng khách hàng
+
+
+
+
+ | ID |
+ Cấp bậc |
+ EXP |
+ Ngày tham gia |
+
+ @foreach (var m in _members)
+ {
+
+ | @m.Id.ToString()[..8] |
+ @(m.LevelName ?? "—") |
+ @m.TotalExpEarned.ToString("N0") |
+ @m.CreatedAt.ToString("dd/MM/yyyy") |
+
+ }
+
+
+
+ }
+ break;
+
+ // ═══ PLACEHOLDER SECTIONS (POS, Tables, Kitchen, Rooms, Appointments, Services, Reports) ═══
+ default:
+
+
+
+
+
+
@_sectionTitle
+
+ @_sectionDescription
+
+
+ Tính năng này sẽ được kích hoạt khi có dữ liệu từ hệ thống
+
+
+
+ break;
+ }
}
@@ -82,21 +236,40 @@
private string _shopName = "";
private string _verticalLabel = "";
+ private string _section = "";
private string _sectionTitle = "";
private string _sectionIcon = "layout-dashboard";
private string _sectionDescription = "";
- private bool _hasQuickStats = false;
- private List<(string Value, string Label)> _quickStats = new();
- private List<(string Icon, string Label)> _sectionActions = new();
+ private Guid? _shopGuid;
- protected override async Task OnInitializedAsync()
+ // ═══ DATA ═══
+ private List
_products = new();
+ private List _inventory = new();
+ private List _orders = new();
+ private List _staff = new();
+ private List _members = new();
+
+ protected override async Task OnInitializedAsync() => await LoadData();
+
+ protected override async Task OnParametersSetAsync()
+ {
+ // EN: Called when URL params change / VI: Gọi khi params URL thay đổi
+ if (_section != (Section?.ToLowerInvariant() ?? ""))
+ await LoadData();
+ }
+
+ private async Task LoadData()
{
IsLoading = true;
+ _section = Section?.ToLowerInvariant() ?? "";
+ ConfigureSection();
+
try
{
- if (Guid.TryParse(ShopId, out var id))
+ _shopGuid = Guid.TryParse(ShopId, out var id) ? id : null;
+ if (_shopGuid.HasValue)
{
- var shop = await DataService.GetShopByIdAsync(id);
+ var shop = await DataService.GetShopByIdAsync(_shopGuid.Value);
if (shop != null)
{
_shopName = shop.Name ?? "Cửa hàng";
@@ -104,110 +277,78 @@
Layout?.SetShopContext(ShopId, _shopName, shop.Category);
}
}
- ConfigureSection();
+
+ // EN: Load only data needed for current section / VI: Chỉ tải data cần cho section hiện tại
+ switch (_section)
+ {
+ case "overview":
+ _products = await DataService.GetAllProductsAsync(_shopGuid);
+ _inventory = await DataService.GetInventoryAsync(_shopGuid);
+ _orders = await DataService.GetOrdersAsync(_shopGuid);
+ _staff = await DataService.GetStaffAsync();
+ break;
+ case "menu":
+ case "products":
+ _products = await DataService.GetAllProductsAsync(_shopGuid);
+ break;
+ case "inventory":
+ _inventory = await DataService.GetInventoryAsync(_shopGuid);
+ break;
+ case "finance":
+ _orders = await DataService.GetOrdersAsync(_shopGuid);
+ break;
+ case "staff":
+ _staff = await DataService.GetStaffAsync();
+ break;
+ case "customers":
+ _members = await DataService.GetMembersAsync();
+ break;
+ }
}
catch { }
finally { IsLoading = false; }
}
- protected override void OnParametersSet()
- {
- ConfigureSection();
- }
-
- ///
- /// EN: Configure section-specific title, icon, description, and quick stats.
- /// VI: Cấu hình tiêu đề, icon, mô tả, thống kê nhanh theo section.
- ///
private void ConfigureSection()
{
- var sec = Section?.ToLowerInvariant() ?? "";
-
- // EN: Reset defaults
- // VI: Đặt lại giá trị mặc định
- _quickStats = new();
- _sectionActions = new();
-
- switch (sec)
+ switch (_section)
{
- case "pos":
- _sectionTitle = "POS Bán hàng";
- _sectionIcon = "monitor";
- _sectionDescription = "Mở giao diện bán hàng tại điểm để phục vụ khách hàng nhanh chóng.";
- _sectionActions = new() { ("monitor", "Mở POS") };
- break;
- case "menu":
- _sectionTitle = "Quản lý Menu";
- _sectionIcon = "coffee";
- _sectionDescription = "Quản lý danh mục, món/sản phẩm, giá, tùy chọn thêm cho cửa hàng.";
- _quickStats = new() { ("0", "Danh mục"), ("0", "Sản phẩm"), ("0", "Topping") };
- _sectionActions = new() { ("plus", "Thêm sản phẩm") };
- break;
- case "tables":
- _sectionTitle = "Quản lý Bàn";
- _sectionIcon = "grid-3x3";
- _sectionDescription = "Thiết lập sơ đồ bàn, khu vực phục vụ cho nhà hàng.";
- _quickStats = new() { ("0", "Bàn"), ("0", "Khu vực") };
- _sectionActions = new() { ("plus", "Thêm bàn") };
- break;
- case "kitchen":
- _sectionTitle = "Bếp (Kitchen Display)";
- _sectionIcon = "flame";
- _sectionDescription = "Màn hình hiển thị đơn cho bếp, quản lý tiến độ chế biến.";
- break;
- case "rooms":
- _sectionTitle = "Quản lý Phòng";
- _sectionIcon = "door-open";
- _sectionDescription = "Thiết lập loại phòng, giá theo giờ, trạng thái phòng karaoke.";
- _quickStats = new() { ("0", "Phòng"), ("0", "Loại phòng") };
- _sectionActions = new() { ("plus", "Thêm phòng") };
- break;
- case "appointments":
- _sectionTitle = "Lịch hẹn";
- _sectionIcon = "calendar";
- _sectionDescription = "Quản lý lịch hẹn khách hàng, phân công nhân viên phục vụ.";
- _quickStats = new() { ("0", "Hôm nay"), ("0", "Tuần này") };
- _sectionActions = new() { ("plus", "Tạo lịch hẹn") };
- break;
- case "services":
- _sectionTitle = "Dịch vụ";
- _sectionIcon = "sparkles";
- _sectionDescription = "Quản lý danh mục dịch vụ, giá, thời gian thực hiện.";
- _quickStats = new() { ("0", "Dịch vụ"), ("0", "Gói combo") };
- _sectionActions = new() { ("plus", "Thêm dịch vụ") };
- break;
- case "inventory":
- _sectionTitle = "Tồn kho";
- _sectionIcon = "warehouse";
- _sectionDescription = "Theo dõi nguyên liệu, hàng tồn kho, cảnh báo hết hàng.";
- _quickStats = new() { ("0", "Nguyên liệu"), ("0", "Cần nhập") };
- break;
- case "staff":
- _sectionTitle = "Nhân sự";
- _sectionIcon = "users";
- _sectionDescription = "Quản lý nhân viên cửa hàng, ca làm việc, phân công.";
- _quickStats = new() { ("0", "Nhân viên"), ("0", "Ca hôm nay") };
- _sectionActions = new() { ("plus", "Thêm nhân viên") };
- break;
- case "customers":
- _sectionTitle = "Khách hàng";
- _sectionIcon = "heart";
- _sectionDescription = "Danh sách khách hàng, lịch sử mua hàng, tích điểm.";
- _quickStats = new() { ("0", "Khách hàng"), ("0", "Thành viên") };
- break;
- case "reports":
- _sectionTitle = "Báo cáo";
- _sectionIcon = "bar-chart-2";
- _sectionDescription = "Doanh thu, đơn hàng, sản phẩm bán chạy, hiệu suất nhân viên.";
- _sectionActions = new() { ("download", "Xuất báo cáo") };
- break;
- default:
- _sectionTitle = Section ?? "Trang";
- _sectionIcon = "layout-dashboard";
- _sectionDescription = "Trang này đang được phát triển.";
- break;
+ case "overview": _sectionTitle = "Tổng quan"; _sectionIcon = "layout-dashboard"; _sectionDescription = "Tổng quan hoạt động cửa hàng."; break;
+ case "menu": _sectionTitle = "Menu / Sản phẩm"; _sectionIcon = "coffee"; _sectionDescription = "Quản lý danh mục, sản phẩm, giá."; break;
+ case "products": _sectionTitle = "Sản phẩm"; _sectionIcon = "package"; _sectionDescription = "Quản lý sản phẩm."; break;
+ case "inventory": _sectionTitle = "Tồn kho"; _sectionIcon = "warehouse"; _sectionDescription = "Theo dõi tồn kho, cảnh báo hết hàng."; break;
+ case "finance": _sectionTitle = "Tài chính"; _sectionIcon = "trending-up"; _sectionDescription = "Doanh thu, đơn hàng, chi phí."; break;
+ case "staff": _sectionTitle = "Nhân sự"; _sectionIcon = "users"; _sectionDescription = "Quản lý nhân viên cửa hàng."; break;
+ case "customers": _sectionTitle = "Khách hàng"; _sectionIcon = "heart"; _sectionDescription = "Khách hàng, thành viên."; break;
+ case "pos": _sectionTitle = "POS Bán hàng"; _sectionIcon = "monitor"; _sectionDescription = "Mở giao diện bán hàng tại điểm."; break;
+ case "tables": _sectionTitle = "Quản lý Bàn"; _sectionIcon = "grid-3x3"; _sectionDescription = "Sơ đồ bàn, khu vực phục vụ."; break;
+ case "kitchen": _sectionTitle = "Bếp (Kitchen)"; _sectionIcon = "flame"; _sectionDescription = "Màn hình hiển thị đơn cho bếp."; break;
+ case "rooms": _sectionTitle = "Phòng"; _sectionIcon = "door-open"; _sectionDescription = "Quản lý phòng karaoke."; break;
+ case "appointments": _sectionTitle = "Lịch hẹn"; _sectionIcon = "calendar"; _sectionDescription = "Quản lý lịch hẹn khách hàng."; break;
+ case "services": _sectionTitle = "Dịch vụ"; _sectionIcon = "sparkles"; _sectionDescription = "Quản lý danh mục dịch vụ."; break;
+ case "reports": _sectionTitle = "Báo cáo"; _sectionIcon = "bar-chart-2"; _sectionDescription = "Doanh thu, sản phẩm bán chạy."; break;
+ default: _sectionTitle = Section ?? "Trang"; _sectionIcon = "layout-dashboard"; _sectionDescription = "Trang đang phát triển."; break;
}
+ }
- _hasQuickStats = _quickStats.Any();
+ private static string FormatVND(decimal val) => val.ToString("N0") + "₫";
+
+ // EN: Reusable empty state renderer / VI: Renderer trạng thái trống tái sử dụng
+ private RenderFragment RenderEmpty(string icon, string color, string title, string desc) => __builder =>
+ {
+
+ };
+
+ private static string HexToRgb(string hex)
+ {
+ hex = hex.TrimStart('#');
+ if (hex.Length != 6) return "0,0,0";
+ return $"{Convert.ToInt32(hex[..2], 16)},{Convert.ToInt32(hex[2..4], 16)},{Convert.ToInt32(hex[4..], 16)}";
}
}
diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/ShopSidebarConfig.cs b/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/ShopSidebarConfig.cs
index 348b7fbb..39eb92ef 100644
--- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/ShopSidebarConfig.cs
+++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Services/ShopSidebarConfig.cs
@@ -6,6 +6,13 @@ namespace WebClientTpos.Client.Services;
///
/// EN: Static config for shop-level sidebar menus per vertical type.
/// VI: Cấu hình tĩnh cho menu sidebar cấp cửa hàng theo loại ngành hàng.
+///
+/// Mỗi ngành hàng có menu khác nhau:
+/// - Café: Menu đồ uống, tồn kho nguyên liệu
+/// - Restaurant: Menu món ăn + Bàn + Bếp
+/// - Karaoke: Phòng + Menu bar
+/// - Spa: Lịch hẹn + Dịch vụ
+/// Tất cả đều có: Tài chính, Nhân sự, Khách hàng, Báo cáo
///
public static class ShopSidebarConfig
{
@@ -17,14 +24,7 @@ public static class ShopSidebarConfig
///
public static List