diff --git a/main.go b/main.go index 1f94e99..d9baefa 100644 --- a/main.go +++ b/main.go @@ -311,8 +311,36 @@ func handleGetReservations(w http.ResponseWriter, r *http.Request) { return } + // Convert reservations to calendar events + type Event struct { + ID string `json:"id"` + Title string `json:"title"` + Start string `json:"start"` + End string `json:"end"` + DriverName string `json:"driverName"` + Vehicle string `json:"vehicle"` + Purpose string `json:"purpose"` + } + + var events []Event + for _, res := range reservations { + // Create proper ISO datetime strings + start := fmt.Sprintf("%sT%s:00", res.StartDate, res.StartTime) + end := fmt.Sprintf("%sT%s:00", res.EndDate, res.EndTime) + + events = append(events, Event{ + ID: res.ID, + Title: fmt.Sprintf("%s - %s", res.Vehicle, res.DriverName), + Start: start, + End: end, + DriverName: res.DriverName, + Vehicle: res.Vehicle, + Purpose: res.Purpose, + }) + } + w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(reservations) + json.NewEncoder(w).Encode(events) } func handleCreateReservation(w http.ResponseWriter, r *http.Request) { @@ -322,6 +350,9 @@ func handleCreateReservation(w http.ResponseWriter, r *http.Request) { return } + // Log received data for debugging + log.Printf("Received reservation data: %+v", reservation) + // Validate required fields if reservation.DriverName == "" || reservation.Vehicle == "" || reservation.StartDate == "" || reservation.StartTime == "" || @@ -330,18 +361,20 @@ func handleCreateReservation(w http.ResponseWriter, r *http.Request) { return } - // Create combined date-time string + // Create combined date-time string for validation startDateTime, err := time.Parse("2006-01-02 15:04", fmt.Sprintf("%s %s", reservation.StartDate, reservation.StartTime)) if err != nil { - http.Error(w, "Invalid start date/time", http.StatusBadRequest) + log.Printf("Error parsing start date/time: %v", err) + http.Error(w, "Invalid start date/time format", http.StatusBadRequest) return } endDateTime, err := time.Parse("2006-01-02 15:04", fmt.Sprintf("%s %s", reservation.EndDate, reservation.EndTime)) if err != nil { - http.Error(w, "Invalid end date/time", http.StatusBadRequest) + log.Printf("Error parsing end date/time: %v", err) + http.Error(w, "Invalid end date/time format", http.StatusBadRequest) return } @@ -351,6 +384,17 @@ func handleCreateReservation(w http.ResponseWriter, r *http.Request) { return } + // Check availability + available, err := checkReservationAvailability(reservation.Vehicle, startDateTime, endDateTime) + if err != nil { + http.Error(w, "Failed to check availability", http.StatusInternalServerError) + return + } + if !available { + http.Error(w, "Selected time slot is not available", http.StatusConflict) + return + } + // Generate unique ID reservation.ID = fmt.Sprintf("res_%d", time.Now().UnixNano()) @@ -368,10 +412,40 @@ func handleCreateReservation(w http.ResponseWriter, r *http.Request) { return } + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(reservation) } +// Add helper function to check availability +func checkReservationAvailability(vehicle string, start, end time.Time) (bool, error) { + reservations, err := loadReservations() + if err != nil { + return false, err + } + + for _, res := range reservations { + resStart, err := time.Parse("2006-01-02 15:04", + fmt.Sprintf("%s %s", res.StartDate, res.StartTime)) + if err != nil { + continue + } + + resEnd, err := time.Parse("2006-01-02 15:04", + fmt.Sprintf("%s %s", res.EndDate, res.EndTime)) + if err != nil { + continue + } + + if res.Vehicle == vehicle && + !(end.Before(resStart) || start.After(resEnd)) { + return false, nil + } + } + + return true, nil +} + func handleCheckAvailability(w http.ResponseWriter, r *http.Request) { // Get query parameters vehicle := r.URL.Query().Get("vehicle") @@ -386,7 +460,7 @@ func handleCheckAvailability(w http.ResponseWriter, r *http.Request) { return } - // Parse dates in correct format (YYYY-MM-DD HH:MM) + // Parse the dates with specific format startDateTime, err := time.Parse("2006-01-02 15:04", fmt.Sprintf("%s %s", startDate, startTime)) if err != nil { @@ -401,23 +475,17 @@ func handleCheckAvailability(w http.ResponseWriter, r *http.Request) { return } - // Validate time order - if endDateTime.Before(startDateTime) { - http.Error(w, "End time must be after start time", http.StatusBadRequest) - return - } - - // Check availability + // Load existing reservations reservations, err := loadReservations() if err != nil { http.Error(w, "Failed to load reservations", http.StatusInternalServerError) return } + // Check for conflicts available := true for _, res := range reservations { if res.Vehicle == vehicle { - // Parse reservation dates resStart, err := time.Parse("2006-01-02 15:04", fmt.Sprintf("%s %s", res.StartDate, res.StartTime)) if err != nil { @@ -430,15 +498,14 @@ func handleCheckAvailability(w http.ResponseWriter, r *http.Request) { continue } - // Check for overlap - if startDateTime.Before(resEnd) && endDateTime.After(resStart) { + // Check if there is any overlap + if !(endDateTime.Before(resStart) || startDateTime.After(resEnd)) { available = false break } } } - // Return response w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]bool{"available": available}) } diff --git a/rezervace-aut.html b/rezervace-aut.html index 3137392..4da515a 100644 --- a/rezervace-aut.html +++ b/rezervace-aut.html @@ -454,23 +454,36 @@ locale: 'cs', slotMinTime: '06:00:00', slotMaxTime: '22:00:00', - slotDuration: '01:00:00', // Set slot duration to 1 hour - snapDuration: '01:00:00', // Snap to hour intervals + slotDuration: '01:00:00', + snapDuration: '01:00:00', allDaySlot: false, - businessHours: { - daysOfWeek: [1, 2, 3, 4, 5], + events: '/api/reservations', + eventDisplay: 'block', + eventTimeFormat: { + hour: '2-digit', + minute: '2-digit', + hour12: false + }, + eventContent: function(arg) { + return { + html: ` +
+
${arg.event.extendedProps.driverName || 'Rezervace'}
+
${arg.event.extendedProps.purpose || ''}
+
+ ` + }; + }, + // Add these properties to ensure proper event rendering + eventConstraint: { startTime: '06:00', endTime: '22:00', + dows: [0,1,2,3,4,5,6] }, - events: '/api/reservations', - eventClick: function(info) { - alert(` - Řidič: ${info.event.title} - Vozidlo: ${info.event.extendedProps.vehicle} - Účel: ${info.event.extendedProps.purpose} - Od: ${info.event.start.toLocaleString()} - Do: ${info.event.end.toLocaleString()} - `); + selectConstraint: { + startTime: '06:00', + endTime: '22:00', + dows: [0,1,2,3,4,5,6] } }); @@ -479,17 +492,16 @@ // Check vehicle availability async function checkVehicleAvailability(vehicle, startDate, endDate) { try { - // Format dates correctly - const formatDateTime = (date) => { - return date.toISOString().split('.')[0]; // Remove milliseconds - }; + // Format dates for API + const startDateTime = new Date(startDate); + const endDateTime = new Date(endDate); const params = new URLSearchParams({ vehicle: vehicle, - startDate: startDate.toISOString().split('T')[0], - startTime: startDate.toTimeString().split(' ')[0].slice(0, 5), - endDate: endDate.toISOString().split('T')[0], - endTime: endDate.toTimeString().split(' ')[0].slice(0, 5) + startDate: startDateTime.toISOString().split('T')[0], + startTime: startDateTime.toTimeString().split(' ')[0].slice(0, 5), + endDate: endDateTime.toISOString().split('T')[0], + endTime: endDateTime.toTimeString().split(' ')[0].slice(0, 5) }); const response = await fetch(`/api/check-availability?${params}`); @@ -585,15 +597,16 @@ }); if (!response.ok) { - throw new Error('Chyba při vytváření rezervace'); + const errorData = await response.json(); + throw new Error(errorData.message || 'Failed to create reservation'); } showMessage('Rezervace byla úspěšně vytvořena', 'success'); - calendar.refetchEvents(); reservationForm.reset(); + calendar.refetchEvents(); } catch (error) { console.error('Error:', error); - showMessage(error.message || 'Nepodařilo se vytvořit rezervaci', 'error'); + showMessage(error.message || 'Chyba při vytváření rezervace', 'error'); } });