Add length slider
This commit is contained in:
parent
a7b05af1f0
commit
dc564d37f0
@ -1,118 +0,0 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||
"github.com/TicketsBot/GoPanel/utils"
|
||||
"github.com/TicketsBot/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rxdn/gdl/objects/interaction/component"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func CreateInput(ctx *gin.Context) {
|
||||
guildId := ctx.Keys["guildid"].(uint64)
|
||||
|
||||
var data inputCreateBody
|
||||
if err := ctx.BindJSON(&data); err != nil {
|
||||
ctx.JSON(400, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
// Validate body
|
||||
if !data.Validate(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
// Parse form ID from URL
|
||||
formId, err := strconv.Atoi(ctx.Param("form_id"))
|
||||
if err != nil {
|
||||
ctx.JSON(400, utils.ErrorStr("Invalid form ID"))
|
||||
return
|
||||
}
|
||||
|
||||
// Get form and validate it belongs to the guild
|
||||
form, ok, err := dbclient.Client.Forms.Get(formId)
|
||||
if err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
ctx.JSON(404, utils.ErrorStr("Form not found"))
|
||||
return
|
||||
}
|
||||
|
||||
if form.GuildId != guildId {
|
||||
ctx.JSON(403, utils.ErrorStr("Form does not belong to this guild"))
|
||||
return
|
||||
}
|
||||
|
||||
// Check there are not more than 25 inputs already
|
||||
// TODO: This is vulnerable to a race condition
|
||||
inputCount, err := getFormInputCount(formId)
|
||||
if err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
if inputCount >= 5 {
|
||||
ctx.JSON(400, utils.ErrorStr("A form cannot have more than 5 inputs"))
|
||||
return
|
||||
}
|
||||
|
||||
// 2^30 chance of collision
|
||||
customId, err := utils.RandString(30)
|
||||
if err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
formInputId, err := dbclient.Client.FormInput.Create(formId, customId, uint8(data.Style), data.Label, data.Placeholder, data.Required)
|
||||
if err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(200, database.FormInput{
|
||||
Id: formInputId,
|
||||
FormId: formId,
|
||||
CustomId: customId,
|
||||
Style: uint8(data.Style),
|
||||
Label: data.Label,
|
||||
Placeholder: data.Placeholder,
|
||||
Required: data.Required,
|
||||
})
|
||||
}
|
||||
|
||||
func (b *inputCreateBody) Validate(ctx *gin.Context) bool {
|
||||
if b.Style != component.TextStyleShort && b.Style != component.TextStyleParagraph {
|
||||
ctx.JSON(400, utils.ErrorStr("Invalid style"))
|
||||
return false
|
||||
}
|
||||
|
||||
if len(b.Label) == 0 || len(b.Label) > 45 {
|
||||
ctx.JSON(400, utils.ErrorStr("The input label must be between 1 and 45 characters"))
|
||||
return false
|
||||
}
|
||||
|
||||
if b.Placeholder != nil && len(*b.Placeholder) == 0 {
|
||||
b.Placeholder = nil
|
||||
}
|
||||
|
||||
if b.Placeholder != nil && len(*b.Placeholder) > 100 {
|
||||
ctx.JSON(400, utils.ErrorStr("The placeholder cannot be more than 100 characters"))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO: Use select count()
|
||||
func getFormInputCount(formId int) (int, error) {
|
||||
inputs, err := dbclient.Client.FormInput.GetInputs(formId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(inputs), nil
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||
"github.com/TicketsBot/GoPanel/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func DeleteInput(ctx *gin.Context) {
|
||||
guildId := ctx.Keys["guildid"].(uint64)
|
||||
|
||||
formId, err := strconv.Atoi(ctx.Param("form_id"))
|
||||
if err != nil {
|
||||
ctx.JSON(400, utils.ErrorStr("Invalid form ID"))
|
||||
return
|
||||
}
|
||||
|
||||
inputId, err := strconv.Atoi(ctx.Param("input_id"))
|
||||
if err != nil {
|
||||
ctx.JSON(400, utils.ErrorStr("Invalid form ID"))
|
||||
return
|
||||
}
|
||||
|
||||
form, ok, err := dbclient.Client.Forms.Get(formId)
|
||||
if err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
ctx.JSON(404, utils.ErrorStr("Form not found"))
|
||||
return
|
||||
}
|
||||
|
||||
if form.GuildId != guildId {
|
||||
ctx.JSON(403, utils.ErrorStr("Form does not belong to this guild"))
|
||||
return
|
||||
}
|
||||
|
||||
input, ok, err := dbclient.Client.FormInput.Get(inputId)
|
||||
if err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
ctx.JSON(404, utils.ErrorStr("Input not found"))
|
||||
return
|
||||
}
|
||||
|
||||
if input.FormId != formId {
|
||||
ctx.JSON(403, utils.ErrorStr("Input does not belong to this form"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := dbclient.Client.FormInput.Delete(input.Id, input.FormId); err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(200, utils.SuccessResponse)
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||
"github.com/TicketsBot/GoPanel/utils"
|
||||
"github.com/TicketsBot/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func SwapInput(ctx *gin.Context) {
|
||||
guildId := ctx.Keys["guildid"].(uint64)
|
||||
|
||||
formId, err := strconv.Atoi(ctx.Param("form_id"))
|
||||
if err != nil {
|
||||
ctx.JSON(400, utils.ErrorStr("Invalid form ID"))
|
||||
return
|
||||
}
|
||||
|
||||
inputId, err := strconv.Atoi(ctx.Param("input_id"))
|
||||
if err != nil {
|
||||
ctx.JSON(400, utils.ErrorStr("Invalid form ID"))
|
||||
return
|
||||
}
|
||||
|
||||
form, ok, err := dbclient.Client.Forms.Get(formId)
|
||||
if err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
ctx.JSON(404, utils.ErrorStr("Form not found"))
|
||||
return
|
||||
}
|
||||
|
||||
if form.GuildId != guildId {
|
||||
ctx.JSON(403, utils.ErrorStr("Form does not belong to this guild"))
|
||||
return
|
||||
}
|
||||
|
||||
input, ok, err := dbclient.Client.FormInput.Get(inputId)
|
||||
if err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
ctx.JSON(404, utils.ErrorStr("Input not found"))
|
||||
return
|
||||
}
|
||||
|
||||
if input.FormId != formId {
|
||||
ctx.JSON(403, utils.ErrorStr("Input does not belong to this form"))
|
||||
return
|
||||
}
|
||||
|
||||
var direction database.InputSwapDirection
|
||||
{
|
||||
directionRaw := ctx.Param("direction")
|
||||
if directionRaw == "up" {
|
||||
direction = database.SwapDirectionUp
|
||||
} else if directionRaw == "down" {
|
||||
direction = database.SwapDirectionDown
|
||||
} else {
|
||||
ctx.JSON(400, utils.ErrorStr("Invalid swap direction"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := dbclient.Client.FormInput.SwapDirection(inputId, formId, direction); err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(200, utils.SuccessResponse)
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||
"github.com/TicketsBot/GoPanel/utils"
|
||||
"github.com/TicketsBot/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func UpdateInput(ctx *gin.Context) {
|
||||
guildId := ctx.Keys["guildid"].(uint64)
|
||||
|
||||
var data inputCreateBody
|
||||
if err := ctx.BindJSON(&data); err != nil {
|
||||
ctx.JSON(400, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
if !data.Validate(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
formId, err := strconv.Atoi(ctx.Param("form_id"))
|
||||
if err != nil {
|
||||
ctx.JSON(400, utils.ErrorStr("Invalid form ID"))
|
||||
return
|
||||
}
|
||||
|
||||
inputId, err := strconv.Atoi(ctx.Param("input_id"))
|
||||
if err != nil {
|
||||
ctx.JSON(400, utils.ErrorStr("Invalid form ID"))
|
||||
return
|
||||
}
|
||||
|
||||
form, ok, err := dbclient.Client.Forms.Get(formId)
|
||||
if err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
ctx.JSON(404, utils.ErrorStr("Form not found"))
|
||||
return
|
||||
}
|
||||
|
||||
if form.GuildId != guildId {
|
||||
ctx.JSON(403, utils.ErrorStr("Form does not belong to this guild"))
|
||||
return
|
||||
}
|
||||
|
||||
input, ok, err := dbclient.Client.FormInput.Get(inputId)
|
||||
if err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
ctx.JSON(404, utils.ErrorStr("Input not found"))
|
||||
return
|
||||
}
|
||||
|
||||
if input.FormId != formId {
|
||||
ctx.JSON(403, utils.ErrorStr("Input does not belong to this form"))
|
||||
return
|
||||
}
|
||||
|
||||
newInput := database.FormInput{
|
||||
Id: inputId,
|
||||
FormId: formId,
|
||||
CustomId: input.CustomId,
|
||||
Style: uint8(data.Style),
|
||||
Label: data.Label,
|
||||
Placeholder: data.Placeholder,
|
||||
Required: data.Required,
|
||||
}
|
||||
|
||||
if err := dbclient.Client.FormInput.Update(newInput); err != nil {
|
||||
ctx.JSON(500, utils.ErrorJson(err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(200, newInput)
|
||||
}
|
@ -26,6 +26,8 @@ type (
|
||||
Position int `json:"position" validate:"required,min=1,max=5"`
|
||||
Style component.TextStyleTypes `json:"style" validate:"required,min=1,max=2"`
|
||||
Required bool `json:"required"`
|
||||
MinLength uint16 `json:"min_length" validate:"min=0,max=1024"` // validator interprets 0 as not set
|
||||
MaxLength uint16 `json:"max_length" validate:"min=0,max=1024"`
|
||||
}
|
||||
|
||||
inputUpdateBody struct {
|
||||
@ -212,6 +214,8 @@ func saveInputs(formId int, data updateInputsBody, existingInputs []database.For
|
||||
Label: input.Label,
|
||||
Placeholder: input.Placeholder,
|
||||
Required: input.Required,
|
||||
MinLength: &input.MinLength,
|
||||
MaxLength: &input.MaxLength,
|
||||
}
|
||||
|
||||
if err := dbclient.Client.FormInput.UpdateTx(tx, wrapped); err != nil {
|
||||
@ -234,6 +238,8 @@ func saveInputs(formId int, data updateInputsBody, existingInputs []database.For
|
||||
input.Label,
|
||||
input.Placeholder,
|
||||
input.Required,
|
||||
&input.MinLength,
|
||||
&input.MaxLength,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -132,11 +132,7 @@ func StartServer(sm *livechat.SocketManager) {
|
||||
guildAuthApiAdmin.POST("/forms", rl(middleware.RateLimitTypeGuild, 30, time.Hour), api_forms.CreateForm)
|
||||
guildAuthApiAdmin.PATCH("/forms/:form_id", rl(middleware.RateLimitTypeGuild, 30, time.Hour), api_forms.UpdateForm)
|
||||
guildAuthApiAdmin.DELETE("/forms/:form_id", api_forms.DeleteForm)
|
||||
guildAuthApiAdmin.POST("/forms/:form_id", api_forms.CreateInput)
|
||||
guildAuthApiAdmin.PATCH("/forms/:form_id/inputs", api_forms.UpdateInputs)
|
||||
guildAuthApiAdmin.PATCH("/forms/:form_id/:input_id", api_forms.UpdateInput)
|
||||
guildAuthApiAdmin.PATCH("/forms/:form_id/:input_id/:direction", api_forms.SwapInput)
|
||||
guildAuthApiAdmin.DELETE("/forms/:form_id/:input_id", api_forms.DeleteInput)
|
||||
|
||||
// Should be a GET, but easier to take a body for development purposes
|
||||
guildAuthApiSupport.POST("/transcripts",
|
||||
|
@ -26,17 +26,18 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row settings-row">
|
||||
<Textarea col3_4={true} label="Placeholder" bind:value={data.placeholder} minHeight="120px"
|
||||
<Textarea col2={true} label="Placeholder" bind:value={data.placeholder} minHeight="120px"
|
||||
placeholder="Placeholder text for the field, just like this text" />
|
||||
<div class="col-4">
|
||||
<div class="col-2 properties-group">
|
||||
<div class="row">
|
||||
<Dropdown col1={true} label="Style" bind:value={data.style}>
|
||||
<Dropdown col2={true} label="Style" bind:value={data.style}>
|
||||
<option value=1 selected>Short</option>
|
||||
<option value=2>Paragraph</option>
|
||||
<option value=2>Multi-line</option>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row" style="gap: 10px">
|
||||
<Checkbox label="Required" bind:value={data.required}/>
|
||||
<DoubleRangeSlider label="Answer Length Range" bind:start={data.min_length} bind:end={data.max_length} min={0} max={1024} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -91,6 +92,7 @@
|
||||
import Button from "../Button.svelte";
|
||||
import Textarea from "../form/Textarea.svelte";
|
||||
import Checkbox from "../form/Checkbox.svelte";
|
||||
import DoubleRangeSlider from "../form/DoubleRangeSlider.svelte";
|
||||
|
||||
export let withCreateButton = false;
|
||||
export let withDeleteButton = false;
|
||||
@ -159,4 +161,10 @@
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 576px) {
|
||||
.properties-group > div:nth-child(2) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -62,7 +62,7 @@
|
||||
<hr class="fill">
|
||||
<div class="row add-input-container" class:add-input-disabled={formLength >= 5}>
|
||||
<i class="fas fa-plus"></i>
|
||||
<a on:click={addInput}>Add Input</a>
|
||||
<a on:click={addInput}>New Field</a>
|
||||
</div>
|
||||
<hr class="fill">
|
||||
</div>
|
||||
@ -187,6 +187,8 @@
|
||||
label: "",
|
||||
placeholder: "",
|
||||
required: true,
|
||||
min_length: 0,
|
||||
max_length: 1024,
|
||||
is_new: true,
|
||||
};
|
||||
|
||||
@ -194,22 +196,6 @@
|
||||
forms = forms;
|
||||
}
|
||||
|
||||
async function editInput(formId, inputId, data) {
|
||||
let mapped = {...data, style: parseInt(data.style)};
|
||||
|
||||
const res = await axios.patch(`${API_URL}/api/${guildId}/forms/${formId}/${inputId}`, mapped);
|
||||
if (res.status !== 200) {
|
||||
notifyError(res.data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let form = getForm(formId);
|
||||
form.inputs = form.inputs.filter(input => input.id !== inputId);
|
||||
form.inputs = [...form.inputs, res.data];
|
||||
|
||||
notifySuccess('Form input updated successfully');
|
||||
}
|
||||
|
||||
async function deleteInput(formId, input) {
|
||||
let form = getForm(formId);
|
||||
|
||||
|
2
go.mod
2
go.mod
@ -8,7 +8,7 @@ require (
|
||||
github.com/BurntSushi/toml v1.2.1
|
||||
github.com/TicketsBot/archiverclient v0.0.0-20240613013458-accc062facc2
|
||||
github.com/TicketsBot/common v0.0.0-20240613013221-1e27eb8bfe37
|
||||
github.com/TicketsBot/database v0.0.0-20240614143550-e9b219d41743
|
||||
github.com/TicketsBot/database v0.0.0-20240620154005-fdf7932c6a00
|
||||
github.com/TicketsBot/logarchiver v0.0.0-20220326162808-cdf0310f5e1c
|
||||
github.com/TicketsBot/worker v0.0.0-20240615173640-85185c239fd0
|
||||
github.com/apex/log v1.1.2
|
||||
|
2
go.sum
2
go.sum
@ -49,6 +49,8 @@ github.com/TicketsBot/common v0.0.0-20240613013221-1e27eb8bfe37 h1:NC5fn+uAup0Jx
|
||||
github.com/TicketsBot/common v0.0.0-20240613013221-1e27eb8bfe37/go.mod h1:UZ6Kzobh9akWyon7iGLPb4w/9gmKV+sLuR6PmthsS+U=
|
||||
github.com/TicketsBot/database v0.0.0-20240614143550-e9b219d41743 h1:wTGntdybAv9GcWYE3RJiAIo4I6rYrfG9uSpHaF0KUXY=
|
||||
github.com/TicketsBot/database v0.0.0-20240614143550-e9b219d41743/go.mod h1:gAtOoQKZfCkQ4AoNWQUSl51Fnlqk+odzD/hZ1e1sXyI=
|
||||
github.com/TicketsBot/database v0.0.0-20240620154005-fdf7932c6a00 h1:2qU/ixn0SEaDxiTQ7S/a4bTGQOlV920Sab/MlqocGzs=
|
||||
github.com/TicketsBot/database v0.0.0-20240620154005-fdf7932c6a00/go.mod h1:gAtOoQKZfCkQ4AoNWQUSl51Fnlqk+odzD/hZ1e1sXyI=
|
||||
github.com/TicketsBot/logarchiver v0.0.0-20220326162808-cdf0310f5e1c h1:OqGjFH6mbE6gd+NqI2ARJdtH3UUvhiAkD0r0fhGJK2s=
|
||||
github.com/TicketsBot/logarchiver v0.0.0-20220326162808-cdf0310f5e1c/go.mod h1:jgi2OXQKsd5nUnTIRkwvPmeuD/i7OhN68LKMssuQY1c=
|
||||
github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261 h1:NHD5GB6cjlkpZFjC76Yli2S63/J2nhr8MuE6KlYJpQM=
|
||||
|
Loading…
x
Reference in New Issue
Block a user