211 lines
5.8 KiB
Go
211 lines
5.8 KiB
Go
package controllers
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"s-class-backend/config"
|
|
"s-class-backend/helpers"
|
|
"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 User
|
|
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
|
|
}
|
|
|
|
// 🌟 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 (Mencegah bentrok jadwal)
|
|
var count int64
|
|
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,
|
|
).Count(&count)
|
|
|
|
if count > 0 {
|
|
c.JSON(http.StatusConflict, gin.H{"error": "Ruangan sudah dibooking atau sedang dalam antrean persetujuan pada jam tersebut!"})
|
|
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,
|
|
RoomID: input.RoomID,
|
|
StartTime: input.StartTime,
|
|
EndTime: input.EndTime,
|
|
Purpose: input.Purpose,
|
|
Status: statusPeminjaman, // <-- Gunakan variabel statusPeminjaman
|
|
}
|
|
|
|
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,
|
|
})
|
|
}
|
|
|
|
// 🌟 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
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"data": bookings})
|
|
}
|
|
|
|
type UpdateStatusInput struct {
|
|
Status string `json:"status" binding:"required"`
|
|
}
|
|
|
|
// UPDATE STATUS (ADMIN)
|
|
func UpdateBookingStatus(c *gin.Context) {
|
|
bookingID := c.Param("id")
|
|
|
|
var input UpdateStatusInput
|
|
if err := c.ShouldBindJSON(&input); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Format data salah"})
|
|
return
|
|
}
|
|
|
|
var booking models.Booking
|
|
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
|
|
}
|
|
|
|
switch input.Status {
|
|
case "Approved":
|
|
if booking.RedeemCode == "" {
|
|
booking.RedeemCode = helpers.GenerateRedeemCode()
|
|
}
|
|
case "Rejected", "Cancelled":
|
|
booking.RedeemCode = ""
|
|
}
|
|
|
|
booking.Status = input.Status
|
|
|
|
// 🌟 PERBAIKAN 2: Menghapus blok Save dan JSON ganda yang bikin error
|
|
if err := config.DB.Save(&booking).Error; err != nil {
|
|
fmt.Println("🔥 ERROR DARI DATABASE:", err.Error())
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"message": "Status booking berhasil diperbarui!",
|
|
"data": booking,
|
|
})
|
|
}
|
|
|
|
// INPUT UNTUK ESP32
|
|
type RedeemInput struct {
|
|
RedeemCode string `json:"redeem_code" binding:"required"`
|
|
}
|
|
|
|
// VERIFIKASI KODE DARI ESP32
|
|
func VerifyRedeemCode(c *gin.Context) {
|
|
var input RedeemInput
|
|
|
|
if err := c.ShouldBindJSON(&input); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Format data salah"})
|
|
return
|
|
}
|
|
|
|
var booking models.Booking
|
|
|
|
if err := config.DB.Preload("Room").First(&booking, "redeem_code = ? AND status = 'Approved'", input.RedeemCode).Error; err != nil {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Kode tidak valid, belum disetujui, atau sudah digunakan!"})
|
|
return
|
|
}
|
|
|
|
booking.Status = "Completed"
|
|
booking.RedeemCode = ""
|
|
config.DB.Save(&booking)
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"message": "Kode valid!",
|
|
"room": booking.Room.Name,
|
|
"status": "success",
|
|
})
|
|
}
|
|
|
|
// GET ALL BOOKINGS (Untuk Calendar View semua user)
|
|
func GetAllBookings(c *gin.Context) {
|
|
var bookings []models.Booking
|
|
|
|
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
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"status": "success",
|
|
"data": bookings,
|
|
})
|
|
} |