From 2a3440ebe93d178e3750ae03896ea1be60041928 Mon Sep 17 00:00:00 2001 From: "[Valentino Heman Budiarto]" <[hemanvalentino@gmail.com]> Date: Fri, 6 Mar 2026 13:53:39 +0700 Subject: [PATCH] 6 maret 2026 --- backend/cmd/main.go | 13 ++++--- backend/controllers/bookingcontroller.go | 48 +++++++++++++++++++----- frontend/app/history/page.tsx | 29 +++++++++++--- 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index 2799723..b8feca2 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -5,7 +5,7 @@ import ( "s-class-backend/config" "s-class-backend/controllers" "s-class-backend/middleware" - "s-class-backend/models" // <--- Pastikan ini ada! + "s-class-backend/models" "github.com/gin-gonic/gin" ) @@ -15,7 +15,6 @@ func main() { config.ConnectDatabase() // 2. AutoMigrate (Membuat tabel jika belum ada) - // Kita masukkan semua model di sini agar relasi terbentuk rapi config.DB.AutoMigrate(&models.User{}, &models.Room{}, &models.Booking{}) r := gin.Default() @@ -34,8 +33,10 @@ func main() { auth.POST("/login", controllers.Login) } + r.POST("/api/verify-code", controllers.VerifyRedeemCode) + protected := r.Group("/api") - protected.Use(middleware.AuthMiddleware()) + protected.Use(middleware.AuthMiddleware()) { protected.GET("/profile", func(c *gin.Context) { userID, _ := c.Get("user_id") @@ -48,9 +49,9 @@ func main() { protected.POST("/rooms", controllers.CreateRoom) // Bookings - protected.POST("/bookings", controllers.CreateBooking) - protected.GET("/bookings", controllers.GetUserBookings) - protected.PATCH("/bookings/:id", controllers.UpdateBookingStatus) + protected.POST("/bookings", controllers.CreateBooking) + protected.GET("/bookings", controllers.GetUserBookings) + protected.PATCH("/bookings/:id", controllers.UpdateBookingStatus) } r.Run(":8080") diff --git a/backend/controllers/bookingcontroller.go b/backend/controllers/bookingcontroller.go index 5490711..ae5494c 100644 --- a/backend/controllers/bookingcontroller.go +++ b/backend/controllers/bookingcontroller.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" "s-class-backend/config" - "s-class-backend/helpers" // Import helper pembuat kode acak + "s-class-backend/helpers" "s-class-backend/models" "time" @@ -26,7 +26,7 @@ func CreateBooking(c *gin.Context) { return } - // --- Handle UUID --- + //Handle UUID userIDInterface, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User ID tidak ditemukan"}) @@ -51,7 +51,7 @@ func CreateBooking(c *gin.Context) { return } - // CEK BENTROK (Overlap Check) + // (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) @@ -105,10 +105,10 @@ func GetUserBookings(c *gin.Context) { } type UpdateStatusInput struct { - Status string `json:"status" binding:"required"` + Status string `json:"status" binding:"required"` } -// --- FUNGSI UPDATE STATUS (ADMIN) --- +//UPDATE STATUS (ADMIN) func UpdateBookingStatus(c *gin.Context) { bookingID := c.Param("id") @@ -126,14 +126,12 @@ func UpdateBookingStatus(c *gin.Context) { booking.Status = input.Status - // 👇 LOGIKA PEMBUATAN REDEEM CODE 👇 + //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 = "" } @@ -143,4 +141,36 @@ func UpdateBookingStatus(c *gin.Context) { "message": "Status booking berhasil diperbarui!", "data": booking, }) -} \ No newline at end of file +} + +//INPUT UNTUK ESP3 +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", + }) +} diff --git a/frontend/app/history/page.tsx b/frontend/app/history/page.tsx index c34d16d..edcf68e 100644 --- a/frontend/app/history/page.tsx +++ b/frontend/app/history/page.tsx @@ -3,7 +3,7 @@ import { useEffect, useState } from "react"; import axios from "axios"; import { useRouter } from "next/navigation"; -import { ArrowLeft, Calendar, Clock, MapPin, AlertCircle, CheckCircle, XCircle } from "lucide-react"; +import { ArrowLeft, Calendar, Clock, MapPin, AlertCircle, CheckCircle, XCircle, Key } from "lucide-react"; interface Booking { booking_id: string; @@ -15,6 +15,7 @@ interface Booking { end_time: string; purpose: string; status: string; + redeem_code: string; created_at: string; } @@ -45,7 +46,6 @@ export default function HistoryPage() { } }; - // Fungsi untuk memformat tanggal agar enak dibaca (Contoh: 10 Feb 2026, 08:00) const formatDate = (isoString: string) => { const date = new Date(isoString); return date.toLocaleDateString("id-ID", { @@ -54,23 +54,21 @@ export default function HistoryPage() { }); }; - // Fungsi menentukan warna status const getStatusBadge = (status: string) => { switch (status) { case "Approved": return Disetujui; case "Rejected": return Ditolak; - default: // Pending + default: return Menunggu; } }; - if (loading) return
Memuat riwayat...
; + if (loading) return
Memuat riwayat...
; return (
- {/* Navbar Header */}