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
This commit is contained in:
parent
38e9243bcd
commit
1b25978d9b
26 changed files with 3792 additions and 956 deletions
|
|
@ -0,0 +1,98 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlanTempus.Application.Features.Localization.Services;
|
||||
using System.Globalization;
|
||||
|
||||
namespace PlanTempus.Application.Features.Customers.Components;
|
||||
|
||||
public class CustomerDetailAppointmentsViewComponent : ViewComponent
|
||||
{
|
||||
private readonly ILocalizationService _localization;
|
||||
|
||||
public CustomerDetailAppointmentsViewComponent(ILocalizationService localization)
|
||||
{
|
||||
_localization = localization;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(string customerId)
|
||||
{
|
||||
var customer = CustomerDetailCatalog.Get(customerId);
|
||||
var culture = new CultureInfo("da-DK");
|
||||
|
||||
var model = new CustomerDetailAppointmentsViewModel
|
||||
{
|
||||
UpcomingTitle = "Kommende aftaler",
|
||||
HistoryTitle = "Tidligere aftaler",
|
||||
MoveButtonText = "Flyt",
|
||||
CancelButtonText = "Aflys",
|
||||
SeeAllText = "Se alle aftaler ->",
|
||||
DateHeader = "Dato",
|
||||
ServiceHeader = "Service",
|
||||
HairdresserHeader = "Frisor",
|
||||
DurationHeader = "Varighed",
|
||||
PriceHeader = "Pris",
|
||||
Upcoming = customer.Appointments.Upcoming.Select(a =>
|
||||
{
|
||||
var dateText = a.Date;
|
||||
if (DateTime.TryParse(a.Date, out var date))
|
||||
{
|
||||
dateText = date.ToString("dddd d. MMMM yyyy", culture);
|
||||
dateText = char.ToUpper(dateText[0]) + dateText[1..];
|
||||
}
|
||||
return new UpcomingAppointmentViewModel
|
||||
{
|
||||
FormattedDate = $"{dateText} kl. {a.Time}",
|
||||
Details = $"{a.Service} - {a.Hairdresser} - {a.Duration}"
|
||||
};
|
||||
}).ToList(),
|
||||
History = customer.Appointments.History.Select(a =>
|
||||
{
|
||||
var dateText = a.Date;
|
||||
if (DateTime.TryParse(a.Date, out var date))
|
||||
{
|
||||
dateText = date.ToString("d. MMM yyyy", culture);
|
||||
}
|
||||
return new HistoryAppointmentViewModel
|
||||
{
|
||||
FormattedDate = dateText,
|
||||
Service = a.Service,
|
||||
Hairdresser = a.Hairdresser,
|
||||
Duration = a.Duration,
|
||||
FormattedPrice = $"{a.Price:N0} kr".Replace(",", ".")
|
||||
};
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomerDetailAppointmentsViewModel
|
||||
{
|
||||
public required string UpcomingTitle { get; init; }
|
||||
public required string HistoryTitle { get; init; }
|
||||
public required string MoveButtonText { get; init; }
|
||||
public required string CancelButtonText { get; init; }
|
||||
public required string SeeAllText { get; init; }
|
||||
public required string DateHeader { get; init; }
|
||||
public required string ServiceHeader { get; init; }
|
||||
public required string HairdresserHeader { get; init; }
|
||||
public required string DurationHeader { get; init; }
|
||||
public required string PriceHeader { get; init; }
|
||||
public List<UpcomingAppointmentViewModel> Upcoming { get; init; } = new();
|
||||
public List<HistoryAppointmentViewModel> History { get; init; } = new();
|
||||
}
|
||||
|
||||
public class UpcomingAppointmentViewModel
|
||||
{
|
||||
public required string FormattedDate { get; init; }
|
||||
public required string Details { get; init; }
|
||||
}
|
||||
|
||||
public class HistoryAppointmentViewModel
|
||||
{
|
||||
public required string FormattedDate { get; init; }
|
||||
public required string Service { get; init; }
|
||||
public required string Hairdresser { get; init; }
|
||||
public required string Duration { get; init; }
|
||||
public required string FormattedPrice { get; init; }
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
@model PlanTempus.Application.Features.Customers.Components.CustomerDetailAppointmentsViewModel
|
||||
|
||||
<swp-detail-grid>
|
||||
<!-- Left Column -->
|
||||
<swp-card-column>
|
||||
<!-- Kommende aftaler -->
|
||||
<swp-card>
|
||||
<swp-card-header>
|
||||
<swp-card-title>@Model.UpcomingTitle</swp-card-title>
|
||||
</swp-card-header>
|
||||
@foreach (var appointment in Model.Upcoming)
|
||||
{
|
||||
<swp-appointment-card>
|
||||
<swp-appointment-date>
|
||||
@appointment.FormattedDate
|
||||
</swp-appointment-date>
|
||||
<swp-appointment-details>
|
||||
@appointment.Details
|
||||
</swp-appointment-details>
|
||||
<swp-appointment-actions>
|
||||
<swp-btn class="secondary">@Model.MoveButtonText</swp-btn>
|
||||
<swp-btn class="secondary">@Model.CancelButtonText</swp-btn>
|
||||
</swp-appointment-actions>
|
||||
</swp-appointment-card>
|
||||
}
|
||||
@if (!Model.Upcoming.Any())
|
||||
{
|
||||
<swp-empty-state>
|
||||
<p>Ingen kommende aftaler</p>
|
||||
</swp-empty-state>
|
||||
}
|
||||
</swp-card>
|
||||
</swp-card-column>
|
||||
|
||||
<!-- Right Column -->
|
||||
<swp-card-column>
|
||||
<!-- Tidligere aftaler -->
|
||||
<swp-card>
|
||||
<swp-card-header>
|
||||
<swp-card-title>@Model.HistoryTitle</swp-card-title>
|
||||
</swp-card-header>
|
||||
<swp-table>
|
||||
<swp-table-header>
|
||||
<span>@Model.DateHeader</span>
|
||||
<span>@Model.ServiceHeader</span>
|
||||
<span>@Model.HairdresserHeader</span>
|
||||
<span>@Model.DurationHeader</span>
|
||||
<span>@Model.PriceHeader</span>
|
||||
</swp-table-header>
|
||||
@foreach (var appointment in Model.History)
|
||||
{
|
||||
<swp-table-row>
|
||||
<span>@appointment.FormattedDate</span>
|
||||
<span>@appointment.Service</span>
|
||||
<span>@appointment.Hairdresser</span>
|
||||
<span>@appointment.Duration</span>
|
||||
<span class="mono">@appointment.FormattedPrice</span>
|
||||
</swp-table-row>
|
||||
}
|
||||
</swp-table>
|
||||
<swp-see-all>@Model.SeeAllText</swp-see-all>
|
||||
</swp-card>
|
||||
</swp-card-column>
|
||||
</swp-detail-grid>
|
||||
Loading…
Add table
Add a link
Reference in a new issue