PlanTempusApp/PlanTempus.Application/Features/Customers/Components/CustomerDetailStatistics/CustomerDetailStatisticsViewComponent.cs
Janus C. H. Knudsen 1b25978d9b Adds comprehensive customer detail view components
Implements full customer detail page with multiple feature-rich components including overview, economy, statistics, journal, appointments, giftcards, and activity sections

Creates reusable ViewComponents for different customer detail aspects with robust data modeling and presentation logic
2026-01-25 01:55:41 +01:00

162 lines
6.7 KiB
C#

using Microsoft.AspNetCore.Mvc;
using PlanTempus.Application.Features.Localization.Services;
namespace PlanTempus.Application.Features.Customers.Components;
public class CustomerDetailStatisticsViewComponent : ViewComponent
{
private readonly ILocalizationService _localization;
public CustomerDetailStatisticsViewComponent(ILocalizationService localization)
{
_localization = localization;
}
public IViewComponentResult Invoke(string customerId)
{
var customer = CustomerDetailCatalog.Get(customerId);
var stats = customer.Statistics;
// Calculate widths for the attendance bar
var total = stats.Attendance.Attended + stats.Attendance.Cancelled + stats.Attendance.NoShow;
string attendedWidth, cancelledWidth, noShowWidth;
bool allZero = total == 0;
if (allZero)
{
// All zero: gray bar with equal segments
attendedWidth = "33.33%";
cancelledWidth = "33.33%";
noShowWidth = "33.34%";
}
else
{
// Calculate percentages, but use 12px minimum for zero values
var attendedPercent = stats.Attendance.Attended * 100 / total;
var cancelledPercent = stats.Attendance.Cancelled * 100 / total;
var noShowPercent = stats.Attendance.NoShow * 100 / total;
attendedWidth = stats.Attendance.Attended == 0 ? "12px" : $"{attendedPercent}%";
cancelledWidth = stats.Attendance.Cancelled == 0 ? "12px" : $"{cancelledPercent}%";
noShowWidth = stats.Attendance.NoShow == 0 ? "12px" : $"{noShowPercent}%";
}
var model = new CustomerDetailStatisticsViewModel
{
// Attendance
AttendanceTitle = "Fremmøde & Pålidelighed",
Attended = stats.Attendance.Attended,
AttendedLabel = "Fremmøder",
Cancelled = stats.Attendance.Cancelled,
CancelledLabel = "Aflysninger",
NoShow = stats.Attendance.NoShow,
NoShowLabel = "No-shows",
ReliabilityPercent = stats.Attendance.ReliabilityPercent,
ReliabilityLabel = "Pålidelighed",
AttendedWidth = attendedWidth,
CancelledWidth = cancelledWidth,
NoShowWidth = noShowWidth,
AllZero = allZero,
// Service patterns
ServicePatternsTitle = "Service-mønstre",
TopServicesLabel = "Top 3 Services",
TopServices = stats.TopServices.Select((s, i) => new TopItemViewModel
{
Rank = i + 1,
Name = s.Name,
Count = s.Count
}).ToList(),
TopProductsLabel = "Top 3 Produkter",
TopProducts = stats.TopProducts.Select((p, i) => new TopItemViewModel
{
Rank = i + 1,
Name = p.Name,
Count = p.Count
}).ToList(),
// Booking behavior
BookingBehaviorTitle = "Booking-adfærd",
AvgBookingNotice = $"{stats.BookingBehavior.AvgBookingNoticeDays} dage",
AvgBookingNoticeLabel = "Gns. bookingvarsel",
PreferredDay = stats.BookingBehavior.PreferredDay,
PreferredDayLabel = "Foretrukken dag",
PreferredTimeSlot = stats.BookingBehavior.PreferredTimeSlot,
PreferredTimeSlotLabel = "Foretrukken tid",
OnlineBookingRate = $"{stats.BookingBehavior.OnlineBookingRate}%",
OnlineBookingRateLabel = "Online booking rate",
AvgCancellationNotice = $"{stats.BookingBehavior.AvgCancellationNoticeDays} dage",
AvgCancellationNoticeLabel = "Gns. aflysningsvarsel",
// Loyalty
LoyaltyTitle = "Loyalitet",
CustomerSinceYears = $"{stats.Loyalty.CustomerSinceYears:0.0} ar".Replace(".", ","),
CustomerSinceYearsLabel = "Kunde siden",
DaysSinceLastVisit = stats.Loyalty.DaysSinceLastVisit,
DaysSinceLastVisitLabel = "Dage siden sidst",
ChurnRisk = stats.Loyalty.ChurnRisk,
ChurnRiskLabel = "Churn-risiko",
AvgIntervalDays = $"{stats.Loyalty.AvgIntervalDays} dage",
AvgIntervalDaysLabel = "Gns. interval"
};
return View(model);
}
}
public class CustomerDetailStatisticsViewModel
{
// Attendance
public required string AttendanceTitle { get; init; }
public int Attended { get; init; }
public required string AttendedLabel { get; init; }
public int Cancelled { get; init; }
public required string CancelledLabel { get; init; }
public int NoShow { get; init; }
public required string NoShowLabel { get; init; }
public int ReliabilityPercent { get; init; }
public required string ReliabilityLabel { get; init; }
public required string AttendedWidth { get; init; }
public required string CancelledWidth { get; init; }
public required string NoShowWidth { get; init; }
public bool AllZero { get; init; }
// Service patterns
public required string ServicePatternsTitle { get; init; }
public required string TopServicesLabel { get; init; }
public List<TopItemViewModel> TopServices { get; init; } = new();
public required string TopProductsLabel { get; init; }
public List<TopItemViewModel> TopProducts { get; init; } = new();
// Booking behavior
public required string BookingBehaviorTitle { get; init; }
public required string AvgBookingNotice { get; init; }
public required string AvgBookingNoticeLabel { get; init; }
public required string PreferredDay { get; init; }
public required string PreferredDayLabel { get; init; }
public required string PreferredTimeSlot { get; init; }
public required string PreferredTimeSlotLabel { get; init; }
public required string OnlineBookingRate { get; init; }
public required string OnlineBookingRateLabel { get; init; }
public required string AvgCancellationNotice { get; init; }
public required string AvgCancellationNoticeLabel { get; init; }
// Loyalty
public required string LoyaltyTitle { get; init; }
public required string CustomerSinceYears { get; init; }
public required string CustomerSinceYearsLabel { get; init; }
public int DaysSinceLastVisit { get; init; }
public required string DaysSinceLastVisitLabel { get; init; }
public required string ChurnRisk { get; init; }
public required string ChurnRiskLabel { get; init; }
public required string AvgIntervalDays { get; init; }
public required string AvgIntervalDaysLabel { get; init; }
}
public class TopItemViewModel
{
public int Rank { get; init; }
public required string Name { get; init; }
public int Count { get; init; }
}