package contactlinks import ( "errors" "net/http" "github.com/gin-gonic/gin" "github.com/google/uuid" ) type Handler struct { service *Service } func NewHandler(service *Service) *Handler { return &Handler{service: service} } type contactLinkResponse struct { ID uuid.UUID `json:"id"` Label string `json:"label"` URL string `json:"url"` IconKey string `json:"icon_key"` IconMediaID *uuid.UUID `json:"icon_media_id"` Color string `json:"color"` Position int `json:"position"` IsActive bool `json:"is_active"` } func toResponse(link *ContactLink) contactLinkResponse { return contactLinkResponse{ ID: link.ID, Label: link.Label, URL: link.URL, IconKey: link.IconKey, IconMediaID: link.IconMediaID, Color: link.Color, Position: link.Position, IsActive: link.IsActive, } } func toResponseList(list []*ContactLink) []contactLinkResponse { resp := make([]contactLinkResponse, 0, len(list)) for _, link := range list { resp = append(resp, toResponse(link)) } return resp } // ListAdmin returns every contact link (active or not). func (h *Handler) ListAdmin(c *gin.Context) { list, err := h.service.List(c.Request.Context(), false) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list contact links"}) return } c.JSON(http.StatusOK, gin.H{"contact_links": toResponseList(list)}) } // ListPublic returns only active contact links, for the storefront to display. func (h *Handler) ListPublic(c *gin.Context) { list, err := h.service.List(c.Request.Context(), true) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list contact links"}) return } c.JSON(http.StatusOK, gin.H{"contact_links": toResponseList(list)}) } type upsertRequest struct { Label string `json:"label" binding:"required"` URL string `json:"url" binding:"required"` IconKey string `json:"icon_key"` IconMediaID *uuid.UUID `json:"icon_media_id"` Color string `json:"color"` Position int `json:"position"` IsActive bool `json:"is_active"` } func (h *Handler) Create(c *gin.Context) { var req upsertRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "validation error", "details": err.Error()}) return } link, err := h.service.Create(c.Request.Context(), req.Label, req.URL, req.IconKey, req.IconMediaID, req.Color, req.Position, req.IsActive) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create contact link"}) return } c.JSON(http.StatusCreated, toResponse(link)) } func (h *Handler) Update(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) return } var req upsertRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "validation error", "details": err.Error()}) return } link, err := h.service.Update(c.Request.Context(), id, req.Label, req.URL, req.IconKey, req.IconMediaID, req.Color, req.Position, req.IsActive) if err != nil { if errors.Is(err, ErrNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "contact link not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update contact link"}) return } c.JSON(http.StatusOK, toResponse(link)) } type updatePositionRequest struct { Position int `json:"position"` } // UpdatePosition lets the admin reorder the contact link display order // (e.g. move up/move down in the admin list) without resending the whole // contact link payload. func (h *Handler) UpdatePosition(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) return } var req updatePositionRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "validation error", "details": err.Error()}) return } link, err := h.service.UpdatePosition(c.Request.Context(), id, req.Position) if err != nil { if errors.Is(err, ErrNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "contact link not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update position"}) return } c.JSON(http.StatusOK, toResponse(link)) } func (h *Handler) Delete(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) return } if err := h.service.Delete(c.Request.Context(), id); err != nil { if errors.Is(err, ErrNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "contact link not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete contact link"}) return } c.Status(http.StatusNoContent) }