feat(multi-vertical): P2 — resources real data, treatments enhanced, inv txns

- Resources: real table from booking_service (stats + capacity + status)
- Treatments: enhanced UI with Before/After, tái khám, hồ sơ y tế chips
- Inventory: transactions loading wired to GetInventoryTransactionsAsync
- PosDataService: added GetResourcesAsync for booking resources
- LoadData: resources, inventory txns cases added
This commit is contained in:
Ho Ngoc Hai
2026-02-28 23:35:47 +07:00
parent 549bfcb038
commit d8b65a368f
2 changed files with 63 additions and 2 deletions

View File

@@ -650,12 +650,60 @@
// ═══ RESOURCES (Spa/Beauty — phòng, giường, thiết bị) ═══
case "resources":
@RenderEmpty("door-open", "#EC4899", "Quản lý tài nguyên", "Phòng, giường, thiết bị — Kết nối với Booking Service", "plus-circle", "Thêm tài nguyên")
@if (!_resources.Any())
{
@RenderEmpty("door-open", "#EC4899", "Chưa có tài nguyên", "Cấu hình phòng, giường, thiết bị cho cửa hàng", "plus-circle", "Thêm tài nguyên")
}
else
{
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;">
<div class="admin-stat-card"><div class="admin-stat-card__icon" style="background:rgba(236,72,153,0.1);"><i data-lucide="door-open" style="color:#EC4899;"></i></div><div class="admin-stat-card__content"><span class="admin-stat-card__value">@_resources.Count</span><span class="admin-stat-card__label">Tổng tài nguyên</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="check-circle" style="color:#22C55E;"></i></div><div class="admin-stat-card__content"><span class="admin-stat-card__value">@_resources.Count(r => r.IsActive)</span><span class="admin-stat-card__label">Đang hoạt động</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="users" style="color:#3B82F6;"></i></div><div class="admin-stat-card__content"><span class="admin-stat-card__value">@_resources.Sum(r => r.Capacity)</span><span class="admin-stat-card__label">Tổng sức chứa</span></div></div>
</div>
<div class="admin-panel" style="margin-top:16px;">
<div class="admin-panel__header"><h3 class="admin-panel__title">Danh sách tài nguyê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);">Tên</th>
<th style="padding:12px 16px;text-align:center;font-size:12px;text-transform:uppercase;color:var(--admin-text-tertiary);">Loại</th>
<th style="padding:12px 16px;text-align:right;font-size:12px;text-transform:uppercase;color:var(--admin-text-tertiary);">Sức chứa</th>
<th style="padding:12px 16px;text-align:center;font-size:12px;text-transform:uppercase;color:var(--admin-text-tertiary);">Trạng thái</th>
</tr></thead><tbody>
@foreach (var r in _resources)
{
<tr style="border-top:1px solid var(--admin-border-subtle);">
<td style="padding:12px 16px;font-weight:600;">@r.Name</td>
<td style="padding:12px 16px;text-align:center;font-size:12px;color:var(--admin-text-tertiary);">@(r.ResourceType ?? "—")</td>
<td style="padding:12px 16px;text-align:right;font-weight:600;color:var(--admin-orange-primary);">@r.Capacity</td>
<td style="padding:12px 16px;text-align:center;"><span class="admin-status-badge @(r.IsActive ? "admin-status-badge--online" : "admin-status-badge--offline")" style="font-size:11px;padding:2px 10px;"><span class="admin-status-badge__dot" style="width:5px;height:5px;"></span>@(r.IsActive ? "Active" : "Inactive")</span></td>
</tr>
}
</tbody></table>
</div>
</div>
}
break;
// ═══ TREATMENTS (Beauty — Liệu trình) ═══
case "treatments":
@RenderEmpty("clipboard-list", "#A855F7", "Quản lý liệu trình", "Theo dõi liệu trình điều trị dài hạn, ảnh before/after, tiến trình")
<div class="admin-panel">
<div class="admin-panel__header" style="display:flex;justify-content:space-between;align-items:center;">
<h3 class="admin-panel__title">Liệu trình điều trị</h3>
</div>
<div class="admin-panel__body" style="text-align:center;padding:40px 20px;">
<div style="width:64px;height:64px;border-radius:20px;background:rgba(168,85,247,0.1);display:flex;align-items:center;justify-content:center;margin:0 auto 16px;">
<i data-lucide="clipboard-list" style="width:28px;height:28px;color:#A855F7;"></i>
</div>
<h3 style="font-size:18px;font-weight:700;margin:0 0 8px;">Quản lý liệu trình dài hạn</h3>
<p style="font-size:14px;color:var(--admin-text-tertiary);margin:0 0 8px;max-width:400px;margin-left:auto;margin-right:auto;">Theo dõi tiến trình điều trị nhiều buổi, ảnh before/after, lịch tái khám sau phẫu thuật.</p>
<div style="display:flex;gap:12px;justify-content:center;margin-top:16px;">
<span style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--admin-text-tertiary);"><i data-lucide="camera" style="width:14px;height:14px;"></i> Ảnh Before/After</span>
<span style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--admin-text-tertiary);"><i data-lucide="calendar-check" style="width:14px;height:14px;"></i> Lịch tái khám</span>
<span style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--admin-text-tertiary);"><i data-lucide="file-text" style="width:14px;height:14px;"></i> Hồ sơ y tế</span>
</div>
</div>
</div>
break;
// ═══ PROMOTIONS (real data) ═══
@@ -802,6 +850,7 @@
private List<PosDataService.LevelDefinitionInfo> _memberLevels = new();
private List<PosDataService.ScheduleInfo> _staffSchedules = new();
private List<PosDataService.InventoryTxnInfo> _invTxns = new();
private List<PosDataService.ResourceInfo> _resources = new();
protected override async Task OnInitializedAsync() => await LoadData();
@@ -853,6 +902,7 @@
break;
case "inventory":
_inventory = await DataService.GetInventoryAsync(_shopGuid);
_invTxns = await DataService.GetInventoryTransactionsAsync(_shopGuid);
break;
case "finance":
_orders = await DataService.GetOrdersAsync(_shopGuid);
@@ -876,6 +926,10 @@
if (_shopGuid.HasValue)
_appointments = await DataService.GetAppointmentsAsync(_shopGuid.Value);
break;
case "resources":
if (_shopGuid.HasValue)
_resources = await DataService.GetResourcesAsync(_shopGuid.Value);
break;
case "services":
_products = await DataService.GetAllProductsAsync(_shopGuid);
break;

View File

@@ -212,4 +212,11 @@ public class PosDataService
public async Task<List<ShopStatsInfo>> GetShopStatsAsync()
{ AttachToken(); return await _http.GetFromJsonAsync<List<ShopStatsInfo>>("api/bff/shops/stats", _jsonOptions) ?? new(); }
// ═══ BOOKING RESOURCES ═══
public record ResourceInfo(Guid Id, string Name, string? ResourceType, int Capacity, bool IsActive);
public async Task<List<ResourceInfo>> GetResourcesAsync(Guid shopId)
{ AttachToken(); return await _http.GetFromJsonAsync<List<ResourceInfo>>($"api/bff/shops/{shopId}/resources", _jsonOptions) ?? new(); }
}