@* EN: POS terminal layout — Responsive full-screen layout with status bar + content. Desktop: Full sidebar + content. Tablet: Collapsible order drawer. Mobile: Bottom nav + full-screen overlay. VI: Layout POS — Layout toàn màn hình responsive với thanh trạng thái + nội dung. Desktop: Sidebar đầy đủ + nội dung. Tablet: Drawer đơn hàng thu gọn. Mobile: Nav dưới + overlay toàn màn hình. Design: pencil-design/src/pages/tPOS/pos/cafe/desktop.pen *@ @inherits LayoutComponentBase @implements IDisposable @inject IStringLocalizer L @inject NavigationManager NavigationManager @inject WebClientTpos.Client.Services.PosDataService DataService @inject IJSRuntime JS @inject WebClientTpos.Client.Services.AuthStateService AuthState @* EN: Theme providers moved to App.razor (FRONT-W-06) — DefaultDark is the ThemeStateService default. *@ @* VI: Theme providers đã chuyển về App.razor (FRONT-W-06) — DefaultDark là mặc định của ThemeStateService. *@
@* ═══ STATUS BAR ═══ *@
@* EN: Hamburger menu — visible on tablet/mobile only / VI: Menu hamburger — chi hien thi tren tablet/mobile *@ @StoreName
Online
@_currentTime @* EN: Order panel toggle — visible on tablet/mobile when order panel is hidden VI: Nút mở panel đơn hàng — hiện trên tablet/mobile khi panel đơn hàng ẩn *@
@* ═══ MAIN CONTENT ═══ *@
@* EN: Mobile sidebar overlay / VI: Overlay sidebar trên mobile *@ @if (_sidebarOpen) {
} @* EN: Sidebar navigation — collapsible on tablet/mobile VI: Sidebar điều hướng — thu gọn trên tablet/mobile *@ @* EN: Page content area / VI: Vùng nội dung trang *@
@Body
@* EN: Order panel drawer — slides in from right on tablet/mobile VI: Drawer panel đơn hàng — trượt vào từ phải trên tablet/mobile *@ @if (_orderPanelOpen) {
}
Đơn hàng
@* EN: Order panel content is rendered by child pages via RenderFragment VI: Nội dung panel đơn hàng được render bởi trang con qua RenderFragment *@ @if (OrderPanelContent is not null) { @OrderPanelContent } else {
Chưa có đơn hàng
}
@code { private string StoreName { get; set; } = "aPOS POS"; private string _currentTime = DateTime.Now.ToString("HH:mm"); private Timer? _timer; private string _shopIdStr = ""; // EN: Responsive state / VI: Trạng thái responsive private bool _sidebarOpen; private bool _orderPanelOpen; private int _orderCount; /// /// EN: Optional order panel content — set by child POS pages via CascadingValue. /// VI: Nội dung panel đơn hàng tùy chọn — set bởi trang con POS qua CascadingValue. /// public RenderFragment? OrderPanelContent { get; set; } /// /// EN: Set order count badge on the order toggle button. /// VI: Đặt badge số lượng đơn hàng trên nút toggle đơn hàng. /// public void SetOrderCount(int count) { _orderCount = count; StateHasChanged(); } /// /// EN: Open order panel drawer (tablet/mobile). /// VI: Mở drawer panel đơn hàng (tablet/mobile). /// public void OpenOrderPanel() { _orderPanelOpen = true; StateHasChanged(); } protected override async Task OnInitializedAsync() { // EN: Update clock every 30 seconds / VI: Cập nhật đồng hồ mỗi 30 giây _timer = new Timer(_ => { _currentTime = DateTime.Now.ToString("HH:mm"); InvokeAsync(StateHasChanged); }, null, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30)); // EN: Extract shopId from URL path: /pos/{shopId}/... // VI: Trích xuất shopId từ URL: /pos/{shopId}/... try { var uri = new Uri(NavigationManager.Uri); var segments = uri.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries); // Expected: ["pos", "{shopId}", "cafe"|"restaurant"|...] if (segments.Length >= 2 && Guid.TryParse(segments[1], out var shopId)) { _shopIdStr = shopId.ToString(); var shop = await DataService.GetShopByIdAsync(shopId); if (shop != null) StoreName = shop.Name; } } catch { // EN: Fallback to default name / VI: Dùng tên mặc định nếu lỗi } } protected override async Task OnAfterRenderAsync(bool firstRender) { // EN: Re-init Lucide icons after every render (Blazor navigation replaces DOM) // VI: Khởi tạo lại Lucide icons sau mỗi lần render try { await JS.InvokeVoidAsync("lucide.createIcons"); } catch { } } private void GoToPortal() { // EN: Navigate to shop admin page if shopId is available, otherwise fallback to portal URL // VI: Điều hướng đến trang admin shop nếu có shopId, nếu không fallback về portal URL if (!string.IsNullOrEmpty(_shopIdStr)) NavigationManager.NavigateTo($"/admin/shop/{_shopIdStr}/overview"); else NavigationManager.NavigateTo(AuthState.GetPortalUrl()); } private void ToggleSidebar() => _sidebarOpen = !_sidebarOpen; private void CloseSidebar() => _sidebarOpen = false; private void ToggleOrderPanel() => _orderPanelOpen = !_orderPanelOpen; private void CloseOrderPanel() => _orderPanelOpen = false; public void Dispose() => _timer?.Dispose(); }