Buchungsseite + Fancy Kalender
This commit is contained in:
139
OnProfNext.Client/Pages/Bookings/BookingEntry.razor
Normal file
139
OnProfNext.Client/Pages/Bookings/BookingEntry.razor
Normal file
@@ -0,0 +1,139 @@
|
||||
@page "/booking-entry"
|
||||
@using OnProfNext.Client.Services
|
||||
@using OnProfNext.Shared.Models.DTOs
|
||||
@inject BookingApiService BookingService
|
||||
@inject OrderApiService OrderService
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
<h3 class="mb-4 d-flex justify-content-between align-items-center">
|
||||
<span class="text-primary">Zeit buchen</span>
|
||||
<button class="btn btn-outline-secondary" @onclick="GoBack">
|
||||
<i class="bi bi-arrow-left"></i> Zurück
|
||||
</button>
|
||||
</h3>
|
||||
|
||||
@if (isLoading)
|
||||
{
|
||||
<div class="text-center mt-5">
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
<p class="mt-3 text-secondary">Daten werden geladen...</p>
|
||||
</div>
|
||||
}
|
||||
else if (errorMessage is not null)
|
||||
{
|
||||
<div class="alert alert-danger">@errorMessage</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="nav nav-tabs mb-3">
|
||||
<li class="nav-item">
|
||||
<button class="nav-link @(activeView == ViewType.Classic ? "active" : "")"
|
||||
@onclick="() => activeView = ViewType.Classic">
|
||||
<i class="bi bi-calendar-month me-1"></i> Klassische Ansicht
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="nav-link @(activeView == ViewType.Fancy ? "active" : "")"
|
||||
@onclick="() => activeView = ViewType.Fancy">
|
||||
<i class="bi bi-calendar-week me-1"></i> Fancy Ansicht
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content p-3 border rounded bg-white shadow-sm">
|
||||
@if (activeView == ViewType.Classic)
|
||||
{
|
||||
<ClassicView Bookings="MyBookings" Orders="AvailableOrders"
|
||||
OnBookingCreated="ReloadBookings" OnError="ShowError" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<FancyView Bookings="MyBookings" Orders="AvailableOrders"
|
||||
OnBookingCreated="ReloadBookings" OnError="ShowError" />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private enum ViewType { Classic, Fancy }
|
||||
private ViewType activeView = ViewType.Classic;
|
||||
private bool isLoading = true;
|
||||
private string? errorMessage;
|
||||
private List<BookingDto> MyBookings = new();
|
||||
private List<OrderDto> AvailableOrders = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadDataAsync();
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Lade Bookings des Users
|
||||
var (bookingSuccess, bookings, bookingError) = await BookingService.GetMyBookingsAsync();
|
||||
if (!bookingSuccess)
|
||||
{
|
||||
errorMessage = bookingError;
|
||||
return;
|
||||
}
|
||||
MyBookings = bookings ?? new();
|
||||
|
||||
// Lade Orders des Users
|
||||
var (orderSuccess, orders, orderError) = await OrderService.GetMyOrdersAsync();
|
||||
if (!orderSuccess)
|
||||
{
|
||||
errorMessage = orderError;
|
||||
return;
|
||||
}
|
||||
AvailableOrders = orders ?? new();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = $"Fehler beim Laden der Daten: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReloadBookings()
|
||||
{
|
||||
var (success, bookings, error) = await BookingService.GetMyBookingsAsync();
|
||||
if (success)
|
||||
{
|
||||
MyBookings = bookings ?? new();
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = error;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowError(string error)
|
||||
{
|
||||
errorMessage = error;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void GoBack() => Navigation.NavigateTo("/projects");
|
||||
}
|
||||
|
||||
<style>
|
||||
.nav-tabs .nav-link {
|
||||
color: #495057;
|
||||
border: none;
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
color: #0d6efd;
|
||||
font-weight: 600;
|
||||
border-color: #0d6efd;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
209
OnProfNext.Client/Pages/Bookings/ClassicView.razor
Normal file
209
OnProfNext.Client/Pages/Bookings/ClassicView.razor
Normal file
@@ -0,0 +1,209 @@
|
||||
@using OnProfNext.Client.Services
|
||||
@using OnProfNext.Shared.Models.DTOs
|
||||
@inject BookingApiService BookingService
|
||||
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<button class="btn btn-outline-primary" @onclick="PreviousMonth">
|
||||
<i class="bi bi-chevron-left"></i> Vorheriger Monat
|
||||
</button>
|
||||
<h5 class="text-primary m-0">@CurrentMonth.ToString("MMMM yyyy")</h5>
|
||||
<button class="btn btn-outline-primary" @onclick="NextMonth">
|
||||
Nächster Monat <i class="bi bi-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="monthly-calendar">
|
||||
<table class="table table-bordered text-center">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Mo</th>
|
||||
<th>Di</th>
|
||||
<th>Mi</th>
|
||||
<th>Do</th>
|
||||
<th>Fr</th>
|
||||
<th>Sa</th>
|
||||
<th>So</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var week in GetCalendarWeeks())
|
||||
{
|
||||
<tr>
|
||||
@foreach (var day in week)
|
||||
{
|
||||
<td class="@(day.Month == CurrentMonth.Month ? "" : "text-muted") @(IsWeekend(day) ? "weekend" : "")"
|
||||
@onclick="() => OpenModal(day)">
|
||||
@day.Day
|
||||
<br />
|
||||
<small class="@GetHoursColorClass(day)">@GetHoursForDay(day) h</small>
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (showModal)
|
||||
{
|
||||
<div class="modal fade show d-block" tabindex="-1" style="background-color: rgba(0,0,0,0.5);">
|
||||
<div class="modal-dialog modal-dialog-centered" @onclick:stopPropagation>
|
||||
<div class="modal-content border-0 shadow">
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title">Buchung für @selectedDate.ToString("dd.MM.yyyy")</h5>
|
||||
<button type="button" class="btn-close btn-close-white" @onclick="CloseModal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<EditForm Model="newBooking" OnValidSubmit="SaveBookingAsync">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Auftrag</label>
|
||||
<InputSelect class="form-select" @bind-Value="newBooking.OrderId">
|
||||
<option value="0" disabled>Auswählen...</option>
|
||||
@foreach (var order in Orders)
|
||||
{
|
||||
<option value="@order.Id">@order.Titel (@order.Auftragsnummer)</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Stunden</label>
|
||||
<InputNumber class="form-control" @bind-Value="newBooking.Hours" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Beschreibung</label>
|
||||
<InputTextArea class="form-control" @bind-Value="newBooking.Description" />
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="button" class="btn btn-secondary me-2" @onclick="CloseModal">Abbrechen</button>
|
||||
<button type="submit" class="btn btn-success">Speichern</button>
|
||||
</div>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public List<BookingDto> Bookings { get; set; } = new();
|
||||
[Parameter] public List<OrderDto> Orders { get; set; } = new();
|
||||
[Parameter] public EventCallback OnBookingCreated { get; set; }
|
||||
[Parameter] public EventCallback<string> OnError { get; set; }
|
||||
|
||||
private DateTime CurrentMonth = DateTime.Today;
|
||||
private bool showModal;
|
||||
private DateTime selectedDate;
|
||||
private BookingCreateDto newBooking = new();
|
||||
|
||||
private void PreviousMonth()
|
||||
{
|
||||
CurrentMonth = CurrentMonth.AddMonths(-1);
|
||||
}
|
||||
|
||||
private void NextMonth()
|
||||
{
|
||||
CurrentMonth = CurrentMonth.AddMonths(1);
|
||||
}
|
||||
|
||||
private List<DateTime[]> GetCalendarWeeks()
|
||||
{
|
||||
var weeks = new List<DateTime[]>();
|
||||
var firstDayOfMonth = new DateTime(CurrentMonth.Year, CurrentMonth.Month, 1);
|
||||
var firstMonday = firstDayOfMonth.AddDays(-(int)firstDayOfMonth.DayOfWeek + (firstDayOfMonth.DayOfWeek == DayOfWeek.Sunday ? 0 : 1));
|
||||
var currentDay = firstMonday;
|
||||
|
||||
for (int weekCount = 0; weekCount < 6; weekCount++)
|
||||
{
|
||||
// Prüfe, ob currentDay noch im gültigen DateTime-Bereich liegt
|
||||
if (currentDay.Year >= DateTime.MaxValue.Year)
|
||||
break;
|
||||
|
||||
var week = new DateTime[7];
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
week[i] = currentDay;
|
||||
// Prüfe, ob das Hinzufügen eines Tages möglich ist
|
||||
if (currentDay < DateTime.MaxValue.AddDays(-1))
|
||||
currentDay = currentDay.AddDays(1);
|
||||
else
|
||||
break;
|
||||
}
|
||||
weeks.Add(week);
|
||||
|
||||
// Wenn currentDay nicht mehr incrementiert werden kann, beende die Schleife
|
||||
if (currentDay >= DateTime.MaxValue.AddDays(-1))
|
||||
break;
|
||||
}
|
||||
return weeks;
|
||||
}
|
||||
|
||||
private bool IsWeekend(DateTime day)
|
||||
{
|
||||
return day.DayOfWeek == DayOfWeek.Saturday || day.DayOfWeek == DayOfWeek.Sunday;
|
||||
}
|
||||
|
||||
private decimal GetHoursForDay(DateTime day)
|
||||
{
|
||||
return Bookings.Where(b => b.Date.Date == day.Date).Sum(b => b.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";
|
||||
}
|
||||
|
||||
private void OpenModal(DateTime date)
|
||||
{
|
||||
selectedDate = date;
|
||||
newBooking = new() { Date = date, MandantId = 1 };
|
||||
showModal = true;
|
||||
}
|
||||
|
||||
private void CloseModal() => showModal = false;
|
||||
|
||||
private async Task SaveBookingAsync()
|
||||
{
|
||||
var (success, error) = await BookingService.CreateBookingAsync(newBooking);
|
||||
if (success)
|
||||
{
|
||||
await OnBookingCreated.InvokeAsync();
|
||||
CloseModal();
|
||||
}
|
||||
else
|
||||
{
|
||||
await OnError.InvokeAsync(error ?? "Fehler beim Speichern der Buchung.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<style>
|
||||
.monthly-calendar td {
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
vertical-align: top;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.monthly-calendar td:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.monthly-calendar td.weekend {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
</style>
|
||||
282
OnProfNext.Client/Pages/Bookings/FancyView.razor
Normal file
282
OnProfNext.Client/Pages/Bookings/FancyView.razor
Normal file
@@ -0,0 +1,282 @@
|
||||
@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>
|
||||
Reference in New Issue
Block a user