diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Layout/AdminLayout.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Layout/AdminLayout.razor index beccc58a..bc7a5fb5 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Layout/AdminLayout.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Layout/AdminLayout.razor @@ -153,7 +153,9 @@ @* ═══ MAIN AREA ═══ *@
- @Body + + @Body +
diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Shop/ShopOverview.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Shop/ShopOverview.razor new file mode 100644 index 00000000..b384caaf --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Shop/ShopOverview.razor @@ -0,0 +1,251 @@ +@page "/admin/shop/{ShopId}/overview" +@layout AdminLayout +@inherits AdminBase +@inject PosDataService DataService +@using WebClientTpos.Client.Services + +@* + EN: Shop overview — KPIs and info when inside a shop context. + Triggers AdminLayout._isShopContext → per-vertical sidebar. + VI: Tổng quan cửa hàng — KPI khi đang trong context cửa hàng. + Kích hoạt AdminLayout._isShopContext → sidebar theo ngành hàng. +*@ + +@(_shop?.Name ?? "Cửa hàng") — GoodGo Admin + +@* ═══ TOP BAR ═══ *@ +
+
+

Tổng quan

+

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

+
+
+ @if (_shop != null) + { +
+ + @GetStatusLabel(_shop.Status) +
+ } + +
+
+ +@* ═══ CONTENT ═══ *@ +
+ + @if (IsLoading) + { +
+
+

Đang tải thông tin cửa hàng...

+
+ } + else if (_shop == null) + { +
+ +

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

+

Cửa hàng không tồn tại hoặc đã bị xóa.

+ + + Quay lại danh sách + +
+ } + else + { + @* ── Store Info Card ── *@ +
+
+
+ +
+
+
@_shop.Name
+
@_shop.Slug • @ShopSidebarConfig.GetVerticalLabel(_shop.Category)
+ @if (!string.IsNullOrEmpty(_shop.Description)) + { +
@_shop.Description
+ } +
+
+ @if (!string.IsNullOrEmpty(_shop.Phone)) + { +
+ @_shop.Phone +
+ } + @if (!string.IsNullOrEmpty(_shop.Email)) + { +
+ @_shop.Email +
+ } +
+
+
+ + @* ── KPI ROW ── *@ +
+
+
+
+ +
+
+
--
+
Doanh thu tháng
+
+
+
+
+ +
+
+
--
+
Đơn hàng tháng
+
+
+
+
+ +
+
+
--
+
Giá trị TB / đơn
+
+
+
+
+ +
+
+
--
+
Đánh giá TB
+
+
+ + @* ── BOTTOM: Chart + Right Column ── *@ +
+ @* LEFT: Revenue Chart *@ +
+
+

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

+
+ + +
+
+
+
+ +

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

+

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

+
+
+
+ + @* RIGHT COLUMN *@ +
+
+
+

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

+
+
+ +

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

+
+
+ +
+
+

+ + Thông tin +

+
+
+
+ Trạng thái + @GetStatusLabel(_shop.Status) +
+
+ Ngành hàng + @ShopSidebarConfig.GetVerticalLabel(_shop.Category) +
+
+ Slug + @(_shop.Slug) +
+
+
+
+
+ } + +
+ +@code { + [Parameter] public string ShopId { get; set; } = ""; + + private PosDataService.ShopInfo? _shop; + + // EN: Cascade layout reference to set shop context for sidebar switching. + // VI: Cascade layout để set shop context cho sidebar chuyển đổi. + [CascadingParameter] public AdminLayout? Layout { get; set; } + + protected override async Task OnInitializedAsync() + { + IsLoading = true; + try + { + if (Guid.TryParse(ShopId, out var id)) + { + _shop = await DataService.GetShopByIdAsync(id); + if (_shop != null) + { + Layout?.SetShopContext(ShopId, _shop.Name ?? "Cửa hàng", _shop.Category); + } + } + } + catch { _shop = null; } + finally { IsLoading = false; } + } + + private static string GetStatusBadgeClass(string? status) => status?.ToLowerInvariant() switch + { + "published" or "active" => "online", + "draft" or "setup" => "setup", + "inactive" or "paused" => "paused", + _ => "setup" + }; + + private static string GetStatusLabel(string? status) => status?.ToLowerInvariant() switch + { + "published" or "active" => "Đang mở", + "draft" or "setup" => "Thiết lập", + "inactive" or "paused" => "Tạm dừng", + "closed" => "Đã đóng", + _ => status ?? "—" + }; + + private static string GetShopIcon(string? category) => category?.ToLowerInvariant() switch + { + "foodbeverage" or "café" or "cafe" or "coffee" => "coffee", + "restaurant" or "nhà hàng" => "utensils", + "entertainment" or "karaoke" => "mic", + "beauty" or "spa" => "sparkles", + "retail" => "shopping-bag", + _ => "store" + }; +} diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Shop/ShopPage.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Shop/ShopPage.razor new file mode 100644 index 00000000..da1c53ea --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Shop/ShopPage.razor @@ -0,0 +1,213 @@ +@page "/admin/shop/{ShopId}/{Section}" +@layout AdminLayout +@inherits AdminBase +@inject PosDataService DataService +@using WebClientTpos.Client.Services + +@* + EN: Catch-all for shop sub-pages (menu, inventory, staff, customers, etc). + Each section either shows real content or a "coming soon" placeholder. + VI: Catch-all cho các trang con cửa hàng (menu, kho, nhân sự, khách hàng...). + Mỗi section hiển thị nội dung thật hoặc placeholder "sắp ra mắt". +*@ + +@_sectionTitle — @(_shopName ?? "Cửa hàng") — GoodGo Admin + +@* ═══ TOP BAR ═══ *@ +
+
+

@_sectionTitle

+

@(_shopName ?? "Cửa hàng") • @_verticalLabel

+
+
+ @if (_sectionActions.Count > 0) + { + @foreach (var act in _sectionActions) + { + + } + } +
+
+ +@* ═══ CONTENT ═══ *@ +
+ + @if (IsLoading) + { +
+
+

Đang tải...

+
+ } + else + { + @* ── Section-specific content placeholder ── *@ +
+
+
+ +
+

@_sectionTitle

+

+ @_sectionDescription +

+ @if (_hasQuickStats) + { +
+ @foreach (var stat in _quickStats) + { +
+
@stat.Value
+
@stat.Label
+
+ } +
+ } +

+ Tính năng này sẽ được kích hoạt khi có dữ liệu từ hệ thống +

+
+
+ } +
+ +@code { + [Parameter] public string ShopId { get; set; } = ""; + [Parameter] public string Section { get; set; } = ""; + [CascadingParameter] public AdminLayout? Layout { get; set; } + + private string _shopName = ""; + private string _verticalLabel = ""; + 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(); + + protected override async Task OnInitializedAsync() + { + IsLoading = true; + try + { + if (Guid.TryParse(ShopId, out var id)) + { + var shop = await DataService.GetShopByIdAsync(id); + if (shop != null) + { + _shopName = shop.Name ?? "Cửa hàng"; + _verticalLabel = ShopSidebarConfig.GetVerticalLabel(shop.Category); + Layout?.SetShopContext(ShopId, _shopName, shop.Category); + } + } + ConfigureSection(); + } + 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) + { + 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; + } + + _hasQuickStats = _quickStats.Any(); + } +} diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreList.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreList.razor index 5caa285f..f062506b 100644 --- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreList.razor +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/Store/StoreList.razor @@ -97,7 +97,7 @@ @foreach (var shop in FilteredShops) { var isPaused = IsPaused(shop.Status); -
+