package contact import ( "tm/pkg/hcaptcha" "tm/pkg/response" "github.com/labstack/echo/v4" ) // Handler handles HTTP requests for contact operations type Handler struct { service Service hcaptchaVerifier hcaptcha.Verifier } // NewHandler creates a new contact handler func NewHandler(service Service, hcaptchaVerifier hcaptcha.Verifier) *Handler { return &Handler{ service: service, hcaptchaVerifier: hcaptchaVerifier, } } // Create handles creating a new contact message // @Summary Create a new contact message // @Description Create a new contact message from a user // @Tags Contacts // @Accept json // @Produce json // @Param contact body CreateContactForm true "Contact information" // @Success 201 {object} response.APIResponse{data=ContactResponse} // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/contacts [post] func (h *Handler) Create(c echo.Context) error { form, err := response.Parse[CreateContactForm](c) if err != nil { return response.ValidationError(c, "Invalid request data", err.Error()) } // Verify hCaptcha token remoteIP := c.RealIP() verificationResponse, err := h.hcaptchaVerifier.Verify(form.HCaptchaToken, remoteIP) if err != nil { return response.BadRequest(c, "Failed to verify hCaptcha token", "") } if !verificationResponse.Success { return response.BadRequest(c, "hCaptcha verification failed", "") } // Create contact contact, err := h.service.Create(c.Request().Context(), form) if err != nil { return response.InternalServerError(c, "Failed to create contact") } // Return public response (limited fields) return response.Created(c, &PublicContactResponse{ ID: contact.ID, Status: contact.Status, CreatedAt: contact.CreatedAt, }, "Contact message created successfully") } // GetByID handles retrieving a contact by ID (admin only) // @Summary Get contact by ID // @Description Retrieve a contact message by its ID (admin only) // @Tags Admin-Contacts // @Accept json // @Produce json // @Param id path string true "Contact ID" // @Success 200 {object} response.APIResponse{data=ContactResponse} // @Failure 400 {object} response.APIResponse // @Failure 404 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /admin/v1/contacts/{id} [get] func (h *Handler) GetByID(c echo.Context) error { id := c.Param("id") contact, err := h.service.GetByID(c.Request().Context(), id) if err != nil { if err.Error() == "contact not found" { return response.NotFound(c, "Contact not found") } return response.InternalServerError(c, "Failed to get contact") } return response.Success(c, contact, "Contact retrieved successfully") } // Search handles searching contacts with filters (admin only) // @Summary Search contacts // @Description Search contacts with filters and pagination (admin only) // @Tags Admin-Contacts // @Accept json // @Produce json // @Param q query string false "Search query" // @Param status query string false "Contact status" Enums(pending, in_progress, resolved, closed) // @Param date_from query int false "Date from (Unix timestamp)" // @Param date_to query int false "Date to (Unix timestamp)" // @Param limit query integer false "Number of contacts per page (1-100)" minimum(1) maximum(100) default(20) // @Param offset query integer false "Number of contacts to skip for pagination" minimum(0) default(0) // @Param sort_by query string false "Field to sort by" Enums(full_name, email, created_at, updated_at, status) default(created_at) // @Param sort_order query string false "Sort order" Enums(asc, desc) default(desc) // @Success 200 {object} response.APIResponse{data=ContactListResponse} // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /admin/v1/contacts [get] func (h *Handler) Search(c echo.Context) error { form, err := response.Parse[SearchContactsForm](c) if err != nil { return response.ValidationError(c, "Invalid request data", err.Error()) } pagination := response.NewPagination(c) result, err := h.service.Search(c.Request().Context(), form, pagination) if err != nil { return response.InternalServerError(c, "Failed to search contacts") } return response.SuccessWithMeta(c, result, result.Meta, "Contacts retrieved successfully") } // UpdateStatus handles updating contact status (admin only) // @Summary Update contact status // @Description Update the status of a contact message (admin only) // @Tags Admin-Contacts // @Accept json // @Produce json // @Param id path string true "Contact ID" // @Param status body UpdateContactStatusForm true "Status update information" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 404 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /admin/v1/contacts/{id}/status [put] func (h *Handler) UpdateStatus(c echo.Context) error { id := c.Param("id") form, err := response.Parse[UpdateContactStatusForm](c) if err != nil { return response.ValidationError(c, "Invalid request data", err.Error()) } // Get admin ID from context (assuming it's set by auth middleware) adminID := c.Get("user_id") if adminID == nil { return response.Unauthorized(c, "Admin authentication required") } // Update contact status err = h.service.UpdateStatus(c.Request().Context(), id, form, adminID.(string)) if err != nil { if err.Error() == "contact not found" { return response.NotFound(c, "Contact not found") } return response.InternalServerError(c, "Failed to update contact status") } return response.Success(c, map[string]interface{}{ "message": "Contact status updated successfully", }, "Contact status updated successfully") } // GetStats handles retrieving contact statistics (admin only) // @Summary Get contact statistics // @Description Get contact statistics by status (admin only) // @Tags Admin-Contacts // @Accept json // @Produce json // @Success 200 {object} response.APIResponse{data=ContactStats} // @Failure 500 {object} response.APIResponse // @Router /admin/v1/contacts/stats [get] func (h *Handler) GetStats(c echo.Context) error { stats, err := h.service.GetStats(c.Request().Context()) if err != nil { return response.InternalServerError(c, "Failed to get contact statistics") } return response.Success(c, stats, "Contact statistics retrieved successfully") } // Delete handles deleting a contact (admin only) // @Summary Delete contact // @Description Delete a contact message by ID (admin only) // @Tags Admin-Contacts // @Accept json // @Produce json // @Param id path string true "Contact ID" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 404 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /admin/v1/contacts/{id} [delete] func (h *Handler) Delete(c echo.Context) error { id := c.Param("id") err := h.service.Delete(c.Request().Context(), id) if err != nil { if err.Error() == "contact not found" { return response.NotFound(c, "Contact not found") } return response.InternalServerError(c, "Failed to delete contact") } return response.Success(c, map[string]interface{}{ "message": "Contact deleted successfully", }, "Contact deleted successfully") }