- Add new AdminHandler with methods for dashboard, schedules, users, and bookings - Implement GetDashboard method to retrieve admin dashboard statistics - Add CreateSchedule method with validation and error handling - Implement GetUsers method with pagination support - Add GetBookings method with pagination and filtering capabilities - Implement GetFinancialReports method with date range filtering - Add UpdateSchedule method to modify existing schedule slots - Enhance error handling and response formatting for admin-related operations - Integrate admin service methods for comprehensive administrative tasks
264 lines
6.2 KiB
Go
264 lines
6.2 KiB
Go
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,
|
|
})
|
|
}
|