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 new file mode 100644 index 00000000..36bbcb31 --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/KaraokeJourney.razor @@ -0,0 +1,287 @@ +@* + EN: Karaoke Journey — End-to-end session workflow tracker with 6 steps from reception to payment. + VI: Hành trình Karaoke — Theo dõi quy trình phiên từ đón khách đến thanh toán qua 6 bước. +*@ +@page "/pos/karaoke/karaoke-journey" +@layout PosLayout +@inherits PosBase + +
+ @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+ + Hành trình Karaoke + Bước @(_activeStep + 1)/6 +
+ + @* ═══ STEP INDICATOR / CHỈ BÁO BƯỚC ═══ *@ +
+ @for (var i = 0; i < _steps.Length; i++) + { + var idx = i; +
+
+ +
+ + @_steps[idx].Label + +
+ @if (i < _steps.Length - 1) + { +
+ } + } +
+ + @* ═══ STEP CONTENT / NỘI DUNG BƯỚC ═══ *@ +
+ @switch (_activeStep) + { + @* EN: Step 1 — Guest reception / VI: Bước 1 — Đón khách *@ + case 0: +
+
+ Đón khách +
+
+
+
Số khách
+
+ + @_guestCount + +
+
+
+
Thẻ thành viên
+
+
+ + +
+
+ @if (!string.IsNullOrEmpty(_memberSearch)) + { +
+ + Nguyễn Văn Minh — Gold • 2,450 điểm +
+ } +
+
+
+ break; + + @* EN: Step 2 — Room selection / VI: Bước 2 — Chọn phòng *@ + case 1: +
+
+ Chọn phòng +
+
+
+
Phòng
+
VIP 2
+
+
+
Sức chứa
+
20 người
+
+
+
Loại
+
Deluxe
+
+
+
+ Tầng 3 • Khu Deluxe • @FormatPrice(200_000)/giờ +
+
+ break; + + @* EN: Step 3 — Open room / VI: Bước 3 — Mở phòng *@ + case 2: +
+
+ Mở phòng +
+
+
+ Giờ bắt đầu + 19:30 +
+
+ Thời lượng đặt + 2.5 giờ +
+
+ Giá/giờ + @FormatPrice(200_000) +
+
+ Giờ kết thúc dự kiến + 22:00 +
+
+ Tạm tính phòng + @FormatPrice(500_000) +
+
+
+ break; + + @* EN: Step 4 — In room / VI: Bước 4 — Trong phòng *@ + case 3: +
+
+ THỜI GIAN SỬ DỤNG +
+
+ 02:15:00 +
+
+ Bắt đầu: 19:30 • Dự kiến: 22:00 +
+
+
+
Đơn F&B hiện tại
+
+ Số món + 6 món +
+
+ Tổng F&B + @FormatPrice(830_000) +
+
+
+ + +
+ break; + + @* EN: Step 5 — Close room / VI: Bước 5 — Đóng phòng *@ + case 4: +
+
+ Kết thúc phiên +
+
+
+ Thời gian sử dụng + 2 giờ 30 phút +
+
+ Tiền phòng + @FormatPrice(500_000) +
+
+ Tiền F&B + @FormatPrice(830_000) +
+
+ Tổng cộng + @FormatPrice(1_330_000) +
+
+
+ break; + + @* EN: Step 6 — Payment / VI: Bước 6 — Thanh toán *@ + case 5: +
+
+ TỔNG THANH TOÁN +
+
+ @FormatPrice(1_330_000) +
+
+ Phòng VIP 2 • 2h30 • 6 món F&B +
+
+
+ + +
+ break; + } +
+ + @* ═══ NAVIGATION BUTTONS / NÚT ĐIỀU HƯỚNG ═══ *@ +
+ + +
+
+ +@code { + // EN: Active step index / VI: Chỉ số bước hiện tại + private int _activeStep; + private int _guestCount = 8; + private string _memberSearch = "0901234567"; + + // EN: Journey steps / VI: Các bước hành trình + private readonly StepInfo[] _steps = + { + new("Đón khách", "users"), + new("Chọn phòng", "door-open"), + new("Mở phòng", "play"), + new("Trong phòng", "music"), + new("Đóng phòng", "lock"), + new("Thanh toán", "credit-card"), + }; + + private record StepInfo(string Label, string Icon); +} 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 new file mode 100644 index 00000000..9c88fc13 --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/PeakWarning.razor @@ -0,0 +1,169 @@ +@* + EN: Karaoke Peak Warning — Peak hours pricing comparison, room type multipliers, cost estimator. + VI: Cảnh báo giờ cao điểm Karaoke — So sánh giá giờ cao điểm, hệ số phòng, ước tính chi phí. +*@ +@page "/pos/karaoke/peak-warning" +@layout PosLayout +@inherits PosBase + +
+ @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+ + + + Giờ cao điểm + +
+ +
+ @* ═══ CURRENT TIME / THỜI GIAN HIỆN TẠI ═══ *@ +
+
+ KHUNG GIỜ HIỆN TẠI +
+
+ 20:30 — Thứ 7 +
+
+ Đang áp dụng giá cuối tuần +
+
+ + @* ═══ PRICING TABLE / BẢNG GIÁ ═══ *@ +
+
Bảng giá theo khung giờ (Standard)
+
+ @foreach (var rate in _pricingRates) + { +
+
+
+ @rate.Label +
+
@rate.TimeRange
+
+
+
+ @FormatPrice(rate.Price)/giờ +
+
x@rate.Multiplier.ToString("0.0")
+
+ @if (rate.IsActive) + { + Hiện tại + } +
+ } +
+
+ + @* ═══ ROOM TYPE SELECTOR / CHỌN LOẠI PHÒNG ═══ *@ +
+
Loại phòng
+
+ @foreach (var room in _roomTypes) + { + + } +
+
+ + @* ═══ COST ESTIMATOR / ƯỚC TÍNH CHI PHÍ ═══ *@ +
+
Ước tính chi phí
+
+ Số giờ: + + @_estimateHours + + giờ +
+
+ Giá hiện tại (@_selectedRoomType.Name) + @FormatPrice(CurrentRatePrice)/giờ +
+
+ Số giờ + @_estimateHours giờ +
+
+ Tổng ước tính + @FormatPrice(CurrentRatePrice * _estimateHours) +
+
+
+ + @* ═══ CONFIRM BUTTON / NÚT XÁC NHẬN ═══ *@ +
+ +
+
+ +@code { + // EN: Estimate hours / VI: Số giờ ước tính + private int _estimateHours = 2; + + // EN: Pricing rates / VI: Bảng giá + private readonly List _pricingRates = new() + { + new("Giờ thường", "T2–T5, 10:00–17:00", 100_000, 1.0m, false), + new("Giờ cao điểm", "T2–T5, 17:00–23:00", 150_000, 1.5m, false), + new("Cuối tuần", "T6–CN", 180_000, 1.8m, true), + new("Lễ/Tết", "Ngày lễ, Tết", 250_000, 2.5m, false), + }; + + // EN: Room types / VI: Loại phòng + private readonly RoomType[] _roomTypes = + { + new("Standard", 1.0m), + new("Deluxe", 1.5m), + new("VIP", 2.0m), + }; + + private RoomType _selectedRoomType = null!; + + protected override void OnInitialized() + { + _selectedRoomType = _roomTypes[0]; + } + + // EN: Current active rate price adjusted for room type / VI: Giá hiện tại theo loại phòng + private decimal CurrentRatePrice + { + get + { + var activeRate = _pricingRates.FirstOrDefault(r => r.IsActive) ?? _pricingRates[0]; + return activeRate.Price * _selectedRoomType.Multiplier; + } + } + + private record PricingRate(string Label, string TimeRange, decimal Price, decimal Multiplier, bool IsActive); + private record RoomType(string Name, decimal Multiplier); +} 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 new file mode 100644 index 00000000..591e6cf1 --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomExtend.razor @@ -0,0 +1,191 @@ +@* + EN: Karaoke Room Extend — Extension dialog with time options, new end time preview, peak warning. + VI: Gia hạn phòng Karaoke — Dialog gia hạn với tùy chọn thời gian, xem trước giờ kết thúc, cảnh báo giờ cao điểm. +*@ +@page "/pos/karaoke/room-extend" +@layout PosLayout +@inherits PosBase + +
+ @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+ + Gia hạn phòng +
+ +
+ @* ═══ CURRENT SESSION INFO / THÔNG TIN PHIÊN HIỆN TẠI ═══ *@ +
+
+
+
Phòng VIP 2
+
Deluxe • 20 người • Tầng 3
+
+ Đang hoạt động +
+
+
+
Bắt đầu
+
19:30
+
+
+
Đã dùng
+
2h15
+
+
+
Giá/giờ
+
@FormatPrice(200_000)
+
+
+
+ Tiền phòng hiện tại + @FormatPrice(450_000) +
+
+ + @* ═══ EXTENSION OPTIONS / TÙY CHỌN GIA HẠN ═══ *@ +
+
Chọn thời gian gia hạn
+
+ @foreach (var opt in _extendOptions) + { + + } +
+
+ + @* EN: Custom input / VI: Nhập tùy chỉnh *@ +
+
Tùy chỉnh (phút)
+
+ + @_customMinutes + + phút + +
+
+ + @* ═══ PREVIEW / XEM TRƯỚC ═══ *@ + @if (_selectedOption is not null) + { +
+
Xem trước sau gia hạn
+
+ Giờ kết thúc mới + @_newEndTime +
+
+ Thời gian thêm + +@_selectedOption.Label +
+
+ Phí gia hạn + +@FormatPrice(_selectedOption.Cost) +
+
+ Tổng tiền phòng mới + @FormatPrice(450_000 + _selectedOption.Cost) +
+
+ + @* EN: Peak warning if applicable / VI: Cảnh báo giờ cao điểm nếu có *@ + @if (_showPeakWarning) + { +
+ +
+
Cảnh báo giờ cao điểm
+
+ Gia hạn vào khung giờ cao điểm (sau 22:00). Giá có thể tăng. +
+
+
+ } + } +
+ + @* ═══ ACTION BUTTONS / NÚT HÀNH ĐỘNG ═══ *@ +
+ + +
+
+ +@code { + // EN: Extension options / VI: Tùy chọn gia hạn + private readonly List _extendOptions = new() + { + new(30, "+30 phút", 100_000), + new(60, "+1 giờ", 200_000), + new(90, "+1.5 giờ", 300_000), + new(120, "+2 giờ", 400_000), + }; + + private ExtendOption? _selectedOption; + private int _customMinutes = 45; + private string _newEndTime = "22:00"; + private bool _showPeakWarning; + + private void SelectExtension(ExtendOption opt) + { + _selectedOption = opt; + UpdatePreview(opt.Minutes); + } + + private void AdjustCustom(int delta) + { + _customMinutes = Math.Max(15, _customMinutes + delta); + } + + private void ApplyCustom() + { + var cost = (decimal)_customMinutes / 60 * 200_000; + _selectedOption = new(_customMinutes, $"+{_customMinutes} phút", Math.Round(cost, -3)); + UpdatePreview(_customMinutes); + } + + private void UpdatePreview(int minutes) + { + var baseEnd = new TimeOnly(22, 0); + var newEnd = baseEnd.AddMinutes(minutes); + _newEndTime = newEnd.ToString("HH:mm"); + _showPeakWarning = newEnd.Hour >= 22 || newEnd.Hour < 2; + } + + private record ExtendOption(int Minutes, string Label, decimal Cost); +} 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 new file mode 100644 index 00000000..97b2d8e5 --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Karaoke/Workflow/RoomReset.razor @@ -0,0 +1,147 @@ +@* + EN: Karaoke Room Reset — Cleanup checklist after session ends, progress tracking, staff assignment. + VI: Reset phòng Karaoke — Danh sách dọn dẹp sau phiên, theo dõi tiến độ, phân công nhân viên. +*@ +@page "/pos/karaoke/room-reset" +@layout PosLayout +@inherits PosBase + +
+ @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+ + Reset phòng + + @(AllChecked ? "Sẵn sàng" : "Đang dọn") + +
+ +
+ @* ═══ ROOM INFO / THÔNG TIN PHÒNG ═══ *@ +
+
+
+
Phòng VIP 2
+
Deluxe • 20 người • Tầng 3
+
+
+
Phiên trước kết thúc
+
22:15
+
+
+
+ + @* ═══ STAFF & TIME / NHÂN VIÊN & THỜI GIAN ═══ *@ +
+
+
+ +
+
+
Nhân viên
+
Trần Thị Hoa
+
+
+
+
+ +
+
+
Bắt đầu dọn
+
22:18 • 12 phút
+
+
+
+ + @* ═══ PROGRESS BAR / THANH TIẾN ĐỘ ═══ *@ +
+
+ Tiến độ + @CompletedCount/@_checkItems.Count hoàn thành +
+
+
+
+
+ + @* ═══ CHECKLIST / DANH SÁCH KIỂM TRA ═══ *@ +
+ @foreach (var item in _checkItems) + { +
+
+ @if (item.Checked) + { + + } +
+
+
+ @item.Label +
+
@item.Description
+
+ +
+ } +
+
+ + @* ═══ COMPLETE BUTTON / NÚT HOÀN TẤT ═══ *@ +
+ +
+
+ +@code { + // EN: Checklist items / VI: Các mục kiểm tra + private readonly List _checkItems = new() + { + new("Dọn bàn ghế", "Clean tables/chairs", "armchair", false), + new("Vệ sinh micro", "Clean microphones", "mic", false), + new("Kiểm tra remote", "Check remote controls", "tv", false), + new("Bổ sung nước uống", "Restock beverages", "cup-soda", false), + new("Kiểm tra ánh sáng", "Check lighting", "lightbulb", false), + new("Hệ thống âm thanh", "Sound system check", "volume-2", false), + new("Kiểm tra thiết bị", "Equipment check", "monitor-speaker", false), + new("Vệ sinh toilet", "Clean restroom", "bath", false), + }; + + private int CompletedCount => _checkItems.Count(i => i.Checked); + private bool AllChecked => _checkItems.All(i => i.Checked); + private int ProgressPercent => _checkItems.Count > 0 ? CompletedCount * 100 / _checkItems.Count : 0; + + private class CheckItem(string label, string description, string icon, bool isChecked) + { + public string Label { get; set; } = label; + public string Description { get; set; } = description; + public string Icon { get; set; } = icon; + public bool Checked { get; set; } = isChecked; + } +}