fix(admin): dashboard loads shops from BFF API, shows onboarding when empty

- Inject PosDataService and load shops in OnInitializedAsync
- Show 'Welcome! Tạo cửa hàng đầu tiên' with onboarding link when no shops
- Render dynamic shop cards from DB data when shops exist
- Keep existing KPI cards and alerts/activity panels unchanged

Co-authored-by: Velik <hongochai10@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-02-27 07:55:29 +00:00
parent dffda6d618
commit c1bb68859e

View File

@@ -1,6 +1,8 @@
@page "/admin"
@layout AdminLayout
@inherits AdminBase
@inject PosDataService DataService
@using WebClientTpos.Client.Services
@*
EN: Admin Dashboard — overview of business metrics, stores, alerts, and recent activity.
@@ -107,123 +109,47 @@
<i data-lucide="store" style="color:var(--admin-orange-primary);"></i>
Cửa hàng của bạn
</h3>
<a href="/admin/stores" class="admin-panel__action">Quản lý tất cả →</a>
@if (_shops.Count > 0)
{
<a href="/admin/stores" class="admin-panel__action">Quản lý tất cả →</a>
}
</div>
<div class="admin-panel__body" style="display:flex;flex-direction:column;gap:12px;">
@* Store 1: Coffee House Q1 *@
<div class="admin-store-card">
<div class="admin-store-card__top">
<div class="admin-store-card__info">
<div class="admin-store-card__avatar" style="background-color:rgba(255,92,0,0.125);">
<i data-lucide="coffee" style="color:var(--admin-orange-primary);"></i>
</div>
<div>
<div class="admin-store-card__name">Coffee House Q1</div>
<div class="admin-store-card__type">Café • 123 Nguyễn Huệ, Q1</div>
@if (_shops.Count == 0)
{
<div style="text-align:center;padding:40px 20px;">
<i data-lucide="store" style="width:48px;height:48px;color:var(--admin-orange-primary);margin-bottom:16px;"></i>
<h3 style="font-size:18px;font-weight:700;color:var(--pos-text-primary, #FFFFFF);margin:0 0 8px;">Welcome! Tạo cửa hàng đầu tiên</h3>
<p style="font-size:14px;color:var(--pos-text-tertiary, #ADADB0);margin:0 0 20px;">Bắt đầu bằng việc tạo cửa hàng để quản lý kinh doanh của bạn.</p>
<a href="/admin/onboarding/store" class="admin-btn-primary" style="display:inline-flex;align-items:center;gap:8px;">
<i data-lucide="plus" style="width:16px;height:16px;"></i>
Tạo cửa hàng ngay
</a>
</div>
}
else
{
@foreach (var shop in _shops)
{
<div class="admin-store-card">
<div class="admin-store-card__top">
<div class="admin-store-card__info">
<div class="admin-store-card__avatar" style="background-color:rgba(255,92,0,0.125);">
<i data-lucide="@GetShopIcon(shop.Category)" style="color:var(--admin-orange-primary);"></i>
</div>
<div>
<div class="admin-store-card__name">@shop.Name</div>
<div class="admin-store-card__type">@(shop.Category ?? "Shop") • @(shop.Description ?? shop.Slug)</div>
</div>
</div>
<div class="admin-status-badge admin-status-badge--@(shop.Status == "active" ? "online" : "setup")">
<span class="admin-status-badge__dot"></span>
@(shop.Status == "active" ? "Đang mở" : "Thiết lập")
</div>
</div>
</div>
<div class="admin-status-badge admin-status-badge--online">
<span class="admin-status-badge__dot"></span>
Đang mở
</div>
</div>
<div class="admin-store-card__stats">
<div class="admin-store-stat">
<div class="admin-store-stat__value">45.2M</div>
<div class="admin-store-stat__label">Doanh thu</div>
</div>
<div class="admin-store-stat">
<div class="admin-store-stat__value">342</div>
<div class="admin-store-stat__label">Đơn hàng</div>
</div>
<div class="admin-store-stat">
<div class="admin-store-stat__value">5</div>
<div class="admin-store-stat__label">Nhân viên</div>
</div>
<div class="admin-store-stat">
<div class="admin-store-stat__value">48</div>
<div class="admin-store-stat__label">Sản phẩm</div>
</div>
</div>
</div>
@* Store 2: Nhà hàng Q3 *@
<div class="admin-store-card">
<div class="admin-store-card__top">
<div class="admin-store-card__info">
<div class="admin-store-card__avatar" style="background-color:rgba(59,130,246,0.125);">
<i data-lucide="utensils" style="color:#3B82F6;"></i>
</div>
<div>
<div class="admin-store-card__name">Nhà hàng Q3</div>
<div class="admin-store-card__type">Restaurant • 456 Lê Văn Sỹ, Q3</div>
</div>
</div>
<div class="admin-status-badge admin-status-badge--online">
<span class="admin-status-badge__dot"></span>
Đang mở
</div>
</div>
<div class="admin-store-card__stats">
<div class="admin-store-stat">
<div class="admin-store-stat__value">62.8M</div>
<div class="admin-store-stat__label">Doanh thu</div>
</div>
<div class="admin-store-stat">
<div class="admin-store-stat__value">185</div>
<div class="admin-store-stat__label">Đơn hàng</div>
</div>
<div class="admin-store-stat">
<div class="admin-store-stat__value">8</div>
<div class="admin-store-stat__label">Nhân viên</div>
</div>
<div class="admin-store-stat">
<div class="admin-store-stat__value">72</div>
<div class="admin-store-stat__label">Sản phẩm</div>
</div>
</div>
</div>
@* Store 3: Karaoke Star Q7 *@
<div class="admin-store-card">
<div class="admin-store-card__top">
<div class="admin-store-card__info">
<div class="admin-store-card__avatar" style="background-color:rgba(139,92,246,0.125);">
<i data-lucide="mic" style="color:#8B5CF6;"></i>
</div>
<div>
<div class="admin-store-card__name">Karaoke Star Q7</div>
<div class="admin-store-card__type">Karaoke • 789 Nguyễn Thị Thập, Q7</div>
</div>
</div>
<div class="admin-status-badge admin-status-badge--setup">
<span class="admin-status-badge__dot"></span>
Thiết lập
</div>
</div>
<div class="admin-store-card__stats">
<div class="admin-store-stat">
<div class="admin-store-stat__value">--</div>
<div class="admin-store-stat__label">Doanh thu</div>
</div>
<div class="admin-store-stat">
<div class="admin-store-stat__value">--</div>
<div class="admin-store-stat__label">Đơn hàng</div>
</div>
<div class="admin-store-stat">
<div class="admin-store-stat__value">0</div>
<div class="admin-store-stat__label">Nhân viên</div>
</div>
<div class="admin-store-stat">
<div class="admin-store-stat__value">0</div>
<div class="admin-store-stat__label">Sản phẩm</div>
</div>
</div>
<button class="admin-store-card__cta admin-store-card__cta--warning">
<i data-lucide="settings" style="width:14px;height:14px;"></i>
Hoàn tất thiết lập
</button>
</div>
}
}
</div>
</div>
@@ -316,3 +242,34 @@
</div>
</div>
</div>
@code {
private List<PosDataService.ShopInfo> _shops = new();
protected override async Task OnInitializedAsync()
{
IsLoading = true;
try
{
_shops = await DataService.GetShopsAsync();
}
catch
{
_shops = new();
}
finally
{
IsLoading = false;
}
}
private static string GetShopIcon(string? category) => category?.ToLowerInvariant() switch
{
"cafe" or "café" or "coffee" => "coffee",
"restaurant" or "nhà hàng" => "utensils",
"karaoke" => "mic",
"spa" => "sparkles",
"retail" => "shopping-bag",
_ => "store"
};
}