diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Layout/StaffLayout.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Layout/StaffLayout.razor
index a4eeef85..7e059c54 100644
--- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Layout/StaffLayout.razor
+++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Layout/StaffLayout.razor
@@ -57,11 +57,11 @@
Bàn / Order
}
- @if (_staffRole == "Cashier")
+ @if (_staffRole == "Cashier" || _staffRole == "Manager")
{
-
+
- Thu ngân
+ Mở POS
}
@if (_staffRole == "Manager")
diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Staff/StaffDashboard.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Staff/StaffDashboard.razor
index 0284f2ad..f08d9c6a 100644
--- a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Staff/StaffDashboard.razor
+++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Staff/StaffDashboard.razor
@@ -46,6 +46,14 @@
Xin nghỉ phép
+
+ @if (_profile?.ShopId.HasValue == true)
+ {
+
+ }
@* ═══ STAT CARDS ═══ *@
diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/StaffController.cs b/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/StaffController.cs
index e1b718a0..178bcafa 100644
--- a/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/StaffController.cs
+++ b/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/StaffController.cs
@@ -170,8 +170,13 @@ public class StaffController : ControllerBase
public async Task GetMyStaffProfile()
{
// EN: Get all staff for this merchant — AuthForwardingHandler auto-forwards the Bearer token.
+ // Fallback: if merchant-service rejects (staff user is not owner), try staff lookup by email.
// VI: Lấy tất cả nhân viên của merchant — AuthForwardingHandler tự động chuyển tiếp Bearer token.
+ // Fallback: nếu merchant-service từ chối (staff user không phải owner), thử tìm theo email.
var authHeader = Request.Headers["Authorization"].FirstOrDefault();
+ var (currentUserId, currentEmail) = ExtractUserClaimsFromJwt(
+ Request.Cookies.TryGetValue("bff_session", out var cookie) ? $"Bearer {cookie}" : authHeader);
+
var staffResp = await _merchant.GetAsync("/api/v1/merchants/me/staff");
if (!staffResp.IsSuccessStatusCode)
return StatusCode((int)staffResp.StatusCode, await staffResp.Content.ReadAsStringAsync());
@@ -191,9 +196,11 @@ public class StaffController : ControllerBase
else
return NotFound(new { success = false, message = "No staff data found" });
- // EN: Extract userId from JWT 'sub' claim to match staff by userId.
- // VI: Trích userId từ JWT 'sub' claim để match nhân viên theo userId.
- var userId = ExtractUserIdFromJwt(authHeader);
+ // EN: Use userId + email already extracted from session cookie (line 177-178).
+ // Previous bug: ExtractUserIdFromJwt(authHeader) used Authorization header which is null for cookie-based BFF auth.
+ // VI: Dùng userId + email đã extract từ session cookie (dòng 177-178).
+ // Bug trước: ExtractUserIdFromJwt(authHeader) dùng Authorization header mà null khi dùng cookie-based BFF auth.
+ var userId = currentUserId;
// EN: Find matching staff member by userId, then by email fallback
// VI: Tìm nhân viên khớp theo userId, fallback theo email
@@ -211,6 +218,21 @@ public class StaffController : ControllerBase
}
}
+ // EN: Fallback: match by email if userId match failed
+ // VI: Fallback: match theo email nếu userId không khớp
+ if (matchedStaff == null && !string.IsNullOrEmpty(currentEmail) && items.ValueKind == JsonValueKind.Array)
+ {
+ foreach (var staff in items.EnumerateArray())
+ {
+ if (staff.TryGetProperty("email", out var emailProp) &&
+ string.Equals(emailProp.GetString(), currentEmail, StringComparison.OrdinalIgnoreCase))
+ {
+ matchedStaff = staff;
+ break;
+ }
+ }
+ }
+
if (matchedStaff == null)
return NotFound(new { success = false, message = "Staff profile not found for current user" });