package controllers import ( "net/http" "strings" "fotbal-club/internal/models" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // ContactInfoController handles contact information and categories type ContactInfoController struct { DB *gorm.DB } // NewContactInfoController creates a new ContactInfoController func NewContactInfoController(db *gorm.DB) *ContactInfoController { return &ContactInfoController{DB: db} } // ==================== PUBLIC ENDPOINTS ==================== // GetPublicContacts returns all active contacts grouped by category (public) func (ctrl *ContactInfoController) GetPublicContacts(c *gin.Context) { var contacts []models.Contact if err := ctrl.DB.Preload("Category"). Where("is_active = ?", true). Order("display_order ASC, created_at ASC"). Find(&contacts).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load contacts"}) return } // Group by category grouped := make(map[string][]models.Contact) uncategorized := []models.Contact{} for _, contact := range contacts { if contact.Category != nil && contact.Category.IsActive { grouped[contact.Category.Name] = append(grouped[contact.Category.Name], contact) } else { uncategorized = append(uncategorized, contact) } } c.JSON(http.StatusOK, gin.H{ "categories": grouped, "uncategorized": uncategorized, }) } // GetPublicContactCategories returns all active contact categories (public) func (ctrl *ContactInfoController) GetPublicContactCategories(c *gin.Context) { var categories []models.ContactCategory if err := ctrl.DB.Where("is_active = ?", true). Order("display_order ASC, name ASC"). Find(&categories).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load categories"}) return } c.JSON(http.StatusOK, categories) } // ==================== ADMIN: CONTACT CATEGORIES ==================== // GetContactCategories lists all contact categories (admin) func (ctrl *ContactInfoController) GetContactCategories(c *gin.Context) { var categories []models.ContactCategory if err := ctrl.DB.Order("display_order ASC, name ASC").Find(&categories).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load categories"}) return } c.JSON(http.StatusOK, categories) } // CreateContactCategory creates a new contact category (admin) func (ctrl *ContactInfoController) CreateContactCategory(c *gin.Context) { var body struct { Name string `json:"name" binding:"required"` Description string `json:"description"` DisplayOrder *int `json:"display_order"` IsActive *bool `json:"is_active"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } category := models.ContactCategory{ Name: strings.TrimSpace(body.Name), Description: strings.TrimSpace(body.Description), } if body.DisplayOrder != nil { category.DisplayOrder = *body.DisplayOrder } if body.IsActive != nil { category.IsActive = *body.IsActive } else { category.IsActive = true } if err := ctrl.DB.Create(&category).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create category"}) return } c.JSON(http.StatusCreated, category) } // UpdateContactCategory updates a contact category (admin) func (ctrl *ContactInfoController) UpdateContactCategory(c *gin.Context) { id := c.Param("id") var category models.ContactCategory if err := ctrl.DB.First(&category, id).Error; err != nil { if err == gorm.ErrRecordNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "Category not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) return } var body struct { Name *string `json:"name"` Description *string `json:"description"` DisplayOrder *int `json:"display_order"` IsActive *bool `json:"is_active"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if body.Name != nil { category.Name = strings.TrimSpace(*body.Name) } if body.Description != nil { category.Description = strings.TrimSpace(*body.Description) } if body.DisplayOrder != nil { category.DisplayOrder = *body.DisplayOrder } if body.IsActive != nil { category.IsActive = *body.IsActive } if err := ctrl.DB.Save(&category).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update category"}) return } c.JSON(http.StatusOK, category) } // DeleteContactCategory deletes a contact category (admin) func (ctrl *ContactInfoController) DeleteContactCategory(c *gin.Context) { id := c.Param("id") // Check if any contacts use this category var count int64 if err := ctrl.DB.Model(&models.Contact{}).Where("category_id = ?", id).Count(&count).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) return } if count > 0 { c.JSON(http.StatusConflict, gin.H{"error": "Cannot delete category with contacts. Remove contacts first."}) return } if err := ctrl.DB.Delete(&models.ContactCategory{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete category"}) return } c.JSON(http.StatusOK, gin.H{"message": "Category deleted"}) } // ==================== ADMIN: CONTACTS ==================== // GetContacts lists all contacts (admin) func (ctrl *ContactInfoController) GetContacts(c *gin.Context) { var contacts []models.Contact if err := ctrl.DB.Preload("Category"). Order("display_order ASC, created_at ASC"). Find(&contacts).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load contacts"}) return } c.JSON(http.StatusOK, contacts) } // CreateContact creates a new contact (admin) func (ctrl *ContactInfoController) CreateContact(c *gin.Context) { var body struct { CategoryID *uint `json:"category_id"` Name string `json:"name" binding:"required"` Position string `json:"position"` Email string `json:"email"` Phone string `json:"phone"` ImageURL string `json:"image_url"` Description string `json:"description"` DisplayOrder *int `json:"display_order"` IsActive *bool `json:"is_active"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } contact := models.Contact{ CategoryID: body.CategoryID, Name: strings.TrimSpace(body.Name), Position: strings.TrimSpace(body.Position), Email: strings.TrimSpace(body.Email), Phone: strings.TrimSpace(body.Phone), ImageURL: strings.TrimSpace(body.ImageURL), Description: strings.TrimSpace(body.Description), } if body.DisplayOrder != nil { contact.DisplayOrder = *body.DisplayOrder } if body.IsActive != nil { contact.IsActive = *body.IsActive } else { contact.IsActive = true } // Validate category if provided if contact.CategoryID != nil { var cat models.ContactCategory if err := ctrl.DB.First(&cat, *contact.CategoryID).Error; err != nil { if err == gorm.ErrRecordNotFound { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid category_id"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) return } } if err := ctrl.DB.Create(&contact).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create contact"}) return } // Reload with category ctrl.DB.Preload("Category").First(&contact, contact.ID) c.JSON(http.StatusCreated, contact) } // UpdateContact updates a contact (admin) func (ctrl *ContactInfoController) UpdateContact(c *gin.Context) { id := c.Param("id") var contact models.Contact if err := ctrl.DB.First(&contact, id).Error; err != nil { if err == gorm.ErrRecordNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "Contact not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) return } var body struct { CategoryID *uint `json:"category_id"` Name *string `json:"name"` Position *string `json:"position"` Email *string `json:"email"` Phone *string `json:"phone"` ImageURL *string `json:"image_url"` Description *string `json:"description"` DisplayOrder *int `json:"display_order"` IsActive *bool `json:"is_active"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Allow setting category to null by sending explicit null if c.Request.Header.Get("Content-Type") == "application/json" { var raw map[string]interface{} if err := c.ShouldBindJSON(&raw); err == nil { if _, exists := raw["category_id"]; exists { contact.CategoryID = body.CategoryID } } } else if body.CategoryID != nil { // Validate new category var cat models.ContactCategory if err := ctrl.DB.First(&cat, *body.CategoryID).Error; err != nil { if err == gorm.ErrRecordNotFound { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid category_id"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) return } contact.CategoryID = body.CategoryID } if body.Name != nil { contact.Name = strings.TrimSpace(*body.Name) } if body.Position != nil { contact.Position = strings.TrimSpace(*body.Position) } if body.Email != nil { contact.Email = strings.TrimSpace(*body.Email) } if body.Phone != nil { contact.Phone = strings.TrimSpace(*body.Phone) } if body.ImageURL != nil { contact.ImageURL = strings.TrimSpace(*body.ImageURL) } if body.Description != nil { contact.Description = strings.TrimSpace(*body.Description) } if body.DisplayOrder != nil { contact.DisplayOrder = *body.DisplayOrder } if body.IsActive != nil { contact.IsActive = *body.IsActive } if err := ctrl.DB.Save(&contact).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update contact"}) return } // Reload with category ctrl.DB.Preload("Category").First(&contact, contact.ID) c.JSON(http.StatusOK, contact) } // DeleteContact deletes a contact (admin) func (ctrl *ContactInfoController) DeleteContact(c *gin.Context) { id := c.Param("id") if err := ctrl.DB.Delete(&models.Contact{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete contact"}) return } c.JSON(http.StatusOK, gin.H{"message": "Contact deleted"}) }