initial commit
This commit is contained in:
@@ -0,0 +1,546 @@
|
||||
using Indotalent.Infrastructure.Database;
|
||||
using Indotalent.Data.Entities;
|
||||
using Indotalent.Data.Enums;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Indotalent.Features.Root.Home.Cqrs;
|
||||
|
||||
// Chart data
|
||||
public class ChartDataPoint
|
||||
{
|
||||
public string? Label { get; set; }
|
||||
public double SalesAmount { get; set; }
|
||||
public double PurchaseAmount { get; set; }
|
||||
}
|
||||
|
||||
// SWM-specific DTOs
|
||||
public class BookingKpiDto
|
||||
{
|
||||
public string? ResourceName { get; set; }
|
||||
public int BookingCount { get; set; }
|
||||
public double WinRate { get; set; }
|
||||
public string? Color { get; set; }
|
||||
}
|
||||
|
||||
public class BookingStatusDto
|
||||
{
|
||||
public string? Source { get; set; }
|
||||
public int Count { get; set; }
|
||||
public double Percentage { get; set; }
|
||||
public string? Color { get; set; }
|
||||
}
|
||||
|
||||
public class CustomerGroupDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public int Count { get; set; }
|
||||
public double PctValue { get; set; }
|
||||
public string? BarColor { get; set; }
|
||||
}
|
||||
|
||||
public class VendorGroupDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public int Count { get; set; }
|
||||
public string? IconClass { get; set; }
|
||||
}
|
||||
|
||||
public class TopProductDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public double Quantity { get; set; }
|
||||
public double Share { get; set; }
|
||||
}
|
||||
|
||||
public class PaymentMethodDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
|
||||
public class DocGridDto
|
||||
{
|
||||
public string? Label { get; set; }
|
||||
public string? Value { get; set; }
|
||||
public string? Color { get; set; }
|
||||
}
|
||||
|
||||
public class CashFlowDto
|
||||
{
|
||||
public string? Label { get; set; }
|
||||
public string? Value { get; set; }
|
||||
public string? Color { get; set; }
|
||||
}
|
||||
|
||||
public class RecentSoDto
|
||||
{
|
||||
public string? Number { get; set; }
|
||||
public string? Customer { get; set; }
|
||||
public decimal Value { get; set; }
|
||||
public string? Stage { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public DateTimeOffset? Date { get; set; }
|
||||
}
|
||||
|
||||
public class ActivityFeedDto
|
||||
{
|
||||
public string? Title { get; set; }
|
||||
public string? Detail { get; set; }
|
||||
public string? TimeAgo { get; set; }
|
||||
public string? ChipLabel { get; set; }
|
||||
public string? ChipColor { get; set; }
|
||||
public string? AvatarBg { get; set; }
|
||||
public string? AvatarText { get; set; }
|
||||
}
|
||||
|
||||
public class PipelineSummaryDto
|
||||
{
|
||||
public string? Label { get; set; }
|
||||
public string? Value { get; set; }
|
||||
public string? IconColorClass { get; set; }
|
||||
}
|
||||
|
||||
public class DealStageDto
|
||||
{
|
||||
public string? Stage { get; set; }
|
||||
public int Count { get; set; }
|
||||
public string? Color { get; set; }
|
||||
}
|
||||
|
||||
public class SalesActivityDto
|
||||
{
|
||||
public string? Category { get; set; }
|
||||
public int Count { get; set; }
|
||||
public string? Color { get; set; }
|
||||
}
|
||||
|
||||
public class DashboardSwmResponse
|
||||
{
|
||||
// Core KPIs
|
||||
public int TotalCustomer { get; set; }
|
||||
public int TotalVendor { get; set; }
|
||||
public int TotalBookings { get; set; }
|
||||
public int ConfirmedBookings { get; set; }
|
||||
public int DraftBookings { get; set; }
|
||||
public int CancelledBookings { get; set; }
|
||||
public int TotalResources { get; set; }
|
||||
public int TotalBookingGroups { get; set; }
|
||||
public double BookingWinRate { get; set; }
|
||||
public int PendingSO { get; set; }
|
||||
public int PendingPO { get; set; }
|
||||
|
||||
// Financial
|
||||
public decimal SalesMTD { get; set; }
|
||||
public decimal PurchaseMTD { get; set; }
|
||||
public decimal TotalReceivable { get; set; }
|
||||
public decimal TotalPayable { get; set; }
|
||||
|
||||
// Collections
|
||||
public List<BookingKpiDto> BookingsByResource { get; set; } = new();
|
||||
public List<BookingKpiDto> BookingsByGroup { get; set; } = new();
|
||||
public List<BookingStatusDto> BookingStatuses { get; set; } = new();
|
||||
public List<CustomerGroupDto> CustomerGroups { get; set; } = new();
|
||||
public List<VendorGroupDto> VendorGroups { get; set; } = new();
|
||||
public List<TopProductDto> TopProducts { get; set; } = new();
|
||||
public List<PaymentMethodDto> PaymentMethods { get; set; } = new();
|
||||
public List<DocGridDto> DocGrid { get; set; } = new();
|
||||
public List<CashFlowDto> CashFlowMTD { get; set; } = new();
|
||||
public List<RecentSoDto> RecentSalesOrders { get; set; } = new();
|
||||
public List<ActivityFeedDto> RecentBookingsFeed { get; set; } = new();
|
||||
public List<ChartDataPoint> RevenueTrend { get; set; } = new();
|
||||
public List<ChartDataPoint> MonthlyBookingsTrend { get; set; } = new();
|
||||
public List<PipelineSummaryDto> PipelineSummary { get; set; } = new();
|
||||
public List<DealStageDto> BookingStages { get; set; } = new();
|
||||
public List<SalesActivityDto> BookingActivity { get; set; } = new();
|
||||
|
||||
// Formatted
|
||||
public string FormattedSales => FormatNumber(SalesMTD);
|
||||
public string FormattedPurchase => FormatNumber(PurchaseMTD);
|
||||
public string FormattedReceivable => FormatNumber(TotalReceivable);
|
||||
public string FormattedPayable => FormatNumber(TotalPayable);
|
||||
|
||||
private static string FormatNumber(decimal v)
|
||||
{
|
||||
if (v >= 1_000_000) return (v / 1_000_000m).ToString("F1") + "M";
|
||||
if (v >= 1_000) return (v / 1_000m).ToString("F1") + "K";
|
||||
return v.ToString("N0");
|
||||
}
|
||||
}
|
||||
|
||||
public record GetSwmDashboardQuery() : IRequest<DashboardSwmResponse>;
|
||||
|
||||
public class DashboardHandler : IRequestHandler<GetSwmDashboardQuery, DashboardSwmResponse>
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
|
||||
public DashboardHandler(AppDbContext context) => _context = context;
|
||||
|
||||
public async Task<DashboardSwmResponse> Handle(GetSwmDashboardQuery request, CancellationToken ct)
|
||||
{
|
||||
var today = DateTime.Today;
|
||||
var monthStart = new DateTime(today.Year, today.Month, 1);
|
||||
|
||||
// 1. Pull top 1000 bookings (with resource)
|
||||
var topBookings = await _context.Booking
|
||||
.AsNoTracking()
|
||||
.OrderByDescending(x => x.StartTime)
|
||||
.Take(1000)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Status,
|
||||
x.StartTime,
|
||||
x.Subject,
|
||||
x.AutoNumber,
|
||||
ResourceName = x.BookingResource != null ? x.BookingResource.Name : "N/A",
|
||||
ResourceId = x.BookingResourceId ?? "0",
|
||||
})
|
||||
.ToListAsync(ct);
|
||||
|
||||
// 2. Pull top 1000 sales orders (with customer)
|
||||
var topSO = await _context.SalesOrder
|
||||
.AsNoTracking()
|
||||
.Where(x => x.OrderDate != null)
|
||||
.OrderByDescending(x => x.OrderDate)
|
||||
.Take(1000)
|
||||
.Select(x => new
|
||||
{
|
||||
x.AutoNumber,
|
||||
x.OrderDate,
|
||||
x.AfterTaxAmount,
|
||||
x.OrderStatus,
|
||||
CustomerName = x.Customer != null ? x.Customer.Name : "N/A",
|
||||
})
|
||||
.ToListAsync(ct);
|
||||
|
||||
// 3. Pull top 1000 sales order items (with product)
|
||||
var topSOItems = await _context.SalesOrderItem
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Quantity != null)
|
||||
.OrderByDescending(x => x.SalesOrder!.OrderDate)
|
||||
.Take(1000)
|
||||
.Select(x => new
|
||||
{
|
||||
ProductName = x.Product != null ? x.Product.Name : "N/A",
|
||||
x.Quantity,
|
||||
})
|
||||
.ToListAsync(ct);
|
||||
|
||||
// 4. Pull all booking resources & groups (small tables, pull all)
|
||||
var allResources = await _context.BookingResource
|
||||
.AsNoTracking()
|
||||
.Select(x => new { x.Name, GroupName = x.BookingGroup != null ? x.BookingGroup.Name : "General" })
|
||||
.ToListAsync(ct);
|
||||
|
||||
// 5. Pull all customers & vendors (with groups)
|
||||
var allCustomers = await _context.Customer
|
||||
.AsNoTracking()
|
||||
.Select(x => new { GroupName = x.CustomerGroup != null ? x.CustomerGroup.Name : "General" })
|
||||
.ToListAsync(ct);
|
||||
|
||||
var allVendors = await _context.Vendor
|
||||
.AsNoTracking()
|
||||
.Select(x => new { GroupName = x.VendorGroup != null ? x.VendorGroup.Name : "General" })
|
||||
.ToListAsync(ct);
|
||||
|
||||
// 6. Pull payment methods
|
||||
var allPayMethods = await _context.PaymentReceive
|
||||
.AsNoTracking()
|
||||
.Select(x => new { MethodName = x.PaymentMethod != null ? x.PaymentMethod.Name : "Cash" })
|
||||
.ToListAsync(ct);
|
||||
|
||||
// === AGGREGATE IN MEMORY ===
|
||||
|
||||
// Core counts from bookings
|
||||
var totalBookings = topBookings.Count;
|
||||
var confirmedBookings = topBookings.Count(x => x.Status == BookingStatus.Confirmed);
|
||||
var draftBookings = topBookings.Count(x => x.Status == BookingStatus.Draft);
|
||||
var cancelledBookings = topBookings.Count(x => x.Status == BookingStatus.Cancelled);
|
||||
var bookingWinRate = totalBookings > 0 ? Math.Round((double)confirmedBookings / totalBookings * 100, 1) : 0;
|
||||
var totalResources = allResources.Select(x => x.Name).Distinct().Count();
|
||||
var totalGroups = allResources.Select(x => x.GroupName).Distinct().Count();
|
||||
var totalCustomer = allCustomers.Count;
|
||||
var totalVendor = allVendors.Count;
|
||||
|
||||
// Financial from topSO (already has dates and amounts)
|
||||
var salesMTD = topSO.Where(x => x.OrderDate >= monthStart).Sum(x => x.AfterTaxAmount ?? 0);
|
||||
var purchaseMTD = await _context.PurchaseOrder.AsNoTracking()
|
||||
.Where(x => x.OrderDate >= monthStart)
|
||||
.SumAsync(x => x.AfterTaxAmount ?? 0, ct);
|
||||
var totalReceivable = await _context.Invoice.AsNoTracking()
|
||||
.Where(x => x.InvoiceStatus == InvoiceStatus.Confirmed)
|
||||
.SumAsync(x => (x.SalesOrder != null ? x.SalesOrder.AfterTaxAmount : 0) ?? 0, ct);
|
||||
var totalPayable = await _context.Bill.AsNoTracking()
|
||||
.Where(x => x.BillStatus == BillStatus.Confirmed)
|
||||
.SumAsync(x => (x.PurchaseOrder != null ? x.PurchaseOrder.AfterTaxAmount : 0) ?? 0, ct);
|
||||
var pendingSO = topSO.Count(x => x.OrderStatus == SalesOrderStatus.Draft);
|
||||
var pendingPO = await _context.PurchaseOrder.AsNoTracking().CountAsync(x => x.OrderStatus == PurchaseOrderStatus.Draft, ct);
|
||||
|
||||
// Bookings by Resource (from topBookings)
|
||||
var whColors = new[] { "#2563eb", "#10b981", "#f59e0b", "#8b5cf6", "#ec4899", "#06b6d4", "#f97316", "#6366f1" };
|
||||
var bookingsByResource = topBookings
|
||||
.GroupBy(x => x.ResourceName)
|
||||
.Select((g, i) => new BookingKpiDto
|
||||
{
|
||||
ResourceName = g.Key,
|
||||
BookingCount = g.Count(),
|
||||
WinRate = g.Count() > 0 ? Math.Round((double)g.Count(x => x.Status == BookingStatus.Confirmed) / g.Count() * 100, 1) : 0,
|
||||
Color = whColors[i % whColors.Length]
|
||||
})
|
||||
.OrderByDescending(x => x.BookingCount)
|
||||
.Take(8)
|
||||
.ToList();
|
||||
|
||||
// Bookings by Group (from allResources)
|
||||
var bookingsByGroup = allResources
|
||||
.GroupBy(x => x.GroupName)
|
||||
.Select((g, i) => new BookingKpiDto
|
||||
{
|
||||
ResourceName = g.Key,
|
||||
BookingCount = g.Count(),
|
||||
WinRate = 0,
|
||||
Color = "#8b5cf6"
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// Booking Statuses (for doughnut chart)
|
||||
var statusColors = new[] { "#10b981", "#f59e0b", "#e11d48" };
|
||||
var statusLabels = new[] { "Confirmed", "Draft", "Cancelled" };
|
||||
var statusCounts = new[] { confirmedBookings, draftBookings, cancelledBookings };
|
||||
var totalStatus = statusCounts.Sum();
|
||||
var bookingStatuses = statusLabels.Select((l, i) => new BookingStatusDto
|
||||
{
|
||||
Source = l,
|
||||
Count = statusCounts[i],
|
||||
Percentage = totalStatus > 0 ? Math.Round((double)statusCounts[i] / totalStatus * 100, 1) : 0,
|
||||
Color = i < statusColors.Length ? statusColors[i] : "#94a3b8"
|
||||
}).ToList();
|
||||
|
||||
// Customer Groups (with progress bars)
|
||||
var customerGroups = allCustomers
|
||||
.GroupBy(x => x.GroupName)
|
||||
.Select(g => new CustomerGroupDto
|
||||
{
|
||||
Name = g.Key,
|
||||
Count = g.Count(),
|
||||
PctValue = totalCustomer > 0 ? Math.Min(Math.Round((double)g.Count() / totalCustomer * 100, 1), 100) : 0,
|
||||
BarColor = "#2563eb"
|
||||
})
|
||||
.OrderByDescending(x => x.Count)
|
||||
.Take(6)
|
||||
.ToList();
|
||||
|
||||
// Vendor Groups
|
||||
var vendorGroups = allVendors
|
||||
.GroupBy(x => x.GroupName)
|
||||
.Select(g => new VendorGroupDto
|
||||
{
|
||||
Name = g.Key,
|
||||
Count = g.Count(),
|
||||
IconClass = "gc7"
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// Top Products (from topSOItems)
|
||||
var totalQty = topSOItems.Sum(x => x.Quantity ?? 0);
|
||||
var topProducts = topSOItems
|
||||
.GroupBy(x => x.ProductName)
|
||||
.Select(g => new { Name = g.Key, Qty = g.Sum(x => (double)(x.Quantity ?? 0)) })
|
||||
.OrderByDescending(x => x.Qty)
|
||||
.Take(4)
|
||||
.Select(x => new TopProductDto
|
||||
{
|
||||
Name = x.Name,
|
||||
Quantity = x.Qty,
|
||||
Share = totalQty > 0 ? Math.Round(x.Qty / totalQty * 100, 1) : 0
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// Payment Methods
|
||||
var paymentMethods = allPayMethods
|
||||
.GroupBy(x => x.MethodName)
|
||||
.Select(g => new PaymentMethodDto { Name = g.Key, Count = g.Count() })
|
||||
.ToList();
|
||||
|
||||
// Doc Grid
|
||||
var docGrid = new List<DocGridDto> {
|
||||
new() { Label = "Sales Order", Value = await _context.SalesOrder.CountAsync(ct).ContinueWith(t => t.Result.ToString()), Color = "#10b981" },
|
||||
new() { Label = "Purchase Order", Value = await _context.PurchaseOrder.CountAsync(ct).ContinueWith(t => t.Result.ToString()), Color = "#ef4444" },
|
||||
new() { Label = "Invoice", Value = await _context.Invoice.CountAsync(ct).ContinueWith(t => t.Result.ToString()), Color = "#14b8a6" },
|
||||
new() { Label = "Bill", Value = await _context.Bill.CountAsync(ct).ContinueWith(t => t.Result.ToString()), Color = "#f97316" },
|
||||
new() { Label = "Bookings", Value = totalBookings.ToString(), Color = "#8b5cf6" },
|
||||
new() { Label = "Resources", Value = totalResources.ToString(), Color = "#6366f1" }
|
||||
};
|
||||
|
||||
// Cash Flow MTD
|
||||
var cashFlowMTD = new List<CashFlowDto> {
|
||||
new() { Label = "Inflow (Receive)", Value = await _context.PaymentReceive.Where(x => x.PaymentDate >= monthStart).SumAsync(x => x.PaymentAmount ?? 0, ct).ContinueWith(t => t.Result.ToString("N0")), Color = "#10b981" },
|
||||
new() { Label = "Outflow (Disburse)", Value = await _context.PaymentDisburse.Where(x => x.PaymentDate >= monthStart).SumAsync(x => x.PaymentAmount ?? 0, ct).ContinueWith(t => t.Result.ToString("N0")), Color = "#ef4444" }
|
||||
};
|
||||
|
||||
// Recent Sales Orders (from topSO)
|
||||
var recentSalesOrders = topSO
|
||||
.OrderByDescending(x => x.OrderDate)
|
||||
.Take(6)
|
||||
.Select(x => new RecentSoDto
|
||||
{
|
||||
Number = x.AutoNumber,
|
||||
Customer = x.CustomerName,
|
||||
Value = x.AfterTaxAmount ?? 0,
|
||||
Stage = x.OrderStatus.ToString(),
|
||||
Status = x.OrderStatus == SalesOrderStatus.Confirmed ? "Posted" : "Pending",
|
||||
Date = x.OrderDate.HasValue ? new DateTimeOffset(x.OrderDate.Value, TimeSpan.Zero) : null
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// Recent Bookings activity feed
|
||||
var recentBookingsFeed = topBookings
|
||||
.OrderByDescending(x => x.StartTime)
|
||||
.Take(3)
|
||||
.Select((b, i) =>
|
||||
{
|
||||
var statusClass = b.Status == BookingStatus.Confirmed ? "chip-green" : b.Status == BookingStatus.Draft ? "chip-amber" : "chip-red";
|
||||
var avatarBg = b.Status == BookingStatus.Confirmed ? "#10b981" : b.Status == BookingStatus.Draft ? "#f59e0b" : "#e11d48";
|
||||
var timeAgo = b.StartTime.HasValue
|
||||
? (DateTime.Now - b.StartTime.Value).TotalMinutes < 60
|
||||
? $"{(int)(DateTime.Now - b.StartTime.Value).TotalMinutes}m ago"
|
||||
: (DateTime.Now - b.StartTime.Value).TotalHours < 24
|
||||
? $"{(int)(DateTime.Now - b.StartTime.Value).TotalHours}h ago"
|
||||
: $"{(int)(DateTime.Now - b.StartTime.Value).TotalDays}d ago"
|
||||
: "";
|
||||
var initials = b.ResourceName.Length > 0 ? b.ResourceName[..Math.Min(2, b.ResourceName.Length)].ToUpper() : "BK";
|
||||
return new ActivityFeedDto
|
||||
{
|
||||
Title = $"{b.Subject} — {b.AutoNumber}",
|
||||
Detail = $"📍 {b.ResourceName} · {b.StartTime:MMM dd, yyyy}",
|
||||
TimeAgo = timeAgo,
|
||||
ChipLabel = b.Status.ToString(),
|
||||
ChipColor = statusClass,
|
||||
AvatarBg = avatarBg,
|
||||
AvatarText = initials
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
// Revenue Trend (from topSO + PurchaseOrder)
|
||||
var monthNames = new[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
var last6Months = Enumerable.Range(0, 6)
|
||||
.Select(i => today.AddMonths(-5 + i))
|
||||
.Select(d => new { d.Year, d.Month })
|
||||
.ToList();
|
||||
|
||||
var poMonthsRaw = await _context.PurchaseOrder.AsNoTracking()
|
||||
.Where(x => x.OrderDate != null)
|
||||
.Select(x => x.OrderDate!.Value)
|
||||
.ToListAsync(ct);
|
||||
|
||||
var revenueTrend = last6Months.Select(m =>
|
||||
{
|
||||
var salesAmount = topSO
|
||||
.Where(x => x.OrderDate.HasValue
|
||||
&& x.OrderDate.Value.Year == m.Year
|
||||
&& x.OrderDate.Value.Month == m.Month)
|
||||
.Sum(x => (double)(x.AfterTaxAmount ?? 0));
|
||||
var purchaseAmount = poMonthsRaw
|
||||
.Where(d => d.Year == m.Year && d.Month == m.Month)
|
||||
.Sum(d => (double)0); // Purchase amounts computed below
|
||||
// Actually compute PO amounts from DB for each month
|
||||
return new { Label = monthNames[m.Month - 1], SalesAmount = Math.Round(salesAmount / 1000.0, 1), m.Year, m.Month };
|
||||
}).ToList();
|
||||
|
||||
// Compute PO amounts for each of last 6 months
|
||||
var revenueTrendFinal = new List<ChartDataPoint>();
|
||||
foreach (var m in last6Months)
|
||||
{
|
||||
var ms = new DateTime(m.Year, m.Month, 1);
|
||||
var me = ms.AddMonths(1);
|
||||
var sa = topSO
|
||||
.Where(x => x.OrderDate.HasValue && x.OrderDate.Value.Year == m.Year && x.OrderDate.Value.Month == m.Month)
|
||||
.Sum(x => (double)(x.AfterTaxAmount ?? 0));
|
||||
var pa = await _context.PurchaseOrder.AsNoTracking()
|
||||
.Where(x => x.OrderDate >= ms && x.OrderDate < me)
|
||||
.SumAsync(x => (double?)x.AfterTaxAmount ?? 0, ct);
|
||||
revenueTrendFinal.Add(new ChartDataPoint
|
||||
{
|
||||
Label = monthNames[m.Month - 1],
|
||||
SalesAmount = Math.Round(sa / 1000.0, 1),
|
||||
PurchaseAmount = Math.Round(pa / 1000.0, 1)
|
||||
});
|
||||
}
|
||||
|
||||
// Monthly Bookings Trend
|
||||
var monthlyBookingsTrend = last6Months.Select(m =>
|
||||
{
|
||||
var count = topBookings
|
||||
.Where(x => x.StartTime.HasValue
|
||||
&& x.StartTime.Value.Year == m.Year
|
||||
&& x.StartTime.Value.Month == m.Month)
|
||||
.Count();
|
||||
return new ChartDataPoint
|
||||
{
|
||||
Label = monthNames[m.Month - 1],
|
||||
SalesAmount = count,
|
||||
PurchaseAmount = 0
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
// Pipeline Summary
|
||||
var pipelineSummary = new List<PipelineSummaryDto>
|
||||
{
|
||||
new() { Label = "Total Bookings", Value = totalBookings.ToString("N0"), IconColorClass = "gc1" },
|
||||
new() { Label = "Confirmed", Value = confirmedBookings.ToString("N0"), IconColorClass = "gc4" },
|
||||
new() { Label = "Cancelled", Value = cancelledBookings.ToString("N0"), IconColorClass = "gc10" }
|
||||
};
|
||||
|
||||
// Booking Stages
|
||||
var bookingStages = new List<DealStageDto>
|
||||
{
|
||||
new() { Stage = "Confirmed", Count = confirmedBookings, Color = "#10b981" },
|
||||
new() { Stage = "Draft", Count = draftBookings, Color = "#f59e0b" },
|
||||
new() { Stage = "Cancelled", Count = cancelledBookings, Color = "#e11d48" }
|
||||
};
|
||||
|
||||
// Booking Activity
|
||||
var bookingActivity = new List<SalesActivityDto>
|
||||
{
|
||||
new() { Category = "Confirmed", Count = confirmedBookings, Color = "#10b981" },
|
||||
new() { Category = "Draft", Count = draftBookings, Color = "#e11d48" },
|
||||
new() { Category = "Cancelled", Count = cancelledBookings, Color = "#e11d48" },
|
||||
new() { Category = "Total", Count = totalBookings, Color = "#6366f1" }
|
||||
};
|
||||
|
||||
return new DashboardSwmResponse
|
||||
{
|
||||
TotalCustomer = totalCustomer,
|
||||
TotalVendor = totalVendor,
|
||||
TotalBookings = totalBookings,
|
||||
ConfirmedBookings = confirmedBookings,
|
||||
DraftBookings = draftBookings,
|
||||
CancelledBookings = cancelledBookings,
|
||||
TotalResources = totalResources,
|
||||
TotalBookingGroups = totalGroups,
|
||||
BookingWinRate = bookingWinRate,
|
||||
PendingSO = pendingSO,
|
||||
PendingPO = pendingPO,
|
||||
SalesMTD = salesMTD,
|
||||
PurchaseMTD = purchaseMTD,
|
||||
TotalReceivable = totalReceivable,
|
||||
TotalPayable = totalPayable,
|
||||
BookingsByResource = bookingsByResource,
|
||||
BookingsByGroup = bookingsByGroup,
|
||||
BookingStatuses = bookingStatuses,
|
||||
CustomerGroups = customerGroups,
|
||||
VendorGroups = vendorGroups,
|
||||
TopProducts = topProducts,
|
||||
PaymentMethods = paymentMethods,
|
||||
DocGrid = docGrid,
|
||||
CashFlowMTD = cashFlowMTD,
|
||||
RecentSalesOrders = recentSalesOrders,
|
||||
RecentBookingsFeed = recentBookingsFeed,
|
||||
RevenueTrend = revenueTrendFinal,
|
||||
MonthlyBookingsTrend = monthlyBookingsTrend,
|
||||
PipelineSummary = pipelineSummary,
|
||||
BookingStages = bookingStages,
|
||||
BookingActivity = bookingActivity
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user