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
142 lines
6.5 KiB
Text
142 lines
6.5 KiB
Text
@model PlanTempus.Application.Features.Customers.Components.CustomerDetailStatisticsViewModel
|
|
|
|
<swp-detail-grid>
|
|
<!-- Fremmøde & Pålidelighed - spans both columns -->
|
|
<swp-card class="full-width">
|
|
<swp-card-header>
|
|
<swp-card-title>@Model.AttendanceTitle</swp-card-title>
|
|
</swp-card-header>
|
|
<div class="grid-4">
|
|
<swp-stat-card class="highlight">
|
|
<swp-stat-value>@Model.Attended</swp-stat-value>
|
|
<swp-stat-label>@Model.AttendedLabel</swp-stat-label>
|
|
</swp-stat-card>
|
|
<swp-stat-card class="warning">
|
|
<swp-stat-value>@Model.Cancelled</swp-stat-value>
|
|
<swp-stat-label>@Model.CancelledLabel</swp-stat-label>
|
|
</swp-stat-card>
|
|
<swp-stat-card class="danger">
|
|
<swp-stat-value>@Model.NoShow</swp-stat-value>
|
|
<swp-stat-label>@Model.NoShowLabel</swp-stat-label>
|
|
</swp-stat-card>
|
|
<swp-stat-card class="success">
|
|
<swp-stat-value>@Model.ReliabilityPercent%</swp-stat-value>
|
|
<swp-stat-label>@Model.ReliabilityLabel</swp-stat-label>
|
|
</swp-stat-card>
|
|
</div>
|
|
<swp-attendance-bar class="@(Model.AllZero ? "empty" : "")">
|
|
<swp-attendance-segment class="attended" style="width: @Model.AttendedWidth;">@Model.Attended</swp-attendance-segment>
|
|
<swp-attendance-segment class="cancelled" style="width: @Model.CancelledWidth;">@Model.Cancelled</swp-attendance-segment>
|
|
<swp-attendance-segment class="noshow" style="width: @Model.NoShowWidth;">@Model.NoShow</swp-attendance-segment>
|
|
</swp-attendance-bar>
|
|
</swp-card>
|
|
|
|
<!-- Left Column -->
|
|
<swp-card-column>
|
|
<!-- Service-mønstre -->
|
|
<swp-card>
|
|
<swp-card-header>
|
|
<swp-card-title>@Model.ServicePatternsTitle</swp-card-title>
|
|
</swp-card-header>
|
|
<div class="grid-2">
|
|
<div>
|
|
<swp-section-label class="small">@Model.TopServicesLabel</swp-section-label>
|
|
<swp-top-list>
|
|
@foreach (var service in Model.TopServices)
|
|
{
|
|
<swp-top-item>
|
|
<swp-top-rank>@service.Rank</swp-top-rank>
|
|
<swp-top-name>@service.Name</swp-top-name>
|
|
<swp-top-count>@(service.Count)x</swp-top-count>
|
|
</swp-top-item>
|
|
}
|
|
</swp-top-list>
|
|
</div>
|
|
<div>
|
|
<swp-section-label class="small">@Model.TopProductsLabel</swp-section-label>
|
|
<swp-top-list>
|
|
@foreach (var product in Model.TopProducts)
|
|
{
|
|
<swp-top-item>
|
|
<swp-top-rank>@product.Rank</swp-top-rank>
|
|
<swp-top-name>@product.Name</swp-top-name>
|
|
<swp-top-count>@(product.Count)x</swp-top-count>
|
|
</swp-top-item>
|
|
}
|
|
</swp-top-list>
|
|
</div>
|
|
</div>
|
|
</swp-card>
|
|
</swp-card-column>
|
|
|
|
<!-- Right Column -->
|
|
<swp-card-column>
|
|
<!-- Booking-adfærd -->
|
|
<swp-card>
|
|
<swp-card-header>
|
|
<swp-card-title>@Model.BookingBehaviorTitle</swp-card-title>
|
|
</swp-card-header>
|
|
<swp-kv-list>
|
|
<swp-kv-row>
|
|
<swp-kv-label>@Model.AvgBookingNoticeLabel</swp-kv-label>
|
|
<swp-kv-value>@Model.AvgBookingNotice</swp-kv-value>
|
|
</swp-kv-row>
|
|
<swp-kv-row>
|
|
<swp-kv-label>@Model.PreferredDayLabel</swp-kv-label>
|
|
<swp-kv-value>@Model.PreferredDay</swp-kv-value>
|
|
</swp-kv-row>
|
|
<swp-kv-row>
|
|
<swp-kv-label>@Model.PreferredTimeSlotLabel</swp-kv-label>
|
|
<swp-kv-value>@Model.PreferredTimeSlot</swp-kv-value>
|
|
</swp-kv-row>
|
|
<swp-kv-row>
|
|
<swp-kv-label>@Model.OnlineBookingRateLabel</swp-kv-label>
|
|
<swp-kv-value>@Model.OnlineBookingRate</swp-kv-value>
|
|
</swp-kv-row>
|
|
<swp-kv-row>
|
|
<swp-kv-label>@Model.AvgCancellationNoticeLabel</swp-kv-label>
|
|
<swp-kv-value>@Model.AvgCancellationNotice</swp-kv-value>
|
|
</swp-kv-row>
|
|
</swp-kv-list>
|
|
</swp-card>
|
|
|
|
<!-- Loyalitet -->
|
|
<swp-card>
|
|
<swp-card-header>
|
|
<swp-card-title>@Model.LoyaltyTitle</swp-card-title>
|
|
</swp-card-header>
|
|
<div class="grid-2 compact">
|
|
<swp-stat-card>
|
|
<swp-stat-value class="small">@Model.CustomerSinceYears</swp-stat-value>
|
|
<swp-stat-label>@Model.CustomerSinceYearsLabel</swp-stat-label>
|
|
</swp-stat-card>
|
|
<swp-stat-card class="success">
|
|
<swp-stat-value class="small">@Model.DaysSinceLastVisit</swp-stat-value>
|
|
<swp-stat-label>@Model.DaysSinceLastVisitLabel</swp-stat-label>
|
|
</swp-stat-card>
|
|
<swp-stat-card>
|
|
<swp-stat-value class="small">
|
|
<swp-risk-indicator class="@Model.ChurnRisk">
|
|
<swp-risk-dot></swp-risk-dot>
|
|
@{
|
|
var riskText = Model.ChurnRisk switch
|
|
{
|
|
"low" => "Lav",
|
|
"medium" => "Medium",
|
|
"high" => "Hoj",
|
|
_ => Model.ChurnRisk
|
|
};
|
|
}
|
|
<span>@riskText</span>
|
|
</swp-risk-indicator>
|
|
</swp-stat-value>
|
|
<swp-stat-label>@Model.ChurnRiskLabel</swp-stat-label>
|
|
</swp-stat-card>
|
|
<swp-stat-card>
|
|
<swp-stat-value class="small">@Model.AvgIntervalDays</swp-stat-value>
|
|
<swp-stat-label>@Model.AvgIntervalDaysLabel</swp-stat-label>
|
|
</swp-stat-card>
|
|
</div>
|
|
</swp-card>
|
|
</swp-card-column>
|
|
</swp-detail-grid>
|