OnProfNext/OnProfNext.Client/Pages/Bookings/FancyView.razor
2025-10-17 10:41:53 +02:00

282 lines
9.6 KiB
Plaintext

@using OnProfNext.Client.Services
@using OnProfNext.Shared.Models.DTOs
@inject BookingApiService BookingService
@inject IJSRuntime JSRuntime
<div class="fancy-layout d-flex">
<div class="orders-sidebar card shadow-sm border-0 me-3" style="width: 250px;">
<div class="card-body">
<h5 class="text-primary mb-3">Verfügbare Aufträge</h5>
<div class="list-group">
@if (Orders == null || !Orders.Any())
{
<p class="text-muted">Keine Aufträge verfügbar</p>
}
else
{
@foreach (var order in Orders)
{
<div class="list-group-item list-group-item-action draggable-order"
draggable="true" @ondragstart="() => StartDrag(order.Id)">
@order.Titel (@order.Auftragsnummer)
</div>
}
}
</div>
</div>
</div>
<div class="week-calendar flex-grow-1">
<div class="d-flex justify-content-between align-items-center mb-3">
<button class="btn btn-outline-primary" @onclick="PreviousWeek">
<i class="bi bi-chevron-left"></i> Vorherige Woche
</button>
<h5 class="text-primary m-0">
Woche vom @CurrentWeekStart.ToString("dd.MM.yyyy") bis @CurrentWeekStart.AddDays(6).ToString("dd.MM.yyyy")
</h5>
<button class="btn btn-outline-primary" @onclick="NextWeek">
Nächste Woche <i class="bi bi-chevron-right"></i>
</button>
</div>
<table class="table table-bordered text-center">
<thead class="table-light">
<tr>
<th style="width: 80px;">Zeit</th>
@foreach (var day in WeekDays)
{
<th class="@(IsWeekend(day) ? "weekend" : "")">
@day.ToString("dd.MM") (@day.ToString("ddd"))
<br />
<small class="@GetHoursColorClass(day)">@GetHoursForDay(day) h</small>
</th>
}
</tr>
</thead>
<tbody>
@for (int hour = 8; hour <= 18; hour++)
{
var localHour = hour;
<tr>
<td>@localHour:00</td>
@foreach (var day in WeekDays)
{
var localDay = day;
<td class="@(IsWeekend(localDay) ? "weekend" : "")"
@ondrop="() => DropOnSlot(localDay, localHour)" @ondragover="AllowDrop"
style="height: 50px; position: relative;">
@foreach (var booking in GetBookingsForSlot(localDay, localHour))
{
<div class="booking-item bg-primary text-white p-1"
style="position: absolute; top: 0; left: 0; right: 0; height: @(booking.Hours * 50)px;"
title="@booking.OrderTitle: @booking.Hours h">
@booking.OrderTitle - @booking.Hours h
<button class="btn btn-sm btn-danger float-end"
@onclick="() => DeleteBooking(booking.Id)">
<i class="bi bi-trash"></i>
</button>
</div>
}
</td>
}
</tr>
}
</tbody>
</table>
</div>
</div>
@code {
[Parameter] public List<BookingDto>? Bookings { get; set; }
[Parameter] public List<OrderDto>? Orders { get; set; }
[Parameter] public EventCallback OnBookingCreated { get; set; }
[Parameter] public EventCallback<string> OnError { get; set; }
private DateTime CurrentWeekStart = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek + 1);
private List<DateTime> WeekDays => Enumerable.Range(0, 7).Select(d => CurrentWeekStart.AddDays(d)).ToList();
private int? draggedOrderId;
private void PreviousWeek()
{
if (CurrentWeekStart.Year > DateTime.MinValue.Year)
{
CurrentWeekStart = CurrentWeekStart.AddDays(-7);
StateHasChanged();
}
}
private void NextWeek()
{
if (CurrentWeekStart.Year < DateTime.MaxValue.Year)
{
CurrentWeekStart = CurrentWeekStart.AddDays(7);
StateHasChanged();
}
}
private void StartDrag(int orderId)
{
draggedOrderId = orderId;
Console.WriteLine($"Start drag for Order ID: {orderId}");
}
private async Task AllowDrop()
{
await JSRuntime.InvokeVoidAsync("onProfNext.preventDefault");
}
private async Task DropOnSlot(DateTime day, int hour)
{
if (draggedOrderId == null) return;
var bookingDate = new DateTime(day.Year, day.Month, day.Day, hour, 0, 0, DateTimeKind.Local);
Console.WriteLine($"Calculated booking date: {bookingDate.ToString("dd.MM.yyyy HH:mm")} (Kind: {bookingDate.Kind})");
var newBooking = new BookingCreateDto
{
OrderId = draggedOrderId.Value,
Date = bookingDate,
Hours = 1,
MandantId = 1
};
Console.WriteLine($"Sending to API: Date={newBooking.Date.ToString("dd.MM.yyyy HH:mm")} (Kind: {newBooking.Date.Kind})");
try
{
var (success, error) = await BookingService.CreateBookingAsync(newBooking);
if (success)
{
await OnBookingCreated.InvokeAsync();
Console.WriteLine("Booking created successfully.");
}
else
{
await OnError.InvokeAsync(error ?? "Fehler beim Erstellen der Buchung.");
Console.WriteLine($"API error: {error}");
}
}
catch (Exception ex)
{
await OnError.InvokeAsync($"Fehler beim Erstellen der Buchung: {ex.Message}");
Console.WriteLine($"Exception in DropOnSlot: {ex.Message}");
}
draggedOrderId = null;
}
private async Task DeleteBooking(int bookingId)
{
try
{
var (success, error) = await BookingService.DeleteBookingAsync(bookingId);
if (success)
{
await OnBookingCreated.InvokeAsync();
Console.WriteLine("Booking deleted successfully.");
}
else
{
await OnError.InvokeAsync(error ?? "Fehler beim Löschen der Buchung.");
Console.WriteLine($"API error: {error}");
}
}
catch (Exception ex)
{
await OnError.InvokeAsync($"Fehler beim Löschen der Buchung: {ex.Message}");
Console.WriteLine($"Exception in DeleteBooking: {ex.Message}");
}
}
private List<BookingDto> GetBookingsForSlot(DateTime day, int hour)
{
var bookings = Bookings?.Where(b => b.Date.Date == day.Date && b.Date.Hour == hour).ToList() ?? new();
if (bookings.Any())
{
Console.WriteLine($"Bookings for {day.ToString("dd.MM.yyyy")} {hour}:00: {bookings.Count}");
foreach (var booking in bookings)
{
Console.WriteLine($"Booking ID={booking.Id}, Date={booking.Date.ToString("dd.MM.yyyy HH:mm")}, Hours={booking.Hours}");
}
}
return bookings;
}
private bool IsWeekend(DateTime day)
{
return day.DayOfWeek == DayOfWeek.Saturday || day.DayOfWeek == DayOfWeek.Sunday;
}
private decimal GetHoursForDay(DateTime day)
{
var hours = Bookings?.Where(b => b.Date.Date == day.Date).Sum(b => b.Hours) ?? 0;
if (hours > 0)
{
Console.WriteLine($"Hours for {day.ToString("dd.MM.yyyy")}: {hours}");
}
return hours;
}
private string GetHoursColorClass(DateTime day)
{
var hours = GetHoursForDay(day);
if (hours >= 8)
return "text-success";
else if (hours > 6 && hours < 8)
return "text-warning";
else
return "text-danger";
}
}
<style>
.orders-sidebar {
min-width: 200px;
}
.draggable-order {
cursor: grab;
padding: 10px;
margin-bottom: 5px;
}
.week-calendar td {
vertical-align: top;
min-height: 50px;
position: relative;
}
.week-calendar th.weekend {
background-color: #f8f9fa;
}
.week-calendar td.weekend {
background-color: #f8f9fa;
}
.week-calendar td:hover {
background-color: #e9ecef;
}
.week-calendar td.weekend:hover {
background-color: #e2e6ea;
}
.booking-item {
border-radius: 4px;
margin: 2px;
font-size: 0.9em;
overflow: hidden;
}
.text-success {
font-weight: bold;
}
.text-warning {
font-weight: bold;
}
.text-danger {
font-weight: bold;
}
</style>