mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
hot fix #1
This commit is contained in:
@@ -0,0 +1,578 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"fotbal-club/internal/models"
|
||||
"fotbal-club/pkg/database"
|
||||
)
|
||||
|
||||
// FacilityController handles facility management operations
|
||||
type FacilityController struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewFacilityController creates a new facility controller
|
||||
func NewFacilityController() *FacilityController {
|
||||
return &FacilityController{
|
||||
db: database.GetDB(),
|
||||
}
|
||||
}
|
||||
|
||||
// FacilityListRequest represents query parameters for facility listing
|
||||
type FacilityListRequest struct {
|
||||
Type string `form:"type"`
|
||||
Status string `form:"status"`
|
||||
Page int `form:"page,default=1"`
|
||||
Limit int `form:"limit,default=20"`
|
||||
Search string `form:"search"`
|
||||
}
|
||||
|
||||
// FacilityResponse represents a facility response
|
||||
type FacilityResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Capacity int `json:"capacity"`
|
||||
Area float64 `json:"area"`
|
||||
Location string `json:"location"`
|
||||
IsIndoor bool `json:"is_indoor"`
|
||||
IsOutdoor bool `json:"is_outdoor"`
|
||||
ImageURL string `json:"image_url"`
|
||||
|
||||
// Booking settings
|
||||
RequiresApproval bool `json:"requires_approval"`
|
||||
MinBookingDuration int `json:"min_booking_duration"`
|
||||
MaxBookingDuration int `json:"max_booking_duration"`
|
||||
BookingAdvanceDays int `json:"booking_advance_days"`
|
||||
PricePerHour float64 `json:"price_per_hour"`
|
||||
|
||||
// Availability
|
||||
AvailabilityRules []models.FacilityAvailabilityRule `json:"availability_rules,omitempty"`
|
||||
|
||||
// Counts
|
||||
BookingsCount int `json:"bookings_count,omitempty"`
|
||||
EquipmentCount int `json:"equipment_count,omitempty"`
|
||||
MaintenanceCount int `json:"maintenance_count,omitempty"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// BookingRequest represents a booking request
|
||||
type BookingRequest struct {
|
||||
FacilityID uint `json:"facility_id" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
StartTime string `json:"start_time" binding:"required"` // ISO 8601 format
|
||||
EndTime string `json:"end_time" binding:"required"` // ISO 8601 format
|
||||
AttendeesCount int `json:"attendees_count"`
|
||||
}
|
||||
|
||||
// BookingResponse represents a booking response
|
||||
type BookingResponse struct {
|
||||
ID uint `json:"id"`
|
||||
FacilityID uint `json:"facility_id"`
|
||||
Facility FacilityResponse `json:"facility"`
|
||||
UserID uint `json:"user_id"`
|
||||
User models.User `json:"user"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
Status string `json:"status"`
|
||||
TotalPrice float64 `json:"total_price"`
|
||||
PaymentStatus string `json:"payment_status"`
|
||||
AttendeesCount int `json:"attendees_count"`
|
||||
PublicNotes string `json:"public_notes"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// GetFacilities handles GET /api/v1/admin/facilities
|
||||
func (fc *FacilityController) GetFacilities(c *gin.Context) {
|
||||
var req FacilityListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var facilities []models.Facility
|
||||
query := fc.db.Model(&models.Facility{})
|
||||
|
||||
// Apply filters
|
||||
if req.Type != "" {
|
||||
query = query.Where("type = ?", req.Type)
|
||||
}
|
||||
if req.Status != "" {
|
||||
query = query.Where("status = ?", req.Status)
|
||||
}
|
||||
if req.Search != "" {
|
||||
query = query.Where("name ILIKE ? OR description ILIKE ?", "%"+req.Search+"%", "%"+req.Search+"%")
|
||||
}
|
||||
|
||||
// Count total
|
||||
var total int64
|
||||
query.Count(&total)
|
||||
|
||||
// Apply pagination
|
||||
offset := (req.Page - 1) * req.Limit
|
||||
query = query.Offset(offset).Limit(req.Limit)
|
||||
|
||||
if err := query.Preload("AvailabilityRules").Find(&facilities).Error; err != nil {
|
||||
c.JSON(500, gin.H{"error": "Failed to fetch facilities"})
|
||||
return
|
||||
}
|
||||
|
||||
// Transform to response format
|
||||
var responses []FacilityResponse
|
||||
for _, facility := range facilities {
|
||||
// Count related records
|
||||
var bookingsCount, equipmentCount, maintenanceCount int64
|
||||
fc.db.Model(&models.FacilityBooking{}).Where("facility_id = ?", facility.ID).Count(&bookingsCount)
|
||||
fc.db.Model(&models.FacilityEquipment{}).Where("facility_id = ?", facility.ID).Count(&equipmentCount)
|
||||
fc.db.Model(&models.FacilityMaintenance{}).Where("facility_id = ?", facility.ID).Count(&maintenanceCount)
|
||||
|
||||
responses = append(responses, FacilityResponse{
|
||||
ID: facility.ID,
|
||||
Name: facility.Name,
|
||||
Type: string(facility.Type),
|
||||
Status: string(facility.Status),
|
||||
Capacity: facility.Capacity,
|
||||
Area: facility.Area,
|
||||
Location: facility.Location,
|
||||
IsIndoor: facility.IsIndoor,
|
||||
IsOutdoor: facility.IsOutdoor,
|
||||
ImageURL: facility.ImageURL,
|
||||
RequiresApproval: facility.RequiresApproval,
|
||||
MinBookingDuration: facility.MinBookingDuration,
|
||||
MaxBookingDuration: facility.MaxBookingDuration,
|
||||
BookingAdvanceDays: facility.BookingAdvanceDays,
|
||||
PricePerHour: facility.PricePerHour,
|
||||
AvailabilityRules: facility.AvailabilityRules,
|
||||
BookingsCount: int(bookingsCount),
|
||||
EquipmentCount: int(equipmentCount),
|
||||
MaintenanceCount: int(maintenanceCount),
|
||||
CreatedAt: facility.CreatedAt,
|
||||
UpdatedAt: facility.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"facilities": responses,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"limit": req.Limit,
|
||||
})
|
||||
}
|
||||
|
||||
// GetFacility handles GET /api/v1/admin/facilities/:id
|
||||
func (fc *FacilityController) GetFacility(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
var facility models.Facility
|
||||
if err := fc.db.Preload("AvailabilityRules").
|
||||
Preload("Bookings").
|
||||
Preload("Equipment").
|
||||
Preload("Maintenance").
|
||||
First(&facility, id).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(404, gin.H{"error": "Facility not found"})
|
||||
} else {
|
||||
c.JSON(500, gin.H{"error": "Failed to fetch facility"})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"facility": facility})
|
||||
}
|
||||
|
||||
// CreateFacility handles POST /api/v1/admin/facilities
|
||||
func (fc *FacilityController) CreateFacility(c *gin.Context) {
|
||||
var facility models.Facility
|
||||
if err := c.ShouldBindJSON(&facility); err != nil {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Set default values
|
||||
if facility.Status == "" {
|
||||
facility.Status = models.FacilityStatusActive
|
||||
}
|
||||
if facility.MinBookingDuration == 0 {
|
||||
facility.MinBookingDuration = 30
|
||||
}
|
||||
if facility.MaxBookingDuration == 0 {
|
||||
facility.MaxBookingDuration = 240
|
||||
}
|
||||
if facility.BookingAdvanceDays == 0 {
|
||||
facility.BookingAdvanceDays = 30
|
||||
}
|
||||
|
||||
if err := fc.db.Create(&facility).Error; err != nil {
|
||||
c.JSON(500, gin.H{"error": "Failed to create facility"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(201, gin.H{"facility": facility})
|
||||
}
|
||||
|
||||
// UpdateFacility handles PUT /api/v1/admin/facilities/:id
|
||||
func (fc *FacilityController) UpdateFacility(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
var facility models.Facility
|
||||
if err := fc.db.First(&facility, id).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(404, gin.H{"error": "Facility not found"})
|
||||
} else {
|
||||
c.JSON(500, gin.H{"error": "Failed to fetch facility"})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var updates models.Facility
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Update fields
|
||||
if err := fc.db.Model(&facility).Updates(updates).Error; err != nil {
|
||||
c.JSON(500, gin.H{"error": "Failed to update facility"})
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch updated facility
|
||||
if err := fc.db.Preload("AvailabilityRules").First(&facility, id).Error; err != nil {
|
||||
c.JSON(500, gin.H{"error": "Failed to fetch updated facility"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"facility": facility})
|
||||
}
|
||||
|
||||
// DeleteFacility handles DELETE /api/v1/admin/facilities/:id
|
||||
func (fc *FacilityController) DeleteFacility(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := fc.db.Delete(&models.Facility{}, id).Error; err != nil {
|
||||
c.JSON(500, gin.H{"error": "Failed to delete facility"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"message": "Facility deleted successfully"})
|
||||
}
|
||||
|
||||
// GetFacilityBookings handles GET /api/v1/admin/facilities/:id/bookings
|
||||
func (fc *FacilityController) GetFacilityBookings(c *gin.Context) {
|
||||
facilityID := c.Param("id")
|
||||
|
||||
// Parse query parameters
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
|
||||
status := c.Query("status")
|
||||
startDate := c.Query("start_date")
|
||||
endDate := c.Query("end_date")
|
||||
|
||||
var bookings []models.FacilityBooking
|
||||
query := fc.db.Model(&models.FacilityBooking{}).Where("facility_id = ?", facilityID)
|
||||
|
||||
// Apply filters
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
if startDate != "" {
|
||||
query = query.Where("start_time >= ?", startDate)
|
||||
}
|
||||
if endDate != "" {
|
||||
query = query.Where("end_time <= ?", endDate)
|
||||
}
|
||||
|
||||
// Count total
|
||||
var total int64
|
||||
query.Count(&total)
|
||||
|
||||
// Apply pagination
|
||||
offset := (page - 1) * limit
|
||||
query = query.Offset(offset).Limit(limit)
|
||||
|
||||
if err := query.Preload("User").Preload("Facility").Order("start_time DESC").Find(&bookings).Error; err != nil {
|
||||
c.JSON(500, gin.H{"error": "Failed to fetch bookings"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"bookings": bookings,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateBooking handles POST /api/v1/facilities/bookings
|
||||
func (fc *FacilityController) CreateBooking(c *gin.Context) {
|
||||
var req BookingRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Get user from context (assuming JWT middleware)
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(401, gin.H{"error": "User not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse times
|
||||
startTime, err := time.Parse(time.RFC3339, req.StartTime)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": "Invalid start_time format"})
|
||||
return
|
||||
}
|
||||
|
||||
endTime, err := time.Parse(time.RFC3339, req.EndTime)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": "Invalid end_time format"})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate time range
|
||||
if endTime.Before(startTime) || endTime.Equal(startTime) {
|
||||
c.JSON(400, gin.H{"error": "End time must be after start time"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get facility
|
||||
var facility models.Facility
|
||||
if err := fc.db.First(&facility, req.FacilityID).Error; err != nil {
|
||||
c.JSON(404, gin.H{"error": "Facility not found"})
|
||||
return
|
||||
}
|
||||
|
||||
// Check if facility is available
|
||||
if facility.Status != models.FacilityStatusActive {
|
||||
c.JSON(400, gin.H{"error": "Facility is not available for booking"})
|
||||
return
|
||||
}
|
||||
|
||||
// Check booking duration limits
|
||||
duration := endTime.Sub(startTime).Minutes()
|
||||
if int(duration) < facility.MinBookingDuration {
|
||||
c.JSON(400, gin.H{"error": fmt.Sprintf("Booking duration must be at least %d minutes", facility.MinBookingDuration)})
|
||||
return
|
||||
}
|
||||
if int(duration) > facility.MaxBookingDuration {
|
||||
c.JSON(400, gin.H{"error": fmt.Sprintf("Booking duration cannot exceed %d minutes", facility.MaxBookingDuration)})
|
||||
return
|
||||
}
|
||||
|
||||
// Check advance booking limit
|
||||
if facility.BookingAdvanceDays > 0 {
|
||||
maxDate := time.Now().AddDate(0, 0, facility.BookingAdvanceDays)
|
||||
if startTime.After(maxDate) {
|
||||
c.JSON(400, gin.H{"error": fmt.Sprintf("Bookings cannot be made more than %d days in advance", facility.BookingAdvanceDays)})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check for overlapping bookings
|
||||
var overlappingBooking models.FacilityBooking
|
||||
if err := fc.db.Where("facility_id = ? AND start_time < ? AND end_time > ? AND status NOT IN (?, ?)",
|
||||
req.FacilityID, endTime, startTime, string(models.BookingStatusCancelled), string(models.BookingStatusNoShow)).
|
||||
First(&overlappingBooking).Error; err == nil {
|
||||
c.JSON(409, gin.H{"error": "Time slot is already booked"})
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate price
|
||||
totalPrice := facility.PricePerHour * (duration / 60)
|
||||
|
||||
// Create booking
|
||||
booking := models.FacilityBooking{
|
||||
FacilityID: req.FacilityID,
|
||||
UserID: userID.(uint),
|
||||
Title: req.Title,
|
||||
Description: req.Description,
|
||||
StartTime: startTime,
|
||||
EndTime: endTime,
|
||||
Status: models.BookingStatusPending,
|
||||
TotalPrice: totalPrice,
|
||||
AttendeesCount: req.AttendeesCount,
|
||||
}
|
||||
|
||||
if facility.RequiresApproval {
|
||||
booking.Status = models.BookingStatusPending
|
||||
} else {
|
||||
booking.Status = models.BookingStatusConfirmed
|
||||
}
|
||||
|
||||
if err := fc.db.Create(&booking).Error; err != nil {
|
||||
c.JSON(500, gin.H{"error": "Failed to create booking"})
|
||||
return
|
||||
}
|
||||
|
||||
// Load relationships for response
|
||||
fc.db.Preload("User").Preload("Facility").First(&booking, booking.ID)
|
||||
|
||||
c.JSON(201, gin.H{"booking": booking})
|
||||
}
|
||||
|
||||
// GetPublicFacilities handles GET /api/v1/facilities
|
||||
func (fc *FacilityController) GetPublicFacilities(c *gin.Context) {
|
||||
var facilities []models.Facility
|
||||
|
||||
query := fc.db.Model(&models.Facility{}).Where("status = ?", models.FacilityStatusActive)
|
||||
|
||||
// Optional filters
|
||||
facilityType := c.Query("type")
|
||||
if facilityType != "" {
|
||||
query = query.Where("type = ?", facilityType)
|
||||
}
|
||||
|
||||
if err := query.Find(&facilities).Error; err != nil {
|
||||
c.JSON(500, gin.H{"error": "Failed to fetch facilities"})
|
||||
return
|
||||
}
|
||||
|
||||
// Transform to public response format (limited fields)
|
||||
var responses []FacilityResponse
|
||||
for _, facility := range facilities {
|
||||
responses = append(responses, FacilityResponse{
|
||||
ID: facility.ID,
|
||||
Name: facility.Name,
|
||||
Type: string(facility.Type),
|
||||
Status: string(facility.Status),
|
||||
Capacity: facility.Capacity,
|
||||
Area: facility.Area,
|
||||
Location: facility.Location,
|
||||
IsIndoor: facility.IsIndoor,
|
||||
IsOutdoor: facility.IsOutdoor,
|
||||
ImageURL: facility.ImageURL,
|
||||
RequiresApproval: facility.RequiresApproval,
|
||||
MinBookingDuration: facility.MinBookingDuration,
|
||||
MaxBookingDuration: facility.MaxBookingDuration,
|
||||
BookingAdvanceDays: facility.BookingAdvanceDays,
|
||||
PricePerHour: facility.PricePerHour,
|
||||
CreatedAt: facility.CreatedAt,
|
||||
UpdatedAt: facility.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"facilities": responses})
|
||||
}
|
||||
|
||||
// GetFacilityAvailability handles GET /api/v1/facilities/:id/availability
|
||||
func (fc *FacilityController) GetFacilityAvailability(c *gin.Context) {
|
||||
facilityID := c.Param("id")
|
||||
|
||||
// Parse date range
|
||||
startDate := c.Query("start_date")
|
||||
endDate := c.Query("end_date")
|
||||
|
||||
if startDate == "" || endDate == "" {
|
||||
c.JSON(400, gin.H{"error": "start_date and end_date are required"})
|
||||
return
|
||||
}
|
||||
|
||||
start, err := time.Parse("2006-01-02", startDate)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": "Invalid start_date format, use YYYY-MM-DD"})
|
||||
return
|
||||
}
|
||||
|
||||
end, err := time.Parse("2006-01-02", endDate)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": "Invalid end_date format, use YYYY-MM-DD"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get facility
|
||||
var facility models.Facility
|
||||
if err := fc.db.Preload("AvailabilityRules").First(&facility, facilityID).Error; err != nil {
|
||||
c.JSON(404, gin.H{"error": "Facility not found"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get existing bookings
|
||||
var bookings []models.FacilityBooking
|
||||
if err := fc.db.Where("facility_id = ? AND start_time >= ? AND end_time <= ? AND status NOT IN (?, ?)",
|
||||
facilityID, start, end.AddDate(0, 0, 1), string(models.BookingStatusCancelled), string(models.BookingStatusNoShow)).
|
||||
Find(&bookings).Error; err != nil {
|
||||
c.JSON(500, gin.H{"error": "Failed to fetch bookings"})
|
||||
return
|
||||
}
|
||||
|
||||
// Generate availability slots
|
||||
availability := fc.generateAvailabilitySlots(facility, bookings, start, end)
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"facility": facility,
|
||||
"availability": availability,
|
||||
})
|
||||
}
|
||||
|
||||
// generateAvailabilitySlots creates available time slots for a facility
|
||||
func (fc *FacilityController) generateAvailabilitySlots(facility models.Facility, bookings []models.FacilityBooking, start, end time.Time) map[string][]map[string]interface{} {
|
||||
availability := make(map[string][]map[string]interface{})
|
||||
|
||||
// Initialize each day with empty slots
|
||||
for d := start; d.Before(end.AddDate(0, 0, 1)); d = d.AddDate(0, 0, 1) {
|
||||
dateStr := d.Format("2006-01-02")
|
||||
availability[dateStr] = []map[string]interface{}{}
|
||||
}
|
||||
|
||||
// For each day, generate available slots based on rules and existing bookings
|
||||
for d := start; d.Before(end.AddDate(0, 0, 1)); d = d.AddDate(0, 0, 1) {
|
||||
dateStr := d.Format("2006-01-02")
|
||||
dayOfWeek := int(d.Weekday())
|
||||
|
||||
// Find availability rules for this day
|
||||
var dayRules []models.FacilityAvailabilityRule
|
||||
for _, rule := range facility.AvailabilityRules {
|
||||
if rule.DayOfWeek == dayOfWeek && rule.IsAvailable {
|
||||
// Check if rule is within date range
|
||||
if (rule.StartDate == nil || d.After(*rule.StartDate) || d.Equal(*rule.StartDate)) &&
|
||||
(rule.EndDate == nil || d.Before(*rule.EndDate) || d.Equal(*rule.EndDate)) {
|
||||
dayRules = append(dayRules, rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate slots for each rule
|
||||
for _, rule := range dayRules {
|
||||
ruleStart, _ := time.Parse("15:04", rule.StartTime)
|
||||
ruleEnd, _ := time.Parse("15:04", rule.EndTime)
|
||||
|
||||
// Convert to full datetime
|
||||
slotStart := time.Date(d.Year(), d.Month(), d.Day(), ruleStart.Hour(), ruleStart.Minute(), 0, 0, d.Location())
|
||||
slotEnd := time.Date(d.Year(), d.Month(), d.Day(), ruleEnd.Hour(), ruleEnd.Minute(), 0, 0, d.Location())
|
||||
|
||||
// Check for overlapping bookings
|
||||
isAvailable := true
|
||||
for _, booking := range bookings {
|
||||
if booking.StartTime.Before(slotEnd) && booking.EndTime.After(slotStart) {
|
||||
isAvailable = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isAvailable {
|
||||
availability[dateStr] = append(availability[dateStr], map[string]interface{}{
|
||||
"start": slotStart.Format("15:04"),
|
||||
"end": slotEnd.Format("15:04"),
|
||||
"available": true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availability
|
||||
}
|
||||
Reference in New Issue
Block a user