@page "/home" @using Microsoft.AspNetCore.Authorization @using Indotalent.Infrastructure.Authorization.Identity @using Indotalent.Features.Root.Home.Cqrs @using Indotalent.Features.Root.Home @attribute [Authorize(Roles = $"{ApplicationRoles.Admin},{ApplicationRoles.Member}")] @inject HomeService HomeService @inject IJSRuntime JS SWM Dashboard
@* HEADER *@

SWM Dashboard

@* ROW 1: 12-col KPI layout *@
@* Key Metrics Card *@

SWM Overview

Key Metrics

MTD Sales

@_data.FormattedSales

Total Bookings

@_data.TotalBookings.ToString("N0")

Win Rate

@_data.BookingWinRate%

Customers

@_data.TotalCustomer.ToString("N0")

AR: @_data.FormattedReceivable FY @DateTime.Now.Year
@* 8 Small KPI Cards (row 1, cols 6-12) *@

MTD Sales

@_data.FormattedSales

Receivable

@_data.FormattedReceivable

Win Rate

@_data.BookingWinRate%

Customers

@_data.TotalCustomer.ToString("N0")

Total Bookings

@_data.TotalBookings.ToString("N0")

Resources

@_data.TotalResources.ToString("N0")

Active SO

@_data.PendingSO.ToString("N0")

Booking Grps

@_data.TotalBookingGroups.ToString("N0")

@* Metrics Matrix *@

SWM Metrics Matrix

Key indicators across all categories

@{ var mmItems = new[] { new { Title = "MTD Sales", Sub = _data.FormattedSales, ColorClass = "mm-c1" }, new { Title = "Receivable", Sub = _data.FormattedReceivable, ColorClass = "mm-c2" }, new { Title = "Win Rate", Sub = $"{_data.BookingWinRate}%", ColorClass = "mm-c3" }, new { Title = "Customers", Sub = _data.TotalCustomer.ToString("N0"), ColorClass = "mm-c4" }, new { Title = "Total Bookings", Sub = _data.TotalBookings.ToString("N0"), ColorClass = "mm-c5" }, new { Title = "Resources", Sub = _data.TotalResources.ToString("N0"), ColorClass = "mm-c6" }, new { Title = "Pending SO", Sub = _data.PendingSO.ToString("N0"), ColorClass = "mm-c7" }, new { Title = "Pending PO", Sub = _data.PendingPO.ToString("N0"), ColorClass = "mm-c8" }, new { Title = "Confirmed", Sub = _data.ConfirmedBookings.ToString("N0"), ColorClass = "mm-c9" }, new { Title = "Draft", Sub = _data.DraftBookings.ToString("N0"), ColorClass = "mm-c10" }, new { Title = "Cancelled", Sub = _data.CancelledBookings.ToString("N0"), ColorClass = "mm-c11" }, new { Title = "Vendors", Sub = _data.TotalVendor.ToString("N0"), ColorClass = "mm-c12" }, new { Title = "Groups", Sub = _data.TotalBookingGroups.ToString("N0"), ColorClass = "mm-c13" }, new { Title = "MTD Purchase", Sub = _data.FormattedPurchase, ColorClass = "mm-c14" }, new { Title = "Payable", Sub = _data.FormattedPayable, ColorClass = "mm-c15" }, }; } @foreach (var item in mmItems) {

@item.Title

@item.Sub

}
@* ROW 2: Revenue Trend + Booking Status Doughnut *@

Revenue Trend

Monthly sales vs purchase

Sales (K) Purchase (K)

Booking Status

By category this period

@if (_data.BookingStatuses.Any()) { @foreach (var src in _data.BookingStatuses) {
@src.Source @src.Percentage%
} } else {
Confirmed 0%
Draft 0%
Cancelled 0%
}
@* ROW 3: Pipeline Summary + Booking Stages + Booking Activity *@

Booking Summary

As of @DateTime.Now.ToString("MMMM dd, yyyy")

@foreach (var ps in _data.PipelineSummary) {

@ps.Label

@ps.Value

}
Win Rate@_data.BookingWinRate% Total@_data.TotalBookings

Booking Stages

Breakdown by status

@foreach (var ds in _data.BookingStages) {

@ds.Stage

@ds.Count

}
Win Rate: @_data.BookingWinRate%Active: @_data.ConfirmedBookings

Booking Activity

Monthly movement

@foreach (var sa in _data.BookingActivity.Take(3)) {
@sa.Count

@sa.Category

}
@* ROW 4: Recent Sales Orders + Recent Bookings *@

Recent Sales Orders

Latest transactions

@if (_data.RecentSalesOrders.Any()) { @foreach (var deal in _data.RecentSalesOrders) { } } else { }
DateSO#CustomerValueStatus
@(deal.Date?.ToString("MMM dd") ?? "-") @deal.Number @deal.Customer @deal.Value.ToString("N0") @deal.Status
No recent sales orders available
Pending SO: @_data.PendingSO Total SO: @_data.RecentSalesOrders.Count

Recent Bookings

Latest spa & wellness bookings

Live
@if (_data.RecentBookingsFeed.Any()) { @foreach (var af in _data.RecentBookingsFeed) {
@af.AvatarText

@af.Title

@af.Detail

@af.TimeAgo

@af.ChipLabel
} } else {

No recent bookings

}
@* ROW 5: Customer Groups + Vendor Groups + Cash Flow *@

Customer Groups

Distribution by category

@if (_data.CustomerGroups.Any()) { @foreach (var cg in _data.CustomerGroups) {

@cg.Name

@cg.Count cust
} } else {

No customer group data available

}
Total: @_data.TotalCustomer

Vendor Groups

Partner distribution

@if (_data.VendorGroups.Any()) { @foreach (var vg in _data.VendorGroups) {

@vg.Name

@vg.Count vend

} } else {

No vendor group data available

}
Total: @_data.TotalVendor

Cash Flow MTD

@DateTime.Now.ToString("MMMM yyyy")

@foreach (var cf in _data.CashFlowMTD) {

@cf.Label

@cf.Value
}
Payable: @_data.FormattedPayable
@* ROW 6: Top Products + Payment Methods *@

Top Products

By quantity sold

@if (_data.TopProducts.Any()) { @foreach (var p in _data.TopProducts) { } } else { }
ProductQuantityShare
@p.Name @p.Quantity.ToString("N0") @p.Share%
No products available
Total Products: @_data.TopProducts.Count

Payment Methods

Transaction distribution

@if (_data.PaymentMethods.Any()) { @foreach (var pm in _data.PaymentMethods) { } } else { }
MethodCountStatus
@pm.Name @pm.Count Active
No payment methods available
Total Methods: @_data.PaymentMethods.Count
@code { private DashboardSwmResponse _data = new(); private bool _isLoading = true; private bool _dataReady = false; private bool _chartsInitialized = false; protected override async Task OnInitializedAsync() { _isLoading = true; try { var res = await HomeService.GetSwmDashboardStatsAsync(); if (res?.IsSuccess == true && res.Value != null) { _data = res.Value; _dataReady = true; StateHasChanged(); } } finally { _isLoading = false; } } protected override async Task OnAfterRenderAsync(bool firstRender) { if (_dataReady && !_chartsInitialized) { _chartsInitialized = true; await InitCharts(); } } private async Task InitCharts() { var revenueLabels = _data.RevenueTrend.Any() ? _data.RevenueTrend.Select(x => x.Label).ToArray() : new[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun" }; var revenueData = _data.RevenueTrend.Any() ? _data.RevenueTrend.Select(x => x.SalesAmount).ToArray() : new double[] { 0, 0, 0, 0, 0, 0 }; var expenseData = _data.RevenueTrend.Any() ? _data.RevenueTrend.Select(x => x.PurchaseAmount).ToArray() : new double[] { 0, 0, 0, 0, 0, 0 }; var sourceLabels = _data.BookingStatuses.Any() ? _data.BookingStatuses.Select(x => x.Source).ToArray() : new[] { "Confirmed", "Draft", "Cancelled" }; var sourceData = _data.BookingStatuses.Any() ? _data.BookingStatuses.Select(x => x.Percentage).Cast().ToArray() : new object[] { 0, 0, 0 }; var sourceColors = _data.BookingStatuses.Any() ? _data.BookingStatuses.Select(x => x.Color).ToArray() : new[] { "#10b981", "#f59e0b", "#e11d48" }; var activityLabels = _data.BookingActivity.Any() ? _data.BookingActivity.Take(4).Select(x => x.Category).ToArray() : new[] { "Confirmed", "Draft", "Cancelled", "Total" }; var activityCounts = _data.BookingActivity.Any() ? _data.BookingActivity.Take(4).Select(x => (double)x.Count).ToArray() : new double[] { 0, 0, 0, 0 }; var activityColors = _data.BookingActivity.Any() ? _data.BookingActivity.Take(4).Select(x => x.Color).ToArray() : new[] { "#10b981", "#e11d48", "#e11d48", "#6366f1" }; await JS.InvokeVoidAsync("initDashboardCharts", revenueLabels, revenueData, expenseData, sourceLabels, sourceData, sourceColors, activityLabels, activityCounts, activityColors); } }