diff --git a/app/http/endpoints/api/panel/panelcreate.go b/app/http/endpoints/api/panel/panelcreate.go index 583c7b7..53ca441 100644 --- a/app/http/endpoints/api/panel/panelcreate.go +++ b/app/http/endpoints/api/panel/panelcreate.go @@ -2,6 +2,7 @@ package api import ( "errors" + "fmt" "github.com/TicketsBot/GoPanel/botcontext" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/rpc" @@ -12,6 +13,7 @@ import ( "github.com/TicketsBot/common/premium" "github.com/TicketsBot/database" "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" "github.com/rxdn/gdl/objects/channel" "github.com/rxdn/gdl/objects/guild/emoji" "github.com/rxdn/gdl/objects/interaction/component" @@ -33,7 +35,7 @@ type panelBody struct { Colour uint32 `json:"colour"` CategoryId uint64 `json:"category_id,string"` Emoji types.Emoji `json:"emote"` - WelcomeMessage *string `json:"welcome_message"` + WelcomeMessage *types.CustomEmbed `json:"welcome_message" validate:"omitempty,dive"` Mentions []string `json:"mentions"` WithDefaultTeam bool `json:"default_team"` Teams []int `json:"teams"` @@ -66,10 +68,7 @@ func CreatePanel(ctx *gin.Context) { botContext, err := botcontext.ContextForGuild(guildId) if err != nil { - ctx.AbortWithStatusJSON(500, gin.H{ - "success": false, - "error": err.Error(), - }) + ctx.JSON(500, utils.ErrorJson(err)) return } @@ -137,26 +136,41 @@ func CreatePanel(ctx *gin.Context) { } } + // Store welcome message embed first + var welcomeMessageEmbed *int + if data.WelcomeMessage != nil { + embed, fields := data.WelcomeMessage.IntoDatabaseStruct() + embed.GuildId = guildId + + id, err := dbclient.Client.Embeds.CreateWithFields(embed, fields) + if err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + welcomeMessageEmbed = &id + } + // Store in DB panel := database.Panel{ - MessageId: msgId, - ChannelId: data.ChannelId, - GuildId: guildId, - Title: data.Title, - Content: data.Content, - Colour: int32(data.Colour), - TargetCategory: data.CategoryId, - EmojiId: emojiId, - EmojiName: emojiName, - WelcomeMessage: data.WelcomeMessage, - WithDefaultTeam: data.WithDefaultTeam, - CustomId: customId, - ImageUrl: data.ImageUrl, - ThumbnailUrl: data.ThumbnailUrl, - ButtonStyle: int(data.ButtonStyle), - ButtonLabel: data.ButtonLabel, - FormId: data.FormId, - NamingScheme: data.NamingScheme, + MessageId: msgId, + ChannelId: data.ChannelId, + GuildId: guildId, + Title: data.Title, + Content: data.Content, + Colour: int32(data.Colour), + TargetCategory: data.CategoryId, + EmojiId: emojiId, + EmojiName: emojiName, + WelcomeMessageEmbed: welcomeMessageEmbed, + WithDefaultTeam: data.WithDefaultTeam, + CustomId: customId, + ImageUrl: data.ImageUrl, + ThumbnailUrl: data.ThumbnailUrl, + ButtonStyle: int(data.ButtonStyle), + ButtonLabel: data.ButtonLabel, + FormId: data.FormId, + NamingScheme: data.NamingScheme, } panelId, err := dbclient.Client.Panel.Create(panel) @@ -214,8 +228,27 @@ func CreatePanel(ctx *gin.Context) { } var urlRegex = regexp.MustCompile(`^https?://([-a-zA-Z0-9@:%._+~#=]{1,256})\.[a-zA-Z0-9()]{1,63}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$`) +var validate = validator.New() func (p *panelBody) doValidations(ctx *gin.Context, guildId uint64) bool { + err := validate.Struct(p) + if err != nil { + validationErrors, ok := err.(validator.ValidationErrors) + if !ok { + ctx.JSON(500, utils.ErrorStr("An error occurred while validating the panel")) + return false + } + + formatted := "Your input contained the following errors:" + for _, validationError := range validationErrors { + formatted += fmt.Sprintf("\n%s", validationError.Error()) + } + + formatted = strings.TrimSuffix(formatted, "\n") + ctx.JSON(400, utils.ErrorStr(formatted)) + return false + } + botContext, err := botcontext.ContextForGuild(guildId) if err != nil { return false // TODO: Log error @@ -254,14 +287,6 @@ func (p *panelBody) doValidations(ctx *gin.Context, guildId uint64) bool { return false } - if !p.verifyWelcomeMessage() { - ctx.JSON(400, gin.H{ - "success": false, - "error": "Welcome message must be blank or between 1 - 4096 characters", - }) - return false - } - if !p.verifyImageUrl() || !p.verifyThumbnailUrl() { ctx.JSON(400, gin.H{ "success": false, @@ -405,10 +430,6 @@ func (p *panelBody) verifyEmoji(ctx botcontext.BotContext, guildId uint64) bool } } -func (p *panelBody) verifyWelcomeMessage() bool { - return p.WelcomeMessage == nil || (len(*p.WelcomeMessage) > 0 && len(*p.WelcomeMessage) <= 4096) -} - func (p *panelBody) verifyImageUrl() bool { if p.ImageUrl != nil && len(*p.ImageUrl) == 0 { p.ImageUrl = nil diff --git a/app/http/endpoints/api/panel/paneldelete.go b/app/http/endpoints/api/panel/paneldelete.go index 04801d7..e585c3e 100644 --- a/app/http/endpoints/api/panel/paneldelete.go +++ b/app/http/endpoints/api/panel/paneldelete.go @@ -45,6 +45,14 @@ func DeletePanel(ctx *gin.Context) { return } + // Delete welcome message embed + if panel.WelcomeMessageEmbed != nil { + if err := database.Client.Embeds.Delete(*panel.WelcomeMessageEmbed); err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + } + if err := database.Client.Panel.Delete(panelId); err != nil { ctx.JSON(500, utils.ErrorJson(err)) return diff --git a/app/http/endpoints/api/panel/panellist.go b/app/http/endpoints/api/panel/panellist.go index a5a5c32..ec9c7e3 100644 --- a/app/http/endpoints/api/panel/panellist.go +++ b/app/http/endpoints/api/panel/panellist.go @@ -14,21 +14,25 @@ import ( func ListPanels(ctx *gin.Context) { type panelResponse struct { database.Panel - UseCustomEmoji bool `json:"use_custom_emoji"` - Emoji types.Emoji `json:"emote"` - Mentions []string `json:"mentions"` - Teams []int `json:"teams"` - UseServerDefaultNamingScheme bool `json:"use_server_default_naming_scheme"` + WelcomeMessage *types.CustomEmbed `json:"welcome_message"` + UseCustomEmoji bool `json:"use_custom_emoji"` + Emoji types.Emoji `json:"emote"` + Mentions []string `json:"mentions"` + Teams []int `json:"teams"` + UseServerDefaultNamingScheme bool `json:"use_server_default_naming_scheme"` } guildId := ctx.Keys["guildid"].(uint64) - panels, err := dbclient.Client.Panel.GetByGuild(guildId) + panels, err := dbclient.Client.Panel.GetByGuildWithWelcomeMessage(guildId) if err != nil { - ctx.AbortWithStatusJSON(500, gin.H{ - "success": false, - "error": err.Error(), - }) + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + allFields, err := dbclient.Client.EmbedFields.GetAllFieldsForPanels(guildId) + if err != nil { + ctx.JSON(500, utils.ErrorJson(err)) return } @@ -75,8 +79,15 @@ func ListPanels(ctx *gin.Context) { teamIds = make([]int, 0) } + var welcomeMessage *types.CustomEmbed + if p.WelcomeMessage != nil { + fields := allFields[p.WelcomeMessage.Id] + welcomeMessage = types.NewCustomEmbed(p.WelcomeMessage, fields) + } + wrapped[i] = panelResponse{ - Panel: p, + Panel: p.Panel, + WelcomeMessage: welcomeMessage, UseCustomEmoji: p.EmojiId != nil, Emoji: types.NewEmoji(p.EmojiName, p.EmojiId), Mentions: mentions, diff --git a/app/http/endpoints/api/panel/panelupdate.go b/app/http/endpoints/api/panel/panelupdate.go index 45eb1f8..fb05388 100644 --- a/app/http/endpoints/api/panel/panelupdate.go +++ b/app/http/endpoints/api/panel/panelupdate.go @@ -2,6 +2,7 @@ package api import ( "errors" + "fmt" "github.com/TicketsBot/GoPanel/botcontext" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/rpc" @@ -105,31 +106,67 @@ func UpdatePanel(ctx *gin.Context) { } } + // Update welcome message + var welcomeMessageEmbed *int + if data.WelcomeMessage == nil { + if existing.WelcomeMessageEmbed != nil { // If welcome message wasn't null, but now is, delete the embed + if err := dbclient.Client.Embeds.Delete(*existing.WelcomeMessageEmbed); err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + } // else, welcomeMessageEmbed will be nil + } else { + // TODO: Upsert? Don't think we can, as no unique key in the table, panel_id is in panels table + if existing.WelcomeMessageEmbed == nil { // Create + embed, fields := data.WelcomeMessage.IntoDatabaseStruct() + embed.GuildId = guildId + + id, err := dbclient.Client.Embeds.CreateWithFields(embed, fields) + if err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + welcomeMessageEmbed = &id + } else { // Update + welcomeMessageEmbed = existing.WelcomeMessageEmbed + + embed, fields := data.WelcomeMessage.IntoDatabaseStruct() + embed.Id = *existing.WelcomeMessageEmbed + embed.GuildId = guildId + + if err := dbclient.Client.Embeds.UpdateWithFields(embed, fields); err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + } + } + // Store in DB panel := database.Panel{ - PanelId: panelId, - MessageId: newMessageId, - ChannelId: data.ChannelId, - GuildId: guildId, - Title: data.Title, - Content: data.Content, - Colour: int32(data.Colour), - TargetCategory: data.CategoryId, - EmojiName: emojiName, - EmojiId: emojiId, - WelcomeMessage: data.WelcomeMessage, - WithDefaultTeam: data.WithDefaultTeam, - CustomId: existing.CustomId, - ImageUrl: data.ImageUrl, - ThumbnailUrl: data.ThumbnailUrl, - ButtonStyle: int(data.ButtonStyle), - ButtonLabel: data.ButtonLabel, - FormId: data.FormId, - NamingScheme: data.NamingScheme, + PanelId: panelId, + MessageId: newMessageId, + ChannelId: data.ChannelId, + GuildId: guildId, + Title: data.Title, + Content: data.Content, + Colour: int32(data.Colour), + TargetCategory: data.CategoryId, + EmojiName: emojiName, + EmojiId: emojiId, + WelcomeMessageEmbed: welcomeMessageEmbed, + WithDefaultTeam: data.WithDefaultTeam, + CustomId: existing.CustomId, + ImageUrl: data.ImageUrl, + ThumbnailUrl: data.ThumbnailUrl, + ButtonStyle: int(data.ButtonStyle), + ButtonLabel: data.ButtonLabel, + FormId: data.FormId, + NamingScheme: data.NamingScheme, } if err = dbclient.Client.Panel.Update(panel); err != nil { - ctx.AbortWithStatusJSON(500, utils.ErrorJson(err)) + ctx.JSON(500, utils.ErrorJson(err)) return } @@ -149,7 +186,7 @@ func UpdatePanel(ctx *gin.Context) { } else { roleId, err := strconv.ParseUint(mention, 10, 64) if err != nil { - ctx.AbortWithStatusJSON(500, utils.ErrorJson(err)) + ctx.JSON(500, utils.ErrorJson(err)) return } @@ -159,6 +196,7 @@ func UpdatePanel(ctx *gin.Context) { } } + fmt.Println(panel.PanelId) if err := dbclient.Client.PanelUserMention.Set(panel.PanelId, shouldMentionUser); err != nil { ctx.AbortWithStatusJSON(500, utils.ErrorJson(err)) return diff --git a/frontend/src/components/Badge.svelte b/frontend/src/components/Badge.svelte index c46063e..2e14517 100644 --- a/frontend/src/components/Badge.svelte +++ b/frontend/src/components/Badge.svelte @@ -12,5 +12,6 @@ font-size: 14px; padding: 0 4px; margin-left: 4px; + margin-bottom: 5px; } \ No newline at end of file diff --git a/frontend/src/components/Collapsible.svelte b/frontend/src/components/Collapsible.svelte new file mode 100644 index 0000000..b15b933 --- /dev/null +++ b/frontend/src/components/Collapsible.svelte @@ -0,0 +1,80 @@ +
+
+ {#if expanded} + + {:else} + + {/if} + + + +
+
+ +
+ +
+
+ + + + \ No newline at end of file diff --git a/frontend/src/components/EmbedBuilder.svelte b/frontend/src/components/EmbedBuilder.svelte new file mode 100644 index 0000000..10ab8ea --- /dev/null +++ b/frontend/src/components/EmbedBuilder.svelte @@ -0,0 +1,208 @@ +