backend-service/internal/handlers/admin.go

264 lines
6.2 KiB
Go
Raw Normal View History

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,
})
}