Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 66 additions & 16 deletions backend/app/http/endpoints/api/forms/updateinputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"sort"
"strconv"
"strings"
"unicode/utf8"

"github.com/TicketsBot-cloud/common/featureflags"
"github.com/TicketsBot-cloud/database"
Expand All @@ -30,10 +31,11 @@ type (
}

inputCreateBody struct {
Label string `json:"label" validate:"required,min=1,max=45"`
Label string `json:"label" validate:"omitempty,max=45"`
Description *string `json:"description,omitempty" validate:"omitempty,max=100"`
Placeholder *string `json:"placeholder,omitempty" validate:"omitempty,min=1,max=100"`
Type int `json:"type" validate:"required,oneof=3 4 5 6 7 8 21 22"`
Content *string `json:"content,omitempty" validate:"omitempty,max=4000"`
Type int `json:"type" validate:"required,oneof=3 4 5 6 7 8 10 21 22"`
Position int `json:"position" validate:"required,min=1,max=5"`
Style component.TextStyleTypes `json:"style" validate:"omitempty,required,min=1,max=2"`
Required bool `json:"required"`
Expand Down Expand Up @@ -93,6 +95,14 @@ func UpdateInputs(c *gin.Context) {
return
}

for i := range data.Create {
data.Create[i] = normalizeTypeFields(data.Create[i])
}

for i := range data.Update {
data.Update[i].inputCreateBody = normalizeTypeFields(data.Update[i].inputCreateBody)
}

if err := validate.Struct(data); err != nil {
var validationErrors validator.ValidationErrors
if !errors.As(err, &validationErrors) {
Expand Down Expand Up @@ -206,6 +216,11 @@ func UpdateInputs(c *gin.Context) {
}
}

if contentLength := totalTextDisplayLength(data); contentLength > 4000 {
c.JSON(400, utils.ErrorStr("Text display content must be at most 4000 characters in total (current: %d characters)", contentLength))
return
}

if err := saveInputs(c, formId, data, existingInputs); err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to save form inputs to database"))
return
Expand Down Expand Up @@ -256,6 +271,23 @@ func arePositionsCorrect(body updateInputsBody) bool {
return true
}

func totalTextDisplayLength(body updateInputsBody) int {
var total int
for _, input := range body.Create {
if input.Type == 10 && input.Content != nil {
total += utf8.RuneCountInString(*input.Content)
}
}

for _, input := range body.Update {
if input.Type == 10 && input.Content != nil {
total += utf8.RuneCountInString(*input.Content)
}
}

return total
}

func validateUniqueOptionValues(options []inputOption) error {
if len(options) == 0 {
return nil
Expand Down Expand Up @@ -307,6 +339,17 @@ func validateApiBodyTemplate(config *inputApiConfigBody) error {
}

func validateInput(input inputCreateBody, optionTypes map[int]string) error {
if input.Type == 10 {
if input.Content == nil || strings.TrimSpace(*input.Content) == "" {
return fmt.Errorf("Text display content is required")
}
return nil
}

if strings.TrimSpace(input.Label) == "" {
return fmt.Errorf("Field labels are required")
}

if input.Type == 4 && input.MaxLength < 1 {
return fmt.Errorf("Text input max length must be at least 1")
}
Expand Down Expand Up @@ -358,7 +401,7 @@ func validateInput(input inputCreateBody, optionTypes map[int]string) error {
}

func normalizeLengths(input inputCreateBody) (*uint16, *uint16) {
if input.Type == 21 {
if input.Type == 10 || input.Type == 21 {
return nil, nil
}

Expand Down Expand Up @@ -393,6 +436,19 @@ func normalizeLengths(input inputCreateBody) (*uint16, *uint16) {
return &minLength, &maxLength
}

func normalizeTypeFields(input inputCreateBody) inputCreateBody {
if input.Type == 10 {
input.Label = ""
input.Required = false
input.Placeholder = nil
input.Description = nil
} else {
input.Content = nil
}

return input
}

func saveInputs(ctx context.Context, formId int, data updateInputsBody, existingInputs []database.FormInput) error {
// We can now update in the database
tx, err := dbclient.Client.BeginTx(ctx)
Expand Down Expand Up @@ -426,6 +482,7 @@ func saveInputs(ctx context.Context, formId int, data updateInputsBody, existing
Label: input.Label,
Description: input.Description,
Placeholder: input.Placeholder,
Content: input.Content,
Required: input.Required,
MinLength: minLengthPtr,
MaxLength: maxLengthPtr,
Expand All @@ -435,19 +492,11 @@ func saveInputs(ctx context.Context, formId int, data updateInputsBody, existing
return err
}

if wrapped.Type == 3 || wrapped.Type == 21 || wrapped.Type == 22 { // String Select, RadioGroup, CheckboxGroup
// Delete existing options
options, err := dbclient.Client.FormInputOption.GetOptions(ctx, wrapped.Id)
if err != nil {
return err
}

for _, option := range options {
if err := dbclient.Client.FormInputOption.DeleteTx(ctx, tx, option.Id); err != nil {
return err
}
}
if err := dbclient.Client.FormInputOption.DeleteByFormInputTx(ctx, tx, wrapped.Id); err != nil {
return err
}

if wrapped.Type == 3 || wrapped.Type == 21 || wrapped.Type == 22 { // String Select, RadioGroup, CheckboxGroup
// Add new options
for i, opt := range input.Options {
option := database.FormInputOption{
Expand Down Expand Up @@ -487,6 +536,7 @@ func saveInputs(ctx context.Context, formId int, data updateInputsBody, existing
input.Label,
input.Description,
input.Placeholder,
input.Content,
input.Required,
minLengthPtr,
maxLengthPtr,
Expand Down Expand Up @@ -522,7 +572,7 @@ func saveInputs(ctx context.Context, formId int, data updateInputsBody, existing

func saveApiConfig(ctx context.Context, tx pgx.Tx, formInputId int, input inputCreateBody) error {
if input.Type != 3 {
return nil
return dbclient.Client.FormInputApiConfig.DeleteByFormInputTx(ctx, tx, formInputId)
}

existingConfig, hasExisting, err := dbclient.Client.FormInputApiConfig.GetTx(ctx, tx, formInputId)
Expand Down
1 change: 1 addition & 0 deletions backend/app/http/endpoints/api/gallery/import_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ func createFormInputs(ctx *gin.Context, tx pgx.Tx, formId int, inputs []database
input.Label,
input.Description,
input.Placeholder,
input.Content,
input.Required,
input.MinLength,
input.MaxLength,
Expand Down
2 changes: 1 addition & 1 deletion backend/app/http/endpoints/api/gallery/resubmit.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func ResubmitHandler(ctx *gin.Context) {
snapshotInputs[i] = database.GalleryFormInputSnapshot{
Type: input.Type, Position: input.Position, Style: input.Style,
Label: input.Label, Description: input.Description, Placeholder: input.Placeholder,
Required: input.Required, MinLength: input.MinLength, MaxLength: input.MaxLength,
Content: input.Content, Required: input.Required, MinLength: input.MinLength, MaxLength: input.MaxLength,
}
if opts, exists := optionsByInput[input.Id]; exists {
snapshotOpts := make([]database.GalleryFormInputOptionSnapshot, len(opts))
Expand Down
1 change: 1 addition & 0 deletions backend/app/http/endpoints/api/gallery/submit_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func SubmitFormHandler(ctx *gin.Context) {
Label: input.Label,
Description: input.Description,
Placeholder: input.Placeholder,
Content: input.Content,
Required: input.Required,
MinLength: input.MinLength,
MaxLength: input.MaxLength,
Expand Down
2 changes: 1 addition & 1 deletion backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/BurntSushi/toml v1.2.1
github.com/TicketsBot-cloud/archiverclient v0.0.0-20251015181023-f0b66a074704
github.com/TicketsBot-cloud/common v0.0.0-20260927072029-424264fc4be2
github.com/TicketsBot-cloud/database v0.0.0-20260927071905-7a22c7be0e4a
github.com/TicketsBot-cloud/database v0.0.0-20261002063446-01dca52a987e
github.com/TicketsBot-cloud/gdl v0.0.0-20260612070331-a3947b410d3e
github.com/TicketsBot-cloud/logarchiver v0.0.0-20251018211319-7a7df5cacbdc
github.com/TicketsBot-cloud/worker v0.0.0-20260827073646-455b39e53841
Expand Down
4 changes: 2 additions & 2 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ github.com/TicketsBot-cloud/archiverclient v0.0.0-20251015181023-f0b66a074704 h1
github.com/TicketsBot-cloud/archiverclient v0.0.0-20251015181023-f0b66a074704/go.mod h1:Mux1bEPpOHwRw1wo6Fa6qJLJH9Erk9qv1yAIfLi1Wmw=
github.com/TicketsBot-cloud/common v0.0.0-20260927072029-424264fc4be2 h1:qeebeGGdrsDXrngOVrKMDt9G3JUTJwT/IEPTB61d46s=
github.com/TicketsBot-cloud/common v0.0.0-20260927072029-424264fc4be2/go.mod h1:yL+VPSYNVK5gxUkA+fbb0WdTjGfWXnr7ScfL0rwk29g=
github.com/TicketsBot-cloud/database v0.0.0-20260927071905-7a22c7be0e4a h1:Y1MPuFHwMmkoGeqnUV8CuXx4yqChdvPpqy3vGWBYnZo=
github.com/TicketsBot-cloud/database v0.0.0-20260927071905-7a22c7be0e4a/go.mod h1:0fKn7nPX6KeljYNQtGQiuZ5aJ/TczgXvBhnbz+4GEc8=
github.com/TicketsBot-cloud/database v0.0.0-20261002063446-01dca52a987e h1:kZS42Mp+6wBhApK7zA6F1SX8ZYYG18cVRHB2ZDTvx94=
github.com/TicketsBot-cloud/database v0.0.0-20261002063446-01dca52a987e/go.mod h1:0fKn7nPX6KeljYNQtGQiuZ5aJ/TczgXvBhnbz+4GEc8=
github.com/TicketsBot-cloud/gdl v0.0.0-20260612070331-a3947b410d3e h1:qCibZQmO2rrBFrvn6+oAchPEQ7k/0Izr3ruL5P9aVZU=
github.com/TicketsBot-cloud/gdl v0.0.0-20260612070331-a3947b410d3e/go.mod h1:CdwBR2egPtxUXjD2CgC9ZwfuB8dz9HPePM8nuG6dt7Y=
github.com/TicketsBot-cloud/logarchiver v0.0.0-20251018211319-7a7df5cacbdc h1:qTLNpCvIqM7UwZ6MdWQ9EztcDsIJfHh+VJdG+ULLEaA=
Expand Down
Loading
Loading