mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-19 08:01:16 +00:00
170 lines
4.4 KiB
Go
170 lines
4.4 KiB
Go
package handler
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/warmbly/warmbly/internal/api/middleware"
|
|
"github.com/warmbly/warmbly/internal/config"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
)
|
|
|
|
type completeOnboardingRequest struct {
|
|
FirstName string `json:"first_name"`
|
|
LastName string `json:"last_name"`
|
|
ReferralSource string `json:"referral_source"`
|
|
Role string `json:"role"`
|
|
TeamSize string `json:"team_size"`
|
|
}
|
|
|
|
var validReferralSources = map[string]bool{
|
|
"reddit": true,
|
|
"x": true,
|
|
"facebook": true,
|
|
"google": true,
|
|
"other": true,
|
|
}
|
|
|
|
// Persona + team-size answers from the onboarding questionnaire. Both optional:
|
|
// when provided they must be one of these, otherwise they're stored as NULL.
|
|
var validRoles = map[string]bool{
|
|
"founder": true,
|
|
"sales": true,
|
|
"marketing": true,
|
|
"agency": true,
|
|
"recruiter": true,
|
|
"other": true,
|
|
}
|
|
|
|
var validTeamSizes = map[string]bool{
|
|
"just_me": true,
|
|
"2-10": true,
|
|
"11-50": true,
|
|
"51-200": true,
|
|
"200+": true,
|
|
}
|
|
|
|
func (h *Handler) CompleteOnboarding(c *gin.Context) {
|
|
var req completeOnboardingRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.Handle(c, errx.ErrInvalid)
|
|
return
|
|
}
|
|
|
|
if req.FirstName == "" || req.LastName == "" {
|
|
errx.Handle(c, errx.New(errx.BadRequest, "First name and last name are required."))
|
|
return
|
|
}
|
|
|
|
if len(req.FirstName) > 50 || len(req.LastName) > 50 {
|
|
errx.Handle(c, errx.New(errx.BadRequest, "Name must be 50 characters or less."))
|
|
return
|
|
}
|
|
|
|
if !validReferralSources[req.ReferralSource] {
|
|
errx.Handle(c, errx.New(errx.BadRequest, "Invalid referral source."))
|
|
return
|
|
}
|
|
|
|
if req.Role != "" && !validRoles[req.Role] {
|
|
errx.Handle(c, errx.New(errx.BadRequest, "Invalid role."))
|
|
return
|
|
}
|
|
|
|
if req.TeamSize != "" && !validTeamSizes[req.TeamSize] {
|
|
errx.Handle(c, errx.New(errx.BadRequest, "Invalid team size."))
|
|
return
|
|
}
|
|
|
|
userID := middleware.GetUserID(c)
|
|
uid, err := uuid.Parse(userID)
|
|
if err != nil {
|
|
errx.Handle(c, errx.ErrUser)
|
|
return
|
|
}
|
|
|
|
if xerr := h.UserService.CompleteOnboarding(c.Request.Context(), uid, req.FirstName, req.LastName, req.ReferralSource, req.Role, req.TeamSize); xerr != nil {
|
|
errx.Handle(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
type updateProfileRequest struct {
|
|
FirstName string `json:"first_name"`
|
|
LastName string `json:"last_name"`
|
|
}
|
|
|
|
// UpdateUserProfile persists editable profile fields (first/last name) from the
|
|
// profile settings page. Unlike onboarding, it carries no questionnaire answers
|
|
// and can be called any time the user renames themselves.
|
|
func (h *Handler) UpdateUserProfile(c *gin.Context) {
|
|
var req updateProfileRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.Handle(c, errx.ErrInvalid)
|
|
return
|
|
}
|
|
|
|
if req.FirstName == "" || req.LastName == "" {
|
|
errx.Handle(c, errx.New(errx.BadRequest, "First name and last name are required."))
|
|
return
|
|
}
|
|
if len(req.FirstName) > 50 || len(req.LastName) > 50 {
|
|
errx.Handle(c, errx.New(errx.BadRequest, "Name must be 50 characters or less."))
|
|
return
|
|
}
|
|
|
|
userID := middleware.GetUserID(c)
|
|
uid, err := uuid.Parse(userID)
|
|
if err != nil {
|
|
errx.Handle(c, errx.ErrUser)
|
|
return
|
|
}
|
|
|
|
if xerr := h.UserService.UpdateProfile(c.Request.Context(), uid, req.FirstName, req.LastName); xerr != nil {
|
|
errx.Handle(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
type updateSendPreferencesRequest struct {
|
|
UndoSendSeconds int `json:"undo_send_seconds"`
|
|
}
|
|
|
|
// UpdateSendPreferences persists the user's undo-send window: how long an
|
|
// instant send is held (and still cancellable) before it actually leaves.
|
|
// PUT /me/send-preferences
|
|
func (h *Handler) UpdateSendPreferences(c *gin.Context) {
|
|
var req updateSendPreferencesRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.Handle(c, errx.ErrInvalid)
|
|
return
|
|
}
|
|
if req.UndoSendSeconds < config.UndoSendSecondsMin || req.UndoSendSeconds > config.UndoSendSecondsMax {
|
|
errx.Handle(c, errx.New(errx.BadRequest, fmt.Sprintf(
|
|
"undo_send_seconds must be between %d and %d",
|
|
config.UndoSendSecondsMin, config.UndoSendSecondsMax,
|
|
)))
|
|
return
|
|
}
|
|
|
|
userID := middleware.GetUserID(c)
|
|
uid, err := uuid.Parse(userID)
|
|
if err != nil {
|
|
errx.Handle(c, errx.ErrUser)
|
|
return
|
|
}
|
|
|
|
if xerr := h.UserService.UpdateUndoSendSeconds(c.Request.Context(), uid, req.UndoSendSeconds); xerr != nil {
|
|
errx.Handle(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"undo_send_seconds": req.UndoSendSeconds})
|
|
}
|