// internal/controllers/item_controller.go package controllers import ( "lost-and-found/internal/models" "lost-and-found/internal/services" "lost-and-found/internal/repositories" // ✅ TAMBAHKAN INI "lost-and-found/internal/utils" "net/http" "strconv" "log" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type ItemController struct { itemService *services.ItemService matchService *services.MatchService itemRepo *repositories.ItemRepository // ✅ TAMBAHKAN INI } func NewItemController(db *gorm.DB) *ItemController { return &ItemController{ itemService: services.NewItemService(db), matchService: services.NewMatchService(db), itemRepo: repositories.NewItemRepository(db), // ✅ INITIALIZE INI } } // ✅ FIXED GetItemByID // ✅ PASTIKAN response detail lengkap untuk manager // ✅ FIXED GetItemByIDfunc (c *ItemController) GetItemByID(ctx *gin.Context) { // ✅ FIXED GetItemByID - NOW RETURNS FULL DETAILS FOR MANAGER func (c *ItemController) GetItemByID(ctx *gin.Context) { id, err := strconv.ParseUint(ctx.Param("id"), 10, 32) if err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid item ID", err.Error()) return } // ✅ Check if user is manager or admin isManager := false if userObj, exists := ctx.Get("user"); exists { user := userObj.(*models.User) isManager = user.IsManager() || user.IsAdmin() } // ✅ Get item DIRECTLY from repository with PRELOAD item, err := c.itemRepo.FindByID(uint(id)) if err != nil { utils.ErrorResponse(ctx, http.StatusNotFound, "Item not found", err.Error()) return } // ✅ LOG untuk debug log.Printf("🔍 Controller GetItemByID - Item ID: %d", item.ID) log.Printf(" 📝 Description: %s", item.Description) log.Printf(" 🔒 SecretDetails: %s", item.SecretDetails) log.Printf(" 👤 ReporterName: %s", item.ReporterName) log.Printf(" 📞 ReporterContact: %s", item.ReporterContact) // ✅ Return response based on role if isManager { // Manager/Admin gets FULL details detailResponse := item.ToDetailResponse() // ✅ LOG response yang akan dikirim log.Printf("📤 Sending DetailResponse to Manager:") log.Printf(" Description: %s", detailResponse.Description) log.Printf(" SecretDetails: %s", detailResponse.SecretDetails) log.Printf(" ReporterName: %s", detailResponse.ReporterName) utils.SuccessResponse(ctx, http.StatusOK, "Item retrieved", detailResponse) } else { // Regular user gets public view only utils.SuccessResponse(ctx, http.StatusOK, "Item retrieved", item.ToPublicResponse()) } } func (c *ItemController) ReportFoundItemLinked(ctx *gin.Context) { userObj, _ := ctx.Get("user") user := userObj.(*models.User) var req services.CreateFoundItemLinkedRequest if err := ctx.ShouldBindJSON(&req); err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Data tidak valid", err.Error()) return } ipAddress := ctx.ClientIP() userAgent := ctx.Request.UserAgent() item, err := c.itemService.CreateFoundItemLinked(user.ID, req, ipAddress, userAgent) if err != nil { utils.ErrorResponse(ctx, http.StatusInternalServerError, "Gagal membuat laporan", err.Error()) return } message := "Laporan berhasil dibuat. Menunggu verifikasi Manager." if req.IsDirectToOwner { message = "Laporan berhasil! Notifikasi langsung dikirim ke pemilik barang." } utils.SuccessResponse(ctx, http.StatusCreated, message, item) } // CreateItem creates a new item // POST /api/items func (c *ItemController) CreateItem(ctx *gin.Context) { userObj, _ := ctx.Get("user") user := userObj.(*models.User) var req services.CreateItemRequest if err := ctx.ShouldBindJSON(&req); err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error()) return } ipAddress := ctx.ClientIP() userAgent := ctx.Request.UserAgent() item, err := c.itemService.CreateItem(user.ID, req, ipAddress, userAgent) if err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to create item", err.Error()) return } // Auto-match with lost items go c.matchService.AutoMatchNewItem(item.ID) utils.SuccessResponse(ctx, http.StatusCreated, "Item created", item.ToDetailResponse()) } // UpdateItem updates an item // PUT /api/items/:id func (c *ItemController) UpdateItem(ctx *gin.Context) { userObj, _ := ctx.Get("user") user := userObj.(*models.User) itemID, err := strconv.ParseUint(ctx.Param("id"), 10, 32) if err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid item ID", err.Error()) return } var req services.UpdateItemRequest if err := ctx.ShouldBindJSON(&req); err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error()) return } ipAddress := ctx.ClientIP() userAgent := ctx.Request.UserAgent() item, err := c.itemService.UpdateItem(user.ID, uint(itemID), req, ipAddress, userAgent) if err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to update item", err.Error()) return } utils.SuccessResponse(ctx, http.StatusOK, "Item updated", item.ToDetailResponse()) } // UpdateItemStatus updates item status // PATCH /api/items/:id/status func (c *ItemController) UpdateItemStatus(ctx *gin.Context) { userObj, _ := ctx.Get("user") user := userObj.(*models.User) itemID, err := strconv.ParseUint(ctx.Param("id"), 10, 32) if err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid item ID", err.Error()) return } var req struct { Status string `json:"status" binding:"required"` } if err := ctx.ShouldBindJSON(&req); err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error()) return } ipAddress := ctx.ClientIP() userAgent := ctx.Request.UserAgent() if err := c.itemService.UpdateItemStatus(user.ID, uint(itemID), req.Status, ipAddress, userAgent); err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to update status", err.Error()) return } utils.SuccessResponse(ctx, http.StatusOK, "Item status updated", nil) } // DeleteItem deletes an item // DELETE /api/items/:id func (c *ItemController) DeleteItem(ctx *gin.Context) { userObj, _ := ctx.Get("user") user := userObj.(*models.User) itemID, err := strconv.ParseUint(ctx.Param("id"), 10, 32) if err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid item ID", err.Error()) return } ipAddress := ctx.ClientIP() userAgent := ctx.Request.UserAgent() if err := c.itemService.DeleteItem(user.ID, uint(itemID), ipAddress, userAgent); err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to delete item", err.Error()) return } utils.SuccessResponse(ctx, http.StatusOK, "Item deleted", nil) } // GetItemsByReporter gets items by reporter // GET /api/user/items func (c *ItemController) GetItemsByReporter(ctx *gin.Context) { userObj, _ := ctx.Get("user") user := userObj.(*models.User) page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10")) items, total, err := c.itemService.GetItemsByReporter(user.ID, page, limit) if err != nil { utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get items", err.Error()) return } var responses []models.ItemDetailResponse for _, item := range items { responses = append(responses, item.ToDetailResponse()) } utils.SendPaginatedResponse(ctx, http.StatusOK, "Items retrieved", responses, total, page, limit) } // GetItemRevisionHistory gets revision history for an item // GET /api/items/:id/revisions func (c *ItemController) GetItemRevisionHistory(ctx *gin.Context) { itemID, err := strconv.ParseUint(ctx.Param("id"), 10, 32) if err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid item ID", err.Error()) return } page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10")) revisions, total, err := c.itemService.GetItemRevisionHistory(uint(itemID), page, limit) if err != nil { utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get revision history", err.Error()) return } utils.SendPaginatedResponse(ctx, http.StatusOK, "Revision history retrieved", revisions, total, page, limit) } func (c *ItemController) GetAllItems(ctx *gin.Context) { page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10")) status := ctx.Query("status") category := ctx.Query("category") search := ctx.Query("search") // ✅ CHECK IF USER IS MANAGER/ADMIN isManager := false if userObj, exists := ctx.Get("user"); exists { user := userObj.(*models.User) isManager = user.IsManager() || user.IsAdmin() } // ✅ FIXED: FORCE FILTER OUT EXPIRED for public users if !isManager { // 1. Jika user mencoba meminta status terlarang, paksa filter aman if status == models.ItemStatusExpired || status == models.ItemStatusCaseClosed { status = "!expired" } // 2. Jika status kosong (default), set ke !expired if status == "" { status = "!expired" } } items, total, err := c.itemService.GetAllItems(page, limit, status, category, search) if err != nil { utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get items", err.Error()) return } utils.SendPaginatedResponse(ctx, http.StatusOK, "Items retrieved", items, total, page, limit) }