282 lines
9.6 KiB
Plaintext
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> |