diff --git a/backend/cmd/main.go b/backend/cmd/main.go index 90fca3b..8b822e5 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -53,6 +53,7 @@ func main() { protected.POST("/bookings", controllers.CreateBooking) protected.GET("/bookings", controllers.GetAllBookings) protected.PUT("/bookings/:id/status", controllers.UpdateBookingStatus) + protected.GET("/my-bookings", controllers.GetMyBookings) // Admin (Manage Rooms) protected.PUT("/admin/rooms/:id/status", controllers.UpdateRoomStatus) diff --git a/backend/controllers/bookingcontroller.go b/backend/controllers/bookingcontroller.go index 102e7d2..9a7dadc 100644 --- a/backend/controllers/bookingcontroller.go +++ b/backend/controllers/bookingcontroller.go @@ -26,7 +26,7 @@ func CreateBooking(c *gin.Context) { return } - //Handle UUID + // Handle UUID User userIDInterface, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User ID tidak ditemukan"}) @@ -45,15 +45,18 @@ func CreateBooking(c *gin.Context) { return } + // 🌟 PERBAIKAN 1: Ambil Role User dari Token + userRoleInterface, _ := c.Get("role") + userRole, _ := userRoleInterface.(string) + // 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 } -// (Overlap Check) + // Overlap Check (Mencegah bentrok jadwal) var count int64 - // KITA UBAH STATUSNYA MENJADI: status IN ('Pending', 'Approved') config.DB.Model(&models.Booking{}).Where( "room_id = ? AND status IN ('Pending', 'Approved') 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, @@ -64,6 +67,12 @@ func CreateBooking(c *gin.Context) { return } + // 🌟 PERBAIKAN 1 Lanjutan: Penentuan Status Otomatis + statusPeminjaman := "Pending" // Default untuk student + if userRole == "lecturer" { + statusPeminjaman = "Approved" // Dosen otomatis disetujui! + } + // Simpan Booking booking := models.Booking{ UserID: userID, @@ -71,7 +80,7 @@ func CreateBooking(c *gin.Context) { StartTime: input.StartTime, EndTime: input.EndTime, Purpose: input.Purpose, - Status: "Pending", + Status: statusPeminjaman, // <-- Gunakan variabel statusPeminjaman } if err := config.DB.Create(&booking).Error; err != nil { @@ -86,18 +95,21 @@ func CreateBooking(c *gin.Context) { }) } -func GetUserBookings(c *gin.Context) { +// 🌟 PERBAIKAN 3: Fungsi khusus untuk mengambil riwayat pribadi +func GetMyBookings(c *gin.Context) { var bookings []models.Booking userID, _ := c.Get("user_id") role, _ := c.Get("role") if role == "admin" { + // Admin bisa melihat semua riwayat 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 { + // Mahasiswa/Dosen HANYA BISA melihat riwayat miliknya sendiri 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 @@ -122,13 +134,11 @@ func UpdateBookingStatus(c *gin.Context) { } var booking models.Booking - // KUNCI PERBAIKAN: Gunakan Where("booking_id = ?") agar tepat sasaran if err := config.DB.Where("booking_id = ?", bookingID).First(&booking).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Data booking tidak ditemukan di database"}) return } - // Gunakan Switch Case (Clean Code) yang sudah kita bahas sebelumnya switch input.Status { case "Approved": if booking.RedeemCode == "" { @@ -140,22 +150,10 @@ func UpdateBookingStatus(c *gin.Context) { booking.Status = input.Status + // 🌟 PERBAIKAN 2: Menghapus blok Save dan JSON ganda yang bikin error if err := config.DB.Save(&booking).Error; err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Gagal menyimpan ke database"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "message": "Status booking berhasil diperbarui!", - "data": booking, - }) - - booking.Status = input.Status - - // KITA BUKA KEDOK ERROR DARI POSTGRESQL: - if err := config.DB.Save(&booking).Error; err != nil { - fmt.Println("🔥 ERROR DARI DATABASE:", err.Error()) // Cetak teks merah di terminal - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) // Tembak ke pop-up web + fmt.Println("🔥 ERROR DARI DATABASE:", err.Error()) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } @@ -165,7 +163,7 @@ func UpdateBookingStatus(c *gin.Context) { }) } -// INPUT UNTUK ESP3 +// INPUT UNTUK ESP32 type RedeemInput struct { RedeemCode string `json:"redeem_code" binding:"required"` } @@ -201,15 +199,13 @@ func VerifyRedeemCode(c *gin.Context) { func GetAllBookings(c *gin.Context) { var bookings []models.Booking - // Preload digunakan untuk mengambil data Relasi (Nama Ruangan & Nama Peminjam) 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 } - // Perhatikan: responsenya menggunakan "data: bookings" agar cocok dengan Frontend React-mu c.JSON(http.StatusOK, gin.H{ "status": "success", "data": bookings, }) -} +} \ No newline at end of file diff --git a/frontend/app/history/page.tsx b/frontend/app/history/page.tsx index edcf68e..7a3888f 100644 --- a/frontend/app/history/page.tsx +++ b/frontend/app/history/page.tsx @@ -33,9 +33,10 @@ export default function HistoryPage() { fetchHistory(token); }, []); - const fetchHistory = async (token: string) => { +const fetchHistory = async (token: string) => { try { - const response = await axios.get("http://localhost:8080/api/bookings", { + // PERBAIKAN: Ubah /api/bookings menjadi /api/my-bookings + const response = await axios.get("http://localhost:8080/api/my-bookings", { headers: { Authorization: `Bearer ${token}` }, }); setBookings(response.data.data || []);