feat(admin): add Staff Schedule UI for Spa/Beauty (S3)

- Add "Lịch làm việc" menu item to Spa & Beauty sidebars
- Add case "schedule": section with stat cards + weekly table
- Display employee code, role, day (T2-CN), start/end time
- Add DayLabel helper for Vietnamese day names
- Add LoadData case + section title config
This commit is contained in:
Ho Ngoc Hai
2026-03-01 04:02:55 +07:00
parent e5b3aa58fb
commit a6e85d0451
2 changed files with 51 additions and 0 deletions

View File

@@ -706,6 +706,44 @@
</div>
break;
// ═══ STAFF SCHEDULE (Spa/Beauty — Lịch làm việc) ═══
case "schedule":
@if (!_staffSchedules.Any())
{
@RenderEmpty("calendar-clock", "#8B5CF6", "Chưa có lịch làm việc", "Thiết lập lịch ca cho nhân viên")
}
else
{
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;margin-bottom:16px;">
<div class="admin-stat-card"><div class="admin-stat-card__icon" style="background:rgba(139,92,246,0.1);"><i data-lucide="users" style="color:#8B5CF6;"></i></div><div class="admin-stat-card__content"><span class="admin-stat-card__value">@_staffSchedules.Select(s => s.StaffId).Distinct().Count()</span><span class="admin-stat-card__label">Nhân viên có lịch</span></div></div>
<div class="admin-stat-card"><div class="admin-stat-card__icon" style="background:rgba(59,130,246,0.1);"><i data-lucide="calendar" style="color:#3B82F6;"></i></div><div class="admin-stat-card__content"><span class="admin-stat-card__value">@_staffSchedules.Count</span><span class="admin-stat-card__label">Ca làm việc</span></div></div>
</div>
<div class="admin-panel">
<div class="admin-panel__header"><h3 class="admin-panel__title">Lịch làm việc theo tuần</h3></div>
<div class="admin-panel__body" style="padding:0;">
<table class="admin-table" style="width:100%;"><thead><tr>
<th style="padding:12px 16px;text-align:left;font-size:12px;text-transform:uppercase;color:var(--admin-text-tertiary);">Nhân viên</th>
<th style="padding:12px 16px;text-align:left;font-size:12px;text-transform:uppercase;color:var(--admin-text-tertiary);">Vai trò</th>
<th style="padding:12px 16px;text-align:center;font-size:12px;text-transform:uppercase;color:var(--admin-text-tertiary);">Thứ</th>
<th style="padding:12px 16px;text-align:center;font-size:12px;text-transform:uppercase;color:var(--admin-text-tertiary);">Bắt đầu</th>
<th style="padding:12px 16px;text-align:center;font-size:12px;text-transform:uppercase;color:var(--admin-text-tertiary);">Kết thúc</th>
</tr></thead><tbody>
@foreach (var s in _staffSchedules.OrderBy(x => x.DayOfWeek).ThenBy(x => x.StartTime))
{
<tr style="border-top:1px solid var(--admin-border-subtle);">
<td style="padding:12px 16px;font-weight:600;">@(s.EmployeeCode ?? s.StaffId.ToString()[..8])</td>
<td style="padding:12px 16px;font-size:13px;color:var(--admin-text-tertiary);">@(s.Role ?? "—")</td>
<td style="padding:12px 16px;text-align:center;font-weight:600;color:var(--admin-orange-primary);">@DayLabel(s.DayOfWeek)</td>
<td style="padding:12px 16px;text-align:center;">@s.StartTime</td>
<td style="padding:12px 16px;text-align:center;">@s.EndTime</td>
</tr>
}
</tbody></table>
</div>
</div>
}
break;
// ═══ PROMOTIONS (real data) ═══
case "promotions":
@if (!_promotions.Any())
@@ -940,6 +978,9 @@
case "promotions":
_promotions = await DataService.GetPromotionsAsync();
break;
case "schedule":
_staffSchedules = await DataService.GetStaffSchedulesAsync(_shopGuid);
break;
}
}
catch (Exception ex)
@@ -972,6 +1013,7 @@
case "promotions": _sectionTitle = "Khuyến mãi"; _sectionIcon = "tag"; _sectionDescription = "Quản lý chương trình khuyến mãi."; 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;
default: _sectionTitle = Section ?? "Trang"; _sectionIcon = "layout-dashboard"; _sectionDescription = "Trang đang phát triển."; break;
}
}
@@ -1065,4 +1107,11 @@
}
catch (Exception ex) { _staffFormMessage = $"Lỗi: {ex.Message}"; _staffFormSuccess = false; }
}
// EN: Day-of-week label / VI: Nhãn ngày trong tuần
private static string DayLabel(int dow) => dow switch
{
0 => "CN", 1 => "T2", 2 => "T3", 3 => "T4",
4 => "T5", 5 => "T6", 6 => "T7", _ => $"#{dow}"
};
}

View File

@@ -80,6 +80,7 @@ public static class ShopSidebarConfig
new("Sản phẩm", "package", "products"),
new("Tài chính", "trending-up", "finance"),
new("Nhân sự", "users", "staff"),
new("Lịch làm việc", "calendar-clock", "schedule"),
new("Khách hàng", "heart", "customers"),
new("Khuyến mãi", "tag", "promotions"),
new("Báo cáo", "bar-chart-2", "reports"),
@@ -96,6 +97,7 @@ public static class ShopSidebarConfig
new("Sản phẩm", "package", "products"),
new("Tài chính", "trending-up", "finance"),
new("Nhân sự", "users", "staff"),
new("Lịch làm việc", "calendar-clock", "schedule"),
new("Khách hàng", "heart", "customers"),
new("Khuyến mãi", "tag", "promotions"),
new("Báo cáo", "bar-chart-2", "reports"),