feat(admin): P6 — Zones, Combo services, Shift management

- R4: Zone management (Restaurant) — 4 zones with table counts, status, actions
- S6: Combo services (Spa) — 3 combo cards with pricing, duration, sold count
- C5: Shift management (Café) — weekly shift grid (S/C/—) with legend + stats
This commit is contained in:
Ho Ngoc Hai
2026-03-01 05:22:32 +07:00
parent 8ec9e2a45a
commit cc0e642c43
2 changed files with 133 additions and 0 deletions

View File

@@ -1338,6 +1338,133 @@
</div>
break;
// ═══ R4: ZONES / KHU VỰC (Nhà hàng) ═══
case "zones":
<div class="admin-panel">
<div class="admin-panel__header" style="display:flex;justify-content:space-between;align-items:center;">
<h3 class="admin-panel__title">📍 Quản lý khu vực</h3>
<button class="admin-btn-primary" style="font-size:12px;padding:6px 14px;"><i data-lucide="plus" style="width:14px;height:14px;margin-right:4px;"></i>Thêm khu vực</button>
</div>
<div class="admin-panel__body">
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px;">
@foreach (var (name, tables, desc, color, icon) in new[] {
("Tầng 1 — Sảnh chính", 12, "Khu vực chính tiếp khách, gần quầy bar", "#3B82F6", "building"),
("Tầng 2 — VIP", 6, "Phòng VIP riêng tư, cách âm tốt", "#A855F7", "crown"),
("Sân vườn", 8, "Khu vực ngoài trời, view đẹp", "#22C55E", "trees"),
("Bar & Lounge", 4, "Khu vực bar, phục vụ đồ uống", "#F59E0B", "wine") })
{
<div style="padding:20px;border-radius:12px;background:var(--admin-bg-elevated);border:1px solid var(--admin-border-subtle);">
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
<div style="width:40px;height:40px;border-radius:10px;background:rgba(@(color == "#3B82F6" ? "59,130,246" : color == "#A855F7" ? "168,85,247" : color == "#22C55E" ? "34,197,94" : "245,158,11"),0.15);display:flex;align-items:center;justify-content:center;">
<i data-lucide="@icon" style="width:20px;height:20px;color:@color;"></i>
</div>
<div>
<div style="font-weight:700;font-size:15px;">@name</div>
<div style="font-size:12px;color:var(--admin-text-tertiary);">@tables bàn</div>
</div>
</div>
<p style="font-size:13px;color:var(--admin-text-secondary);margin:0 0 14px;">@desc</p>
<div style="display:flex;gap:8px;">
<span style="font-size:11px;padding:3px 10px;border-radius:6px;background:rgba(34,197,94,0.12);color:#22C55E;">Đang hoạt động</span>
</div>
<div style="display:flex;gap:12px;margin-top:14px;border-top:1px solid var(--admin-border-subtle);padding-top:12px;">
<button style="font-size:12px;color:var(--admin-orange-primary);background:none;border:none;cursor:pointer;display:flex;align-items:center;gap:4px;"><i data-lucide="pencil" style="width:12px;height:12px;"></i>Sửa</button>
<button style="font-size:12px;color:var(--admin-text-tertiary);background:none;border:none;cursor:pointer;display:flex;align-items:center;gap:4px;"><i data-lucide="layout-grid" style="width:12px;height:12px;"></i>Sơ đồ bàn</button>
</div>
</div>
}
</div>
</div>
</div>
break;
// ═══ S6: COMBO DỊCH VỤ (Spa) ═══
case "combos":
<div class="admin-panel">
<div class="admin-panel__header" style="display:flex;justify-content:space-between;align-items:center;">
<h3 class="admin-panel__title">🎁 Combo dịch vụ</h3>
<button class="admin-btn-primary" style="font-size:12px;padding:6px 14px;"><i data-lucide="plus" style="width:14px;height:14px;margin-right:4px;"></i>Tạo combo</button>
</div>
<div class="admin-panel__body">
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:16px;">
@foreach (var (name, services, duration, price, orig, sold, color) in new[] {
("Combo Thư Giãn Toàn Thân", "Massage body 60' + Xông hơi 30' + Đắp mặt nạ 20'", "110 phút", "800,000₫", "1,050,000₫", 45, "#3B82F6"),
("Combo Detox & Làm Đẹp", "Tẩy tế bào chết + Massage mặt + Chăm sóc da 5 bước", "90 phút", "650,000₫", "850,000₫", 32, "#A855F7"),
("Combo Cặp Đôi Premium", "2 Massage body 90' + 2 Xông hơi + Trà thảo mộc", "120 phút", "1,500,000₫", "2,100,000₫", 18, "#EC4899") })
{
<div style="padding:20px;border-radius:12px;background:var(--admin-bg-elevated);border:1px solid var(--admin-border-subtle);">
<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:12px;">
<h4 style="font-size:16px;font-weight:700;margin:0;">@name</h4>
<span style="font-size:11px;padding:3px 10px;border-radius:6px;background:rgba(34,197,94,0.12);color:#22C55E;">Đang bán</span>
</div>
<p style="font-size:13px;color:var(--admin-text-secondary);margin:0 0 12px;line-height:1.5;">@services</p>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
<i data-lucide="clock" style="width:14px;height:14px;color:var(--admin-text-tertiary);"></i>
<span style="font-size:13px;color:var(--admin-text-tertiary);">@duration</span>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;padding-top:12px;border-top:1px solid var(--admin-border-subtle);">
<div>
<span style="font-size:18px;font-weight:800;color:var(--admin-orange-primary);">@price</span>
<span style="font-size:12px;color:var(--admin-text-tertiary);text-decoration:line-through;margin-left:8px;">@orig</span>
</div>
<span style="font-size:12px;color:var(--admin-text-tertiary);">Đã bán: @sold</span>
</div>
</div>
}
</div>
</div>
</div>
break;
// ═══ C5: CA LÀM VIỆC / SHIFTS (Café) ═══
case "shifts":
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:16px;margin-bottom:16px;">
<div class="admin-stat-card"><div class="admin-stat-card__icon" style="background:rgba(59,130,246,0.1);"><i data-lucide="clock" style="color:#3B82F6;"></i></div><div class="admin-stat-card__content"><span class="admin-stat-card__value">3</span><span class="admin-stat-card__label">Ca hôm nay</span></div></div>
<div class="admin-stat-card"><div class="admin-stat-card__icon" style="background:rgba(34,197,94,0.1);"><i data-lucide="user-check" style="color:#22C55E;"></i></div><div class="admin-stat-card__content"><span class="admin-stat-card__value">5</span><span class="admin-stat-card__label">Đang làm việc</span></div></div>
<div class="admin-stat-card"><div class="admin-stat-card__icon" style="background:rgba(245,158,11,0.1);"><i data-lucide="alert-circle" style="color:#F59E0B;"></i></div><div class="admin-stat-card__content"><span class="admin-stat-card__value">1</span><span class="admin-stat-card__label">Vắng mặt</span></div></div>
</div>
<div class="admin-panel">
<div class="admin-panel__header" style="display:flex;justify-content:space-between;align-items:center;">
<h3 class="admin-panel__title">📋 Lịch ca — Tuần này</h3>
<button class="admin-btn-primary" style="font-size:12px;padding:6px 14px;"><i data-lucide="plus" style="width:14px;height:14px;margin-right:4px;"></i>Phân ca</button>
</div>
<div class="admin-panel__body" style="padding:0;overflow-x:auto;">
<table class="admin-table" style="width:100%;min-width:700px;"><thead><tr>
<th style="padding:12px 16px;text-align:left;font-size:12px;text-transform:uppercase;color:var(--admin-text-tertiary);white-space:nowrap;">Nhân viên</th>
@foreach (var d in new[] { "T2", "T3", "T4", "T5", "T6", "T7", "CN" })
{
<th style="padding:12px 8px;text-align:center;font-size:12px;text-transform:uppercase;color:var(--admin-text-tertiary);min-width:80px;">@d</th>
}
</tr></thead><tbody>
@foreach (var (name, shifts) in new[] {
("Nguyễn A", new[] { "S", "S", "C", "C", "—", "S", "—" }),
("Trần B", new[] { "C", "C", "S", "S", "C", "—", "—" }),
("Lê C", new[] { "S", "—", "S", "C", "S", "C", "S" }),
("Phạm D", new[] { "—", "S", "C", "—", "C", "S", "C" }),
("Hoàng E", new[] { "C", "C", "—", "S", "S", "—", "S" }) })
{
<tr style="border-top:1px solid var(--admin-border-subtle);">
<td style="padding:12px 16px;font-weight:600;white-space:nowrap;">@name</td>
@foreach (var s in shifts)
{
var bg = s == "S" ? "rgba(59,130,246,0.12)" : s == "C" ? "rgba(168,85,247,0.12)" : "transparent";
var fg = s == "S" ? "#3B82F6" : s == "C" ? "#A855F7" : "var(--admin-text-tertiary)";
<td style="padding:8px;text-align:center;">
<span style="display:inline-block;width:36px;height:28px;line-height:28px;border-radius:6px;font-size:12px;font-weight:700;background:@bg;color:@fg;">@s</span>
</td>
}
</tr>
}
</tbody></table>
</div>
</div>
<div style="display:flex;gap:16px;margin-top:12px;">
<div style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--admin-text-tertiary);"><span style="display:inline-block;width:12px;height:12px;border-radius:3px;background:rgba(59,130,246,0.25);"></span> S = Sáng (7:00-14:00)</div>
<div style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--admin-text-tertiary);"><span style="display:inline-block;width:12px;height:12px;border-radius:3px;background:rgba(168,85,247,0.25);"></span> C = Chiều (14:00-22:00)</div>
<div style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--admin-text-tertiary);"><span style="display:inline-block;width:12px;height:12px;border-radius:3px;background:transparent;border:1px solid var(--admin-border-subtle);"></span> — = Nghỉ</div>
</div>
break;
// ═══ UNKNOWN SECTIONS ═══
default:
<div class="admin-panel">
@@ -1554,6 +1681,9 @@
case "consent": _sectionTitle = "Cam kết KH"; _sectionIcon = "file-check"; _sectionDescription = "Biểu mẫu đồng ý khách hàng."; break;
case "doctors": _sectionTitle = "Bác sĩ / CK"; _sectionIcon = "stethoscope"; _sectionDescription = "Quản lý bác sĩ và chuyên gia."; break;
case "followup": _sectionTitle = "Tái khám"; _sectionIcon = "calendar-heart"; _sectionDescription = "Lịch tái khám sau điều trị."; break;
case "zones": _sectionTitle = "Khu vực"; _sectionIcon = "map-pin"; _sectionDescription = "Quản lý khu vực phục vụ."; break;
case "combos": _sectionTitle = "Combo dịch vụ"; _sectionIcon = "layers"; _sectionDescription = "Quản lý combo dịch vụ."; break;
case "shifts": _sectionTitle = "Ca làm việc"; _sectionIcon = "clock-4"; _sectionDescription = "Lịch ca làm, phân ca."; break;
default: _sectionTitle = Section ?? "Trang"; _sectionIcon = "layout-dashboard"; _sectionDescription = "Trang đang phát triển."; break;
}
}

View File

@@ -34,6 +34,7 @@ public static class ShopSidebarConfig
new("POS Bán hàng", "monitor", "pos"),
new("Menu & Đồ uống", "coffee", "menu"),
new("Nguyên liệu", "flask-conical", "recipes"),
new("Ca làm việc", "clock-4", "shifts"),
new("Tồn kho", "warehouse", "inventory"),
new("Tài chính", "trending-up", "finance"),
new("Nhân sự", "users", "staff"),
@@ -49,6 +50,7 @@ public static class ShopSidebarConfig
new("Menu & Món ăn", "utensils", "menu"),
new("Bàn / Table", "grid-3x3", "tables"),
new("Đặt bàn", "calendar-check", "reservations"),
new("Khu vực", "map-pin", "zones"),
new("Bếp (Kitchen)", "flame", "kitchen"),
new("Tồn kho", "warehouse", "inventory"),
new("Tài chính", "trending-up", "finance"),
@@ -80,6 +82,7 @@ public static class ShopSidebarConfig
new("Lịch hẹn", "calendar", "appointments"),
new("Dịch vụ", "sparkles", "services"),
new("Gói dịch vụ", "gift", "packages"),
new("Combo dịch vụ", "layers", "combos"),
new("Tài nguyên", "door-open", "resources"),
new("Sản phẩm", "package", "products"),
new("Tài chính", "trending-up", "finance"),