From 116e5d6e05eb37c56b200398f9c94f9af981fa6f Mon Sep 17 00:00:00 2001 From: rxdn <29165304+rxdn@users.noreply.github.com> Date: Wed, 9 Feb 2022 17:30:07 +0000 Subject: [PATCH] Forms --- app/http/endpoints/api/forms/createform.go | 45 +++ app/http/endpoints/api/forms/createinput.go | 119 ++++++++ app/http/endpoints/api/forms/deleteform.go | 41 +++ app/http/endpoints/api/forms/deleteinput.go | 63 ++++ app/http/endpoints/api/forms/getforms.go | 28 ++ app/http/endpoints/api/forms/updateform.go | 52 ++++ app/http/endpoints/api/forms/updateinput.go | 83 ++++++ app/http/endpoints/api/panel/panelcreate.go | 41 +++ app/http/endpoints/api/panel/panelupdate.go | 17 +- app/http/server.go | 9 + frontend/src/components/form/Dropdown.svelte | 4 +- frontend/src/components/form/Textarea.svelte | 4 +- .../src/components/manage/FormInputRow.svelte | 126 ++++++++ .../manage/PanelCreationForm.svelte | 9 + .../components/manage/PanelEditModal.svelte | 3 +- .../src/components/manage/SettingsCard.svelte | 2 +- frontend/src/includes/Navbar.svelte | 1 + frontend/src/routes.js | 2 + frontend/src/views/Forms.svelte | 279 ++++++++++++++++++ frontend/src/views/Panels.svelte | 16 +- go.mod | 4 +- go.sum | 19 +- 22 files changed, 939 insertions(+), 28 deletions(-) create mode 100644 app/http/endpoints/api/forms/createform.go create mode 100644 app/http/endpoints/api/forms/createinput.go create mode 100644 app/http/endpoints/api/forms/deleteform.go create mode 100644 app/http/endpoints/api/forms/deleteinput.go create mode 100644 app/http/endpoints/api/forms/getforms.go create mode 100644 app/http/endpoints/api/forms/updateform.go create mode 100644 app/http/endpoints/api/forms/updateinput.go create mode 100644 frontend/src/components/manage/FormInputRow.svelte create mode 100644 frontend/src/views/Forms.svelte diff --git a/app/http/endpoints/api/forms/createform.go b/app/http/endpoints/api/forms/createform.go new file mode 100644 index 0000000..20fd271 --- /dev/null +++ b/app/http/endpoints/api/forms/createform.go @@ -0,0 +1,45 @@ +package forms + +import ( + dbclient "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/utils" + "github.com/TicketsBot/database" + "github.com/gin-gonic/gin" +) + +type createFormBody struct { + Title string `json:"title"` +} + +func CreateForm(ctx *gin.Context) { + guildId := ctx.Keys["guildid"].(uint64) + + var data createFormBody + if err := ctx.BindJSON(&data); err != nil { + ctx.JSON(400, utils.ErrorJson(err)) + return + } + + if len(data.Title) > 255 { + ctx.JSON(400, utils.ErrorStr("Title is too long")) + return + } + + // 26^50 chance of collision + customId := utils.RandString(50) + + id, err := dbclient.Client.Forms.Create(guildId, data.Title, customId) + if err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + form := database.Form{ + Id: id, + GuildId: guildId, + Title: data.Title, + CustomId: customId, + } + + ctx.JSON(200, form) +} diff --git a/app/http/endpoints/api/forms/createinput.go b/app/http/endpoints/api/forms/createinput.go new file mode 100644 index 0000000..1a65ce0 --- /dev/null +++ b/app/http/endpoints/api/forms/createinput.go @@ -0,0 +1,119 @@ +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" +) + +type inputCreateBody struct { + Style component.TextStyleTypes `json:"style"` + Label string `json:"label"` + Placeholder *string `json:"placeholder"` +} + +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 := utils.RandString(30) + + formInputId, err := dbclient.Client.FormInput.Create(formId, customId, uint8(data.Style), data.Label, data.Placeholder) + 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, + }) +} + +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) > 255 { + ctx.JSON(400, utils.ErrorStr("The input label must be between 1 and 255 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 +} diff --git a/app/http/endpoints/api/forms/deleteform.go b/app/http/endpoints/api/forms/deleteform.go new file mode 100644 index 0000000..30fd2a0 --- /dev/null +++ b/app/http/endpoints/api/forms/deleteform.go @@ -0,0 +1,41 @@ +package forms + +import ( + dbclient "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/gin" + "strconv" +) + +func DeleteForm(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 + } + + 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 + } + + if err := dbclient.Client.Forms.Delete(formId); err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + ctx.JSON(200, utils.SuccessResponse) +} diff --git a/app/http/endpoints/api/forms/deleteinput.go b/app/http/endpoints/api/forms/deleteinput.go new file mode 100644 index 0000000..c11109d --- /dev/null +++ b/app/http/endpoints/api/forms/deleteinput.go @@ -0,0 +1,63 @@ +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) +} diff --git a/app/http/endpoints/api/forms/getforms.go b/app/http/endpoints/api/forms/getforms.go new file mode 100644 index 0000000..79191f3 --- /dev/null +++ b/app/http/endpoints/api/forms/getforms.go @@ -0,0 +1,28 @@ +package forms + +import ( + dbclient "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/gin" +) + +func GetForms(ctx *gin.Context) { + guildId := ctx.Keys["guildid"].(uint64) + + forms, err := dbclient.Client.Forms.GetForms(guildId) + if err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + inputs, err := dbclient.Client.FormInput.GetInputsForGuild(guildId) + if err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + ctx.JSON(200, gin.H{ + "forms": forms, + "inputs": inputs, + }) +} diff --git a/app/http/endpoints/api/forms/updateform.go b/app/http/endpoints/api/forms/updateform.go new file mode 100644 index 0000000..d215063 --- /dev/null +++ b/app/http/endpoints/api/forms/updateform.go @@ -0,0 +1,52 @@ +package forms + +import ( + dbclient "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/gin" + "strconv" +) + +func UpdateForm(ctx *gin.Context) { + guildId := ctx.Keys["guildid"].(uint64) + + var data createFormBody + if err := ctx.BindJSON(&data); err != nil { + ctx.JSON(400, utils.ErrorJson(err)) + return + } + + if len(data.Title) > 255 { + ctx.JSON(400, utils.ErrorStr("Title is too long")) + return + } + + formId, err := strconv.Atoi(ctx.Param("form_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 + } + + if err := dbclient.Client.Forms.UpdateTitle(formId, data.Title); err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + ctx.JSON(200, utils.SuccessResponse) +} diff --git a/app/http/endpoints/api/forms/updateinput.go b/app/http/endpoints/api/forms/updateinput.go new file mode 100644 index 0000000..9a3927a --- /dev/null +++ b/app/http/endpoints/api/forms/updateinput.go @@ -0,0 +1,83 @@ +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, + } + + if err := dbclient.Client.FormInput.Update(newInput); err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + ctx.JSON(200, newInput) +} diff --git a/app/http/endpoints/api/panel/panelcreate.go b/app/http/endpoints/api/panel/panelcreate.go index 8328387..ca0527b 100644 --- a/app/http/endpoints/api/panel/panelcreate.go +++ b/app/http/endpoints/api/panel/panelcreate.go @@ -38,6 +38,7 @@ type panelBody struct { ImageUrl *string `json:"image_url,omitempty"` ThumbnailUrl *string `json:"thumbnail_url,omitempty"` ButtonStyle component.ButtonStyle `json:"button_style,string"` + FormId int `json:"form_id"` } func (p *panelBody) IntoPanelMessageData(customId string, isPremium bool) panelMessageData { @@ -138,6 +139,11 @@ func CreatePanel(ctx *gin.Context) { return } + var formId *int + if data.FormId != 0 { // Already validated + formId = &data.FormId + } + // Store in DB panel := database.Panel{ MessageId: msgId, @@ -154,6 +160,7 @@ func CreatePanel(ctx *gin.Context) { ImageUrl: data.ImageUrl, ThumbnailUrl: data.ThumbnailUrl, ButtonStyle: int(data.ButtonStyle), + FormId: formId, } panelId, err := dbclient.Client.Panel.Create(panel) @@ -305,6 +312,19 @@ func (p *panelBody) doValidations(ctx *gin.Context, guildId uint64) bool { return false } + { + ok, err := p.verifyFormId(guildId) + if err != nil { + ctx.AbortWithStatusJSON(500, utils.ErrorJson(err)) + return false + } + + if !ok { + ctx.AbortWithStatusJSON(400, utils.ErrorStr("Guild ID for form does not match")) + return false + } + } + return true } @@ -383,3 +403,24 @@ func (p *panelBody) verifyThumbnailUrl() bool { func (p *panelBody) verifyButtonStyle() bool { return p.ButtonStyle >= component.ButtonStylePrimary && p.ButtonStyle <= component.ButtonStyleDanger } + +func (p *panelBody) verifyFormId(guildId uint64) (bool, error) { + if p.FormId == 0 { // TODO: Use nil + return true, nil + } else { + form, ok, err := dbclient.Client.Forms.Get(p.FormId) + if err != nil { + return false, err + } + + if !ok { + return false, nil + } + + if form.GuildId != guildId { + return false, nil + } + + return true, nil + } +} diff --git a/app/http/endpoints/api/panel/panelupdate.go b/app/http/endpoints/api/panel/panelupdate.go index 5288dfa..55e1d3c 100644 --- a/app/http/endpoints/api/panel/panelupdate.go +++ b/app/http/endpoints/api/panel/panelupdate.go @@ -84,12 +84,12 @@ func UpdatePanel(ctx *gin.Context) { } messageData := multiPanelMessageData{ - Title: multiPanel.Title, - Content: multiPanel.Content, - Colour: multiPanel.Colour, - ChannelId: multiPanel.ChannelId, + Title: multiPanel.Title, + Content: multiPanel.Content, + Colour: multiPanel.Colour, + ChannelId: multiPanel.ChannelId, SelectMenu: multiPanel.SelectMenu, - IsPremium: premiumTier > premium.None, + IsPremium: premiumTier > premium.None, } messageId, err := messageData.send(&botContext, panels) @@ -142,6 +142,12 @@ func UpdatePanel(ctx *gin.Context) { } } + // Already validated + var formId *int + if data.FormId != 0 { + formId = &data.FormId + } + // Store in DB panel := database.Panel{ PanelId: panelId, @@ -159,6 +165,7 @@ func UpdatePanel(ctx *gin.Context) { ImageUrl: data.ImageUrl, ThumbnailUrl: data.ThumbnailUrl, ButtonStyle: int(data.ButtonStyle), + FormId: formId, } if err = dbclient.Client.Panel.Update(panel); err != nil { diff --git a/app/http/server.go b/app/http/server.go index 150db3d..4ae2e3b 100644 --- a/app/http/server.go +++ b/app/http/server.go @@ -5,6 +5,7 @@ import ( api_autoclose "github.com/TicketsBot/GoPanel/app/http/endpoints/api/autoclose" api_blacklist "github.com/TicketsBot/GoPanel/app/http/endpoints/api/blacklist" api_customisation "github.com/TicketsBot/GoPanel/app/http/endpoints/api/customisation" + api_forms "github.com/TicketsBot/GoPanel/app/http/endpoints/api/forms" api_panels "github.com/TicketsBot/GoPanel/app/http/endpoints/api/panel" api_settings "github.com/TicketsBot/GoPanel/app/http/endpoints/api/settings" api_tags "github.com/TicketsBot/GoPanel/app/http/endpoints/api/tags" @@ -102,6 +103,14 @@ func StartServer() { guildAuthApiAdmin.PATCH("/multipanels/:panelid", api_panels.MultiPanelUpdate) guildAuthApiAdmin.DELETE("/multipanels/:panelid", api_panels.MultiPanelDelete) + guildAuthApiSupport.GET("/forms", api_forms.GetForms) + 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/:input_id", api_forms.UpdateInput) + 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", rl(middleware.RateLimitTypeUser, 5, 5*time.Second), diff --git a/frontend/src/components/form/Dropdown.svelte b/frontend/src/components/form/Dropdown.svelte index 272749c..de0187e 100644 --- a/frontend/src/components/form/Dropdown.svelte +++ b/frontend/src/components/form/Dropdown.svelte @@ -1,5 +1,7 @@
- + {#if label !== undefined} + + {/if} diff --git a/frontend/src/components/form/Textarea.svelte b/frontend/src/components/form/Textarea.svelte index a59daca..3f593b4 100644 --- a/frontend/src/components/form/Textarea.svelte +++ b/frontend/src/components/form/Textarea.svelte @@ -1,4 +1,4 @@ -
+
@@ -12,6 +12,8 @@ export let col2 = false; export let col3 = false; export let col4 = false; + + export let col3_4 = false; \ No newline at end of file diff --git a/frontend/src/components/manage/PanelCreationForm.svelte b/frontend/src/components/manage/PanelCreationForm.svelte index cccd914..e09a7d4 100644 --- a/frontend/src/components/manage/PanelCreationForm.svelte +++ b/frontend/src/components/manage/PanelCreationForm.svelte @@ -21,6 +21,13 @@ + + + + {#each forms as form} + + {/each} +
@@ -94,12 +101,14 @@ default_team: true, teams: [], button_style: "1", + form_id: 0, }; } export let channels = []; export let roles = []; export let teams = []; + export let forms = []; let advancedSettings = false; let overflowShow = false; diff --git a/frontend/src/components/manage/PanelEditModal.svelte b/frontend/src/components/manage/PanelEditModal.svelte index a04f0bc..b8dd3e3 100644 --- a/frontend/src/components/manage/PanelEditModal.svelte +++ b/frontend/src/components/manage/PanelEditModal.svelte @@ -4,7 +4,7 @@ Edit Panel
- +
@@ -30,6 +30,7 @@ export let guildId; export let panel = {}; export let channels = []; + export let forms = []; export let roles = []; export let teams = []; diff --git a/frontend/src/components/manage/SettingsCard.svelte b/frontend/src/components/manage/SettingsCard.svelte index a964d91..5d35038 100644 --- a/frontend/src/components/manage/SettingsCard.svelte +++ b/frontend/src/components/manage/SettingsCard.svelte @@ -227,7 +227,7 @@ justify-content: center; } - :global(.col-4, .col-3, .col-2) { + :global(.col-4, .col-3, .col-2, .col-3-4) { width: 100% !important; } } diff --git a/frontend/src/includes/Navbar.svelte b/frontend/src/includes/Navbar.svelte index 4eadcf6..51f1d14 100644 --- a/frontend/src/includes/Navbar.svelte +++ b/frontend/src/includes/Navbar.svelte @@ -9,6 +9,7 @@ Settings Transcripts Reaction Panels + Forms Teams Tickets Blacklist diff --git a/frontend/src/routes.js b/frontend/src/routes.js index 4de81a5..89fab61 100644 --- a/frontend/src/routes.js +++ b/frontend/src/routes.js @@ -20,6 +20,7 @@ import Teams from './views/Teams.svelte' import Tickets from './views/Tickets.svelte' import TicketView from './views/TicketView.svelte' import Appearance from './views/Appearance.svelte'; +import Forms from './views/Forms.svelte'; export const routes = [ {name: '/', component: Index, layout: IndexLayout}, @@ -65,6 +66,7 @@ export const routes = [ {name: 'blacklist', component: Blacklist, layout: ManageLayout}, {name: 'tags', component: Tags, layout: ManageLayout}, {name: 'teams', component: Teams, layout: ManageLayout}, + {name: 'forms', component: Forms, layout: ManageLayout}, { name: 'tickets', nestedRoutes: [ diff --git a/frontend/src/views/Forms.svelte b/frontend/src/views/Forms.svelte new file mode 100644 index 0000000..795ded1 --- /dev/null +++ b/frontend/src/views/Forms.svelte @@ -0,0 +1,279 @@ +
+
+ + Forms +
+
+

Create New Form

+ +
+
+ +
+ +
+
+
+
+
+

Manage Forms

+ +
+
+
+ + + {#each forms as form} + + {/each} + +
+
+ + {#if activeFormId !== null} +
+ +
+ {/if} +
+ +
+ {#if activeFormId !== null && inputs[activeFormId.toString()]} + {#each inputs[activeFormId.toString()] as input} + editInput(activeFormId, input.id, e.detail)} + on:delete={() => deleteInput(activeFormId, input.id)}/> + {/each} + {/if} + + {#if activeFormId !== null} + createInput(e.detail)}/> + {/if} +
+
+
+
+
+
+ + + + + + diff --git a/frontend/src/views/Panels.svelte b/frontend/src/views/Panels.svelte index f64889d..e224c6c 100644 --- a/frontend/src/views/Panels.svelte +++ b/frontend/src/views/Panels.svelte @@ -1,5 +1,5 @@ {#if editModal} - editModal = false} on:confirm={submitEdit}/> {/if} @@ -55,7 +55,7 @@
{#if !$loadingScreen} - +
@@ -143,6 +143,7 @@ let channels = []; let roles = []; let teams = []; + let forms = []; let panels = []; let multiPanels = []; let isPremium = false; @@ -320,10 +321,21 @@ roles = res.data.roles; } + async function loadForms() { + const res = await axios.get(`${API_URL}/api/${guildId}/forms`); + if (res.status !== 200) { + notifyError(res.data.error); + return; + } + + forms = res.data.forms || []; + } + withLoadingScreen(async () => { await loadPremium(); await loadChannels(); await loadTeams(); + await loadForms(); await loadRoles(); await loadPanels(); await loadMultiPanels(); diff --git a/go.mod b/go.mod index 3cc5aab..5fa178f 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/BurntSushi/toml v0.3.1 github.com/TicketsBot/archiverclient v0.0.0-20210220155137-a562b2f1bbbb github.com/TicketsBot/common v0.0.0-20210910205523-7ce93fba6fa5 - github.com/TicketsBot/database v0.0.0-20211109153802-24100e383d78 + github.com/TicketsBot/database v0.0.0-20211202174040-bd189305f898 github.com/TicketsBot/worker v0.0.0-20211108224403-97ac8e44b789 github.com/apex/log v1.1.2 github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect @@ -23,7 +23,7 @@ require ( github.com/jackc/pgx/v4 v4.7.1 github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c github.com/pkg/errors v0.9.1 - github.com/rxdn/gdl v0.0.0-20211030160619-a8772c268ca4 + github.com/rxdn/gdl v0.0.0-20220209151849-19fd8c86af50 github.com/sirupsen/logrus v1.5.0 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 ) diff --git a/go.sum b/go.sum index d11549a..792e53e 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/TicketsBot/common v0.0.0-20210910205523-7ce93fba6fa5/go.mod h1:SVwX6g github.com/TicketsBot/database v0.0.0-20200516170158-fd8a949aec2c/go.mod h1:eky4tBL+IZ0svPgTT0N/9i6j7ygHDQH3784DW+HgfcA= github.com/TicketsBot/database v0.0.0-20210902172951-4e1f8ced84b7/go.mod h1:A4T2uQFIWC/ttCYpfgv7AkPjR09mMRgzG13lgoV/+aI= github.com/TicketsBot/database v0.0.0-20211108142700-c406ab0fc1bb/go.mod h1:72oWvH/Gq1iKeXCZhVRZn1JFbNVC5iAgERZWTrEarEo= -github.com/TicketsBot/database v0.0.0-20211109153802-24100e383d78 h1:zzjOyxCdXN1fGDL2Na6Q82EDU96Cfd1vnlafeY1utUQ= -github.com/TicketsBot/database v0.0.0-20211109153802-24100e383d78/go.mod h1:72oWvH/Gq1iKeXCZhVRZn1JFbNVC5iAgERZWTrEarEo= +github.com/TicketsBot/database v0.0.0-20211202174040-bd189305f898 h1:bbDirB6NmLjJ+9Zbw+Zy3073I7vRGxlKOb+jTRXm3vE= +github.com/TicketsBot/database v0.0.0-20211202174040-bd189305f898/go.mod h1:72oWvH/Gq1iKeXCZhVRZn1JFbNVC5iAgERZWTrEarEo= github.com/TicketsBot/logarchiver v0.0.0-20200423221245-a3f92edf8c14/go.mod h1:whts8TRxrAF4WuDuEAMllkWA/inKem0NhDEFeyuoOvE= github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261 h1:NHD5GB6cjlkpZFjC76Yli2S63/J2nhr8MuE6KlYJpQM= github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261/go.mod h1:2zPxDAN2TAPpxUPjxszjs3QFKreKrQh5al/R3cMXmYk= @@ -106,7 +106,6 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.6.1 h1:W6TRDXt4WcWp4c4nf/G+6BkGdhiIo0k417gfr+V6u4I= github.com/go-playground/validator/v10 v10.6.1/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= -github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= @@ -115,7 +114,6 @@ github.com/go-redis/redis/v8 v8.11.3 h1:GCjoYp8c+yQTJfc0n69iwSiHjvuAdruxl7elnZCx github.com/go-redis/redis/v8 v8.11.3/go.mod h1:xNJ9xDG09FsIPwh3bWdk+0oDWHbtF9rPN0F/oD9XeKc= github.com/go-redis/redis_rate/v9 v9.1.1 h1:7SIrbnhQ7zsTNEgIvprFhJf7/+l3wSpZc2iRVwUmaq8= github.com/go-redis/redis_rate/v9 v9.1.1/go.mod h1:jjU9YxOSZ3cz0yj1QJVAJiy5ueKmL9o4AySJHcKyTSE= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= @@ -237,7 +235,6 @@ github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.1.1 h1:PJAw7H/9hoWC4Kf3J8iNmL1SwA6E8vfsLqBiL+F6CtI= github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -294,7 +291,6 @@ github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1y github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -323,14 +319,12 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= @@ -353,10 +347,8 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/rxdn/gdl v0.0.0-20200522202912-4ae241eb98c1/go.mod h1:2gPBB++1s9Zh11AGM/y84KpmqTyLCnjGwEnt6xRRKL4= -github.com/rxdn/gdl v0.0.0-20210527173953-25dde613ff0a/go.mod h1:jvcb1N6AdaGx3/e8MoLedO6qSo/+UdA5GGHOA8cnAeU= -github.com/rxdn/gdl v0.0.0-20211030160619-a8772c268ca4 h1:vHSTqcCCZZwlj6trBUj3tqys5hnKbBf9J6mtuG7DvgM= -github.com/rxdn/gdl v0.0.0-20211030160619-a8772c268ca4/go.mod h1:rENs8TxMsoYSJRssegNS/+fy18NCI9EUdCJX8R83PlY= +github.com/rxdn/gdl v0.0.0-20220209151849-19fd8c86af50 h1:xkWpJfblzNEzt/wQ0OB/vqJdLBnSCYIcuVE6z396MWY= +github.com/rxdn/gdl v0.0.0-20220209151849-19fd8c86af50/go.mod h1:rENs8TxMsoYSJRssegNS/+fy18NCI9EUdCJX8R83PlY= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= @@ -445,7 +437,6 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -461,7 +452,6 @@ golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -532,7 +522,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=