diff --git a/rezervace-aut.html b/rezervace-aut.html
index 7134a62..c5de475 100644
--- a/rezervace-aut.html
+++ b/rezervace-aut.html
@@ -211,6 +211,103 @@
font-size: 0.875rem;
}
+ /* Enhanced calendar event styles */
+ .fc-event {
+ border: none !important;
+ border-radius: 6px !important;
+ padding: 4px 6px !important;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
+ margin: 2px 0 !important;
+ background: white !important;
+ }
+
+ .fc-event-title {
+ font-weight: 500 !important;
+ font-size: 0.9rem !important;
+ }
+
+ .fc-event-time {
+ font-weight: 600 !important;
+ opacity: 0.8 !important;
+ }
+
+ /* Vehicle-specific event colors */
+ .event-vw-caddy {
+ border-left: 4px solid #1a73e8 !important;
+ background: #e6f3ff !important;
+ }
+
+ .event-vw-golf {
+ border-left: 4px solid #28a745 !important;
+ background: #e6ffe6 !important;
+ }
+
+ .event-skoda-fabia {
+ border-left: 4px solid #fd7e14 !important;
+ background: #fff3e6 !important;
+ }
+
+ .event-bmw-218d {
+ border-left: 4px solid #6f42c1 !important;
+ background: #f3e6ff !important;
+ }
+
+ .event-skoda-superb {
+ border-left: 4px solid #dc3545 !important;
+ background: #ffe6e6 !important;
+ }
+
+ /* Reservation list styles */
+ .reservations-list {
+ margin-top: 2rem;
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
+ overflow: hidden;
+ }
+
+ .reservations-list-header {
+ background: #f8fafc;
+ padding: 1rem;
+ border-bottom: 1px solid #e5e7eb;
+ }
+
+ .reservations-list-body {
+ max-height: 400px;
+ overflow-y: auto;
+ }
+
+ .reservation-item {
+ padding: 1rem;
+ border-bottom: 1px solid #e5e7eb;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ }
+
+ .reservation-item:last-child {
+ border-bottom: none;
+ }
+
+ .reservation-vehicle-badge {
+ padding: 0.25rem 0.75rem;
+ border-radius: 9999px;
+ font-size: 0.875rem;
+ font-weight: 500;
+ }
+
+ /* High traffic warning styles */
+ .high-traffic-warning {
+ display: none;
+ background: #fff3e6;
+ border-left: 4px solid #fd7e14;
+ padding: 0.75rem 1rem;
+ margin-top: 0.5rem;
+ border-radius: 0.375rem;
+ font-size: 0.875rem;
+ color: #c05621;
+ }
+
/* Form container styles */
.form-container {
max-width: 600px;
@@ -452,6 +549,44 @@
.reservation-modal .close-button:hover {
color: #333;
}
+
+ /* Availability and warning styles */
+ .form-group button[type="submit"]:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
+ #availabilityStatus, #highTrafficWarning {
+ display: flex;
+ align-items: center;
+ font-size: 0.875rem;
+ margin-top: 0.5rem;
+ padding: 0.5rem;
+ border-radius: 0.375rem;
+ transition: all 0.3s ease;
+ }
+
+ #availabilityStatus i, #highTrafficWarning i {
+ margin-right: 0.5rem;
+ }
+
+ #availabilityStatus.bg-green-50 {
+ background-color: #f0fdf4;
+ color: #166534;
+ border: 1px solid #bbf7d0;
+ }
+
+ #availabilityStatus.bg-red-50 {
+ background-color: #fef2f2;
+ color: #991b1b;
+ border: 1px solid #fecaca;
+ }
+
+ #highTrafficWarning {
+ background-color: #fff7ed;
+ color: #9a3412;
+ border: 1px solid #fed7aa;
+ }
@@ -517,15 +652,24 @@
-
@@ -538,7 +682,7 @@
class="w-full p-2 border border-gray-300 rounded-md">
-
+
@@ -695,16 +846,100 @@
const reservationForm = document.getElementById('reservationForm');
const statusMessage = document.getElementById('statusMessage');
const modal = document.getElementById('eventModal');
+ const highTrafficWarning = document.getElementById('highTrafficWarning');
+ const availabilityStatus = document.getElementById('availabilityStatus');
let currentEventId = null;
- // Initialize FullCalendar
- const calendarEl = document.getElementById('calendar'); calendar = new FullCalendar.Calendar(calendarEl, {
+ // Function to check availability and traffic
+ async function checkAvailabilityAndTraffic() {
+ const vehicle = document.getElementById('vehicle').value;
+ const startDate = document.getElementById('startDate').value;
+ const startTime = document.getElementById('startTime').value;
+ const endDate = document.getElementById('endDate').value;
+ const endTime = document.getElementById('endTime').value;
+
+ if (!vehicle || !startDate || !startTime || !endDate || !endTime) {
+ return;
+ }
+
+ try {
+ const response = await fetch(`/api/check-availability?vehicle=${encodeURIComponent(vehicle)}&startDate=${startDate}&startTime=${startTime}&endDate=${endDate}&endTime=${endTime}`);
+ const data = await response.json();
+
+ // Update availability status
+ availabilityStatus.classList.remove('hidden', 'bg-green-50', 'text-green-700', 'bg-red-50', 'text-red-700');
+
+ if (data.available) {
+ availabilityStatus.classList.add('bg-green-50', 'text-green-700');
+ availabilityStatus.innerHTML = '
Vozidlo je v tomto čase k dispozici';
+ } else {
+ availabilityStatus.classList.add('bg-red-50', 'text-red-700');
+ availabilityStatus.innerHTML = '
Vozidlo je v tomto čase již rezervované';
+ }
+ availabilityStatus.classList.remove('hidden');
+
+ // Check for high traffic
+ if (data.reservationCount >= 3) {
+ highTrafficWarning.querySelector('.warning-message').textContent =
+ `Toto vozidlo má v daný den již ${data.reservationCount} rezervací.`;
+ highTrafficWarning.classList.remove('hidden');
+ } else {
+ highTrafficWarning.classList.add('hidden');
+ }
+
+ // Disable submit button if not available
+ const submitButton = reservationForm.querySelector('button[type="submit"]');
+ submitButton.disabled = !data.available;
+ submitButton.classList.toggle('opacity-50', !data.available);
+ submitButton.classList.toggle('cursor-not-allowed', !data.available);
+
+ return data.available;
+ } catch (error) {
+ console.error('Error checking availability:', error);
+ return false;
+ }
+ }
+
+ // Add event listeners for form inputs
+ const formInputs = ['vehicle', 'startDate', 'startTime', 'endDate', 'endTime'];
+ formInputs.forEach(inputId => {
+ document.getElementById(inputId).addEventListener('change', checkAvailabilityAndTraffic);
+ });
+
+ // Modify form submission to check availability first
+ reservationForm.addEventListener('submit', async function(e) {
+ e.preventDefault();
+
+ const isAvailable = await checkAvailabilityAndTraffic();
+ if (!isAvailable) {
+ return;
+ }
+
+ // Rest of the form submission code
+ // ...existing code...
+ }); // Initialize calendar
+ const calendarEl = document.getElementById('calendar');
+ function createEventContent(arg) {
+ return {
+ html: `
+
+
${arg.timeText}
+
${arg.event.extendedProps.driverName}
+
${arg.event.extendedProps.vehicle}
+
+ `
+ };
+ }
+ const calendarConfig = {
initialView: 'timeGridWeek',
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
+ validRange: {
+ start: new Date().toISOString().split('T')[0]
+ },
locale: 'cs',
slotMinTime: '06:00:00',
slotMaxTime: '22:00:00',
@@ -716,41 +951,37 @@
eventTimeFormat: {
hour12: false
},
+ eventClassNames: function(arg) {
+ return ['event-' + arg.event.extendedProps.vehicle.toLowerCase().replace(/\s+/g, '-')];
+ },
+ eventContent: {
+ html: function(arg) {
+ return `
+
+
${arg.timeText}
+
${arg.event.extendedProps.driverName}
+
${arg.event.extendedProps.vehicle}
+
+ `;
+ }
+ },
dateClick: function(info) {
- // Store the timestamp of the last click
const now = Date.now();
if (this.lastClick && (now - this.lastClick < 300)) {
- // Double click detected
showReservationForm(info.date);
}
this.lastClick = now;
},
- eventContent: function(arg) {
- return {
- html: `
-
-
${arg.event.extendedProps.vehicle}
-
${arg.event.extendedProps.driverName}
- ${arg.event.extendedProps.purpose ? `
${arg.event.extendedProps.purpose}
` : ''}
-
- `
- };
- },
eventClick: function(info) {
showEventModal(info.event);
- },
- eventConstraint: {
- dows: [0,1,2,3,4,5,6]
- },
- selectConstraint: {
- startTime: '06:00',
- endTime: '22:00',
- dows: [0,1,2,3,4,5,6]
}
- });
+ };
+ calendar = new FullCalendar.Calendar(calendarEl, calendarConfig);
calendar.render();
+ // Rest of the JavaScript code
+ // ...existing code...
// Reservation modal functions
const reservationModal = document.getElementById('reservationModal');
const closeReservationModal = document.getElementById('closeReservationModal'); function showReservationForm(date) {
@@ -789,6 +1020,111 @@
}
}
+ // Function to check vehicle availability and high traffic
+ async function checkVehicleAvailability(vehicle, startDate, endDate) {
+ try {
+ const response = await fetch(`/api/check-availability?vehicle=${encodeURIComponent(vehicle)}&startDate=${startDate}&startTime=${startTime}&endDate=${endDate}&endTime=${endTime}`);
+ const data = await response.json();
+
+ // Check for high traffic (more than 3 reservations for the same vehicle on the same day)
+ if (data.reservationCount > 3) {
+ showHighTrafficWarning(vehicle);
+ } else {
+ hideHighTrafficWarning();
+ }
+
+ return data.available;
+ } catch (error) {
+ console.error('Error checking availability:', error);
+ return false;
+ }
+ }
+
+ // Function to show high traffic warning
+ function showHighTrafficWarning(vehicle) {
+ const warningEl = document.querySelector('.high-traffic-warning');
+ if (!warningEl) {
+ const warning = document.createElement('div');
+ warning.className = 'high-traffic-warning';
+ warning.innerHTML = `
+
+
+ Upozornění: Vozidlo ${vehicle} má v tento den vysoký počet rezervací.
+
+ `;
+ document.getElementById('vehicle').parentNode.appendChild(warning);
+ }
+ warningEl.style.display = 'block';
+ }
+
+ // Function to hide high traffic warning
+ function hideHighTrafficWarning() {
+ const warningEl = document.querySelector('.high-traffic-warning');
+ if (warningEl) {
+ warningEl.style.display = 'none';
+ }
+ }
+
+ // Function to update reservations list
+ function updateReservationsList() {
+ const reservationsList = document.getElementById('reservationsList');
+ const events = calendar.getEvents();
+
+ // Sort events by start date
+ events.sort((a, b) => a.start - b.start);
+
+ // Filter future events
+ const futureEvents = events.filter(event => event.start >= new Date());
+
+ if (futureEvents.length === 0) {
+ reservationsList.innerHTML = '
Žádné nadcházející rezervace
';
+ return;
+ }
+
+ reservationsList.innerHTML = futureEvents.map(event => {
+ const vehicleClass = 'event-' + event.extendedProps.vehicle.toLowerCase().replace(/\s+/g, '-');
+ return `
+
+
+ ${event.extendedProps.vehicle}
+
+
+
${event.extendedProps.driverName}
+
+ ${formatDateTime(event.start)} - ${formatDateTime(event.end)}
+
+
+
+ ${event.extendedProps.purpose || 'Bez účelu'}
+
+
+ `;
+ }).join('');
+ }
+
+ // Update the eventContent function to add vehicle-specific styling
+ // (Already defined in calendarConfig above, so this duplicate is removed)
+
+ // Call updateReservationsList when events change
+ calendar.on('eventAdd', updateReservationsList);
+ calendar.on('eventRemove', updateReservationsList);
+ calendar.on('eventChange', updateReservationsList);
+
+ // Initial update of reservations list
+ updateReservationsList();
+
+ // Vehicle select change handler
+ document.getElementById('vehicle').addEventListener('change', async function() {
+ const startDate = document.getElementById('startDate').value;
+ const startTime = document.getElementById('startTime').value;
+ const endDate = document.getElementById('endDate').value;
+ const endTime = document.getElementById('endTime').value;
+
+ if (startDate && startTime && endDate && endTime) {
+ await checkVehicleAvailability(this.value, startDate, endDate);
+ }
+ });
+
// Check vehicle availability
async function checkVehicleAvailability(vehicle, startDate, endDate) {
try {
@@ -1025,8 +1361,16 @@
reservationModal.style.display = 'block';
});
- // Rest of the JavaScript code
- // ...existing code...
+ // Initialize time dropdowns when page loads
+ document.addEventListener('DOMContentLoaded', function() {
+ populateTimeDropdowns();
+
+ // Set default dates to today
+ const today = new Date();
+ const dateStr = today.toISOString().split('T')[0];
+ document.getElementById('startDate').value = dateStr;
+ document.getElementById('endDate').value = dateStr;
+ });
});