package handlers import ( "net/http" "strconv" "time" "attune-heart-therapy/internal/services" "github.com/gin-gonic/gin" ) type AdminHandler struct { adminService services.AdminService } func NewAdminHandler(adminService services.AdminService) *AdminHandler { return &AdminHandler{ adminService: adminService, } } // GetDashboard handles GET /api/admin/dashboard for dashboard statistics func (h *AdminHandler) GetDashboard(c *gin.Context) { stats, err := h.adminService.GetDashboardStats() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to get dashboard statistics", "details": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "stats": stats, }) } // CreateSchedule handles POST /api/admin/schedules for schedule creation func (h *AdminHandler) CreateSchedule(c *gin.Context) { var req services.CreateScheduleRequest // Bind JSON request to struct if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid request format", "details": err.Error(), }) return } // Create the schedule schedule, err := h.adminService.CreateSchedule(req) if err != nil { // Handle specific error cases if err.Error() == "end time must be after start time" || err.Error() == "cannot create schedule slots in the past" || err.Error() == "max bookings must be at least 1" { c.JSON(http.StatusBadRequest, gin.H{ "error": err.Error(), }) return } if err.Error() == "schedule slot overlaps with existing available slot" { c.JSON(http.StatusConflict, gin.H{ "error": "Schedule slot overlaps with existing available slot", }) return } c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to create schedule", "details": err.Error(), }) return } c.JSON(http.StatusCreated, gin.H{ "message": "Schedule created successfully", "schedule": schedule, }) } // GetUsers handles GET /api/admin/users for retrieving all users func (h *AdminHandler) GetUsers(c *gin.Context) { // Parse pagination parameters limitStr := c.DefaultQuery("limit", "50") offsetStr := c.DefaultQuery("offset", "0") limit, err := strconv.Atoi(limitStr) if err != nil || limit <= 0 { limit = 50 } offset, err := strconv.Atoi(offsetStr) if err != nil || offset < 0 { offset = 0 } // Get users users, total, err := h.adminService.GetAllUsers(limit, offset) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to get users", "details": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "users": users, "total": total, "limit": limit, "offset": offset, }) } // GetBookings handles GET /api/admin/bookings for retrieving all bookings func (h *AdminHandler) GetBookings(c *gin.Context) { // Parse pagination parameters limitStr := c.DefaultQuery("limit", "50") offsetStr := c.DefaultQuery("offset", "0") limit, err := strconv.Atoi(limitStr) if err != nil || limit <= 0 { limit = 50 } offset, err := strconv.Atoi(offsetStr) if err != nil || offset < 0 { offset = 0 } // Get bookings bookings, total, err := h.adminService.GetAllBookings(limit, offset) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to get bookings", "details": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "bookings": bookings, "total": total, "limit": limit, "offset": offset, }) } // GetFinancialReports handles GET /api/admin/reports/financial for financial reports func (h *AdminHandler) GetFinancialReports(c *gin.Context) { // Parse date parameters startDateStr := c.Query("start_date") endDateStr := c.Query("end_date") if startDateStr == "" || endDateStr == "" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Both start_date and end_date parameters are required (format: YYYY-MM-DD)", }) return } // Parse dates startDate, err := time.Parse("2006-01-02", startDateStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid start_date format. Expected: YYYY-MM-DD", "details": err.Error(), }) return } endDate, err := time.Parse("2006-01-02", endDateStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid end_date format. Expected: YYYY-MM-DD", "details": err.Error(), }) return } // Adjust end date to include the entire day endDate = endDate.Add(23*time.Hour + 59*time.Minute + 59*time.Second) // Generate financial report report, err := h.adminService.GetFinancialReports(startDate, endDate) if err != nil { if err.Error() == "end date cannot be before start date" { c.JSON(http.StatusBadRequest, gin.H{ "error": "End date cannot be before start date", }) return } c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to generate financial report", "details": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "report": report, }) } // UpdateSchedule handles PUT /api/admin/schedules/:id for schedule updates func (h *AdminHandler) UpdateSchedule(c *gin.Context) { // Get schedule ID from URL parameter scheduleIDStr := c.Param("id") scheduleID, err := strconv.ParseUint(scheduleIDStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid schedule ID", }) return } var req services.UpdateScheduleRequest // Bind JSON request to struct if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid request format", "details": err.Error(), }) return } // Update the schedule schedule, err := h.adminService.UpdateSchedule(uint(scheduleID), req) if err != nil { // Handle specific error cases if err.Error() == "schedule not found" { c.JSON(http.StatusNotFound, gin.H{ "error": "Schedule not found", }) return } if err.Error() == "end time must be after start time" || err.Error() == "max bookings must be at least 1" { c.JSON(http.StatusBadRequest, gin.H{ "error": err.Error(), }) return } c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to update schedule", "details": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "message": "Schedule updated successfully", "schedule": schedule, }) }