feat(pos): Display empty state messages for resource grids and implement shop-specific staff management in POS views.

This commit is contained in:
Ho Ngoc Hai
2026-03-05 05:03:28 +07:00
parent 88cd45c3a8
commit c0301a22e5
6 changed files with 57 additions and 10 deletions

View File

@@ -2861,7 +2861,7 @@
case "overview":
_ovOrders = await DataService.GetOrdersAsync(_shopGuid);
_ovProducts = await DataService.GetAllProductsAsync(_shopGuid);
_ovStaff = await DataService.GetStaffAsync();
_ovStaff = await DataService.GetStaffForShopAsync(_shopGuid.Value);
if (_shopGuid.HasValue && (_posVertical == "restaurant" || _posVertical == "karaoke"))
_ovTables = await DataService.GetTablesAsync(_shopGuid.Value);
if (_shopGuid.HasValue && (_posVertical == "spa" || _posVertical == "beauty"))
@@ -2882,7 +2882,7 @@
_walletTxns = await DataService.GetWalletTransactionsAsync();
break;
case "staff":
_staff = await DataService.GetStaffAsync();
_staff = await DataService.GetStaffForShopAsync(_shopGuid.Value);
_staffSchedules = await DataService.GetStaffSchedulesAsync(_shopGuid);
break;
case "customers":
@@ -2922,7 +2922,7 @@
case "shifts":
case "schedule":
_staffSchedules = await DataService.GetStaffSchedulesAsync(_shopGuid);
_staff = await DataService.GetStaffAsync();
_staff = await DataService.GetStaffForShopAsync(_shopGuid.Value);
break;
case "kitchen":
if (_shopGuid.HasValue)
@@ -3008,7 +3008,7 @@
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 "customers": _sectionTitle = "Khách hàng"; _sectionIcon = "heart"; _sectionDescription = "Khách hàng & thành viên — dữ liệu chung cho tất cả cửa hàng."; 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;
@@ -3017,7 +3017,7 @@
case "services": _sectionTitle = "Dịch vụ"; _sectionIcon = "sparkles"; _sectionDescription = "Quản lý danh mục dịch vụ."; break;
case "resources": _sectionTitle = "Tài nguyên"; _sectionIcon = "door-open"; _sectionDescription = "Quản lý phòng, giường, thiết bị."; break;
case "treatments": _sectionTitle = "Liệu trình"; _sectionIcon = "clipboard-list"; _sectionDescription = "Theo dõi liệu trình điều trị."; break;
case "promotions": _sectionTitle = "Khuyến mãi"; _sectionIcon = "tag"; _sectionDescription = "Quản lý chương trình khuyến mãi."; break;
case "promotions": _sectionTitle = "Khuyến mãi"; _sectionIcon = "tag"; _sectionDescription = "Chương trình khuyến mãi — dữ liệu chung cho tất cả cửa hàng."; break;
case "settings": _sectionTitle = "Thiết lập"; _sectionIcon = "settings"; _sectionDescription = "Cài đặt cửa hàng."; break;
case "reports": _sectionTitle = "Báo cáo"; _sectionIcon = "bar-chart-2"; _sectionDescription = "Doanh thu, sản phẩm bán chạy."; break;
case "schedule": _sectionTitle = "Lịch làm việc"; _sectionIcon = "calendar-clock"; _sectionDescription = "Lịch ca làm việc nhân viên."; break;
@@ -3213,7 +3213,7 @@
}
_newStaffCode = ""; _newStaffPhone = ""; _newStaffEmail = ""; _newStaffFirstName = ""; _newStaffLastName = ""; _newStaffPassword = ""; _newStaffAddress = ""; _createStaffAccount = false;
_staffDocFrontFile = null; _staffDocBackFile = null; _staffDocFrontPreview = null; _staffDocBackPreview = null;
_staff = await DataService.GetStaffAsync();
_staff = await DataService.GetStaffForShopAsync(_shopGuid.Value);
}
catch (Exception ex) { _staffFormMessage = $"Lỗi: {ex.Message}"; _staffFormSuccess = false; }
}
@@ -3252,7 +3252,7 @@
_staffFormMessage = $"Đã cập nhật NV '{_newStaffCode}' thành công!"; _staffFormSuccess = true;
_editingStaffId = null;
_staffDocFrontFile = null; _staffDocBackFile = null; _staffDocFrontPreview = null; _staffDocBackPreview = null;
_staff = await DataService.GetStaffAsync();
_staff = await DataService.GetStaffForShopAsync(_shopGuid.Value);
}
catch (Exception ex) { _staffFormMessage = $"Lỗi: {ex.Message}"; _staffFormSuccess = false; }
}
@@ -3262,7 +3262,7 @@
try
{
await DataService.DeleteStaffAsync(staffId);
_staff = await DataService.GetStaffAsync();
_staff = await DataService.GetStaffForShopAsync(_shopGuid.Value);
}
catch (Exception ex) { _errorMessage = $"Không thể xóa nhân viên: {ex.Message}"; }
}

View File

@@ -41,6 +41,16 @@
</div>
@* ═══ ROOM GRID / LƯỚI PHÒNG ═══ *@
@if (!_rooms.Any())
{
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 0;color:var(--pos-text-tertiary);">
<i data-lucide="door-open" style="width:48px;height:48px;margin-bottom:12px;opacity:0.3;"></i>
<div style="font-size:15px;font-weight:600;margin-bottom:4px;">Chưa có phòng nào</div>
<div style="font-size:13px;">Vui lòng thêm phòng trong phần Quản lý cửa hàng</div>
</div>
}
else
{
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:12px;padding:8px 0;">
@foreach (var room in FilteredRooms)
{
@@ -70,6 +80,7 @@
}
</div>
}
}
</div>
@* ═══ SESSION PANEL (RIGHT) / PANEL PHIÊN HÁT (PHẢI) ═══ *@

View File

@@ -34,6 +34,16 @@
</div>
@* ═══ TABLE MAP GRID / LƯỚI SƠ ĐỒ BÀN ═══ *@
@if (!_tables.Any())
{
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 0;color:var(--pos-text-tertiary);">
<i data-lucide="grid-3x3" style="width:48px;height:48px;margin-bottom:12px;opacity:0.3;"></i>
<div style="font-size:15px;font-weight:600;margin-bottom:4px;">Chưa có bàn nào</div>
<div style="font-size:13px;">Vui lòng thêm bàn trong phần Quản lý cửa hàng</div>
</div>
}
else
{
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:12px;padding:8px 0;">
@foreach (var table in FilteredTables)
{
@@ -49,6 +59,7 @@
</div>
}
</div>
}
}
</div>

View File

@@ -35,6 +35,16 @@
</div>
@* ═══ SERVICE GRID / LƯỚI DỊCH VỤ ═══ *@
@if (!_services.Any())
{
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 0;color:var(--pos-text-tertiary);">
<i data-lucide="sparkles" style="width:48px;height:48px;margin-bottom:12px;opacity:0.3;"></i>
<div style="font-size:15px;font-weight:600;margin-bottom:4px;">Chưa có dịch vụ nào</div>
<div style="font-size:13px;">Vui lòng thêm dịch vụ trong phần Quản lý cửa hàng</div>
</div>
}
else
{
<div class="pos-product-grid">
@foreach (var svc in FilteredServices)
{
@@ -50,6 +60,7 @@
</div>
}
</div>
}
}
</div>

View File

@@ -142,8 +142,18 @@ public class PosDataService
public record CategoryInfo(Guid Id, string Name, string? Description, int DisplayOrder, string? ImageUrl = null);
public record TableInfo(Guid Id, string TableNumber, int Capacity, string? Zone, string Status, Guid? SessionId, int? GuestCount, DateTime? StartedAt);
public record AppointmentInfo(Guid Id, Guid? CustomerId, Guid? StaffId, Guid? ResourceId, Guid ServiceId, DateTime StartTime, DateTime EndTime, string Status, string? ResourceName);
public record ShopAssignmentInfo(Guid ShopId, string? ShopRole, Guid? BranchId);
public record StaffInfo(Guid Id, Guid? UserId, string? EmployeeCode, string? Phone, string? Email, DateTime? JoinedAt, DateTime? TerminatedAt, string? Role, string? Status, string? ShopName,
string? FirstName = null, string? LastName = null, string? Address = null, string? ProfilePhotoUrl = null, string? DocumentFrontUrl = null, string? DocumentBackUrl = null);
string? FirstName = null, string? LastName = null, string? Address = null, string? ProfilePhotoUrl = null, string? DocumentFrontUrl = null, string? DocumentBackUrl = null,
List<ShopAssignmentInfo>? ShopAssignments = null);
public async Task<List<StaffInfo>> GetStaffForShopAsync(Guid shopId)
{
var all = await GetStaffAsync();
return all.Where(s =>
s.ShopAssignments == null || s.ShopAssignments.Count == 0 ||
s.ShopAssignments.Any(a => a.ShopId == shopId)).ToList();
}
public async Task<List<ShopInfo>> GetShopsAsync()
=> await GetListFromApiAsync<ShopInfo>("api/bff/shops");