package controllers import ( "fmt" "net/http" "s-class-backend/config" "s-class-backend/helpers" // Import helper pembuat kode acak "s-class-backend/models" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" ) type BookingInput struct { RoomID uint `json:"room_id" binding:"required"` StartTime time.Time `json:"start_time" binding:"required"` EndTime time.Time `json:"end_time" binding:"required"` Purpose string `json:"purpose" binding:"required"` } func CreateBooking(c *gin.Context) { var input BookingInput if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // --- Handle UUID --- userIDInterface, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User ID tidak ditemukan"}) return } userIDStr, ok := userIDInterface.(string) if !ok { c.JSON(http.StatusInternalServerError, gin.H{"error": "Format User ID token salah"}) return } userID, err := uuid.Parse(userIDStr) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Gagal memproses User ID"}) return } // Validasi Waktu if input.EndTime.Before(input.StartTime) { c.JSON(http.StatusBadRequest, gin.H{"error": "Waktu selesai tidak boleh lebih awal dari waktu mulai!"}) return } // CEK BENTROK (Overlap Check) var count int64 config.DB.Model(&models.Booking{}).Where("room_id = ? AND status != 'Cancelled' AND ((start_time < ? AND end_time > ?) OR (start_time < ? AND end_time > ?) OR (start_time >= ? AND end_time <= ?))", input.RoomID, input.EndTime, input.StartTime, input.EndTime, input.StartTime, input.StartTime, input.EndTime).Count(&count) if count > 0 { c.JSON(http.StatusConflict, gin.H{"error": "Ruangan sudah dibooking pada jam tersebut!"}) return } // Simpan Booking booking := models.Booking{ UserID: userID, RoomID: input.RoomID, StartTime: input.StartTime, EndTime: input.EndTime, Purpose: input.Purpose, Status: "Pending", } if err := config.DB.Create(&booking).Error; err != nil { fmt.Println("Error DB:", err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "message": "Booking Berhasil diajukan!", "data": booking, }) } func GetUserBookings(c *gin.Context) { var bookings []models.Booking userID, _ := c.Get("user_id") role, _ := c.Get("role") if role == "admin" { if err := config.DB.Preload("Room").Preload("User").Order("created_at desc").Find(&bookings).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } } else { if err := config.DB.Preload("Room").Where("user_id = ?", userID).Order("created_at desc").Find(&bookings).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } } c.JSON(http.StatusOK, gin.H{"data": bookings}) } type UpdateStatusInput struct { Status string `json:"status" binding:"required"` } // --- FUNGSI UPDATE STATUS (ADMIN) --- func UpdateBookingStatus(c *gin.Context) { bookingID := c.Param("id") var booking models.Booking if err := config.DB.First(&booking, "booking_id = ?", bookingID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Booking tidak ditemukan"}) return } var input UpdateStatusInput if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } booking.Status = input.Status // 👇 LOGIKA PEMBUATAN REDEEM CODE 👇 if input.Status == "Approved" { // Buat kode dari helper jika sebelumnya belum punya kode if booking.RedeemCode == "" { booking.RedeemCode = helpers.GenerateRedeemCode() } } else if input.Status == "Rejected" || input.Status == "Cancelled" { // Kosongkan kode jika peminjaman ditolak/dibatalkan booking.RedeemCode = "" } config.DB.Save(&booking) c.JSON(http.StatusOK, gin.H{ "message": "Status booking berhasil diperbarui!", "data": booking, }) }