custom mentions

This commit is contained in:
rxdn 2020-06-19 20:48:02 +01:00
parent b8e2136cb7
commit de1d41da05
22 changed files with 327 additions and 1189 deletions

6
.gitattributes vendored
View File

@ -1,9 +1,3 @@
public/static/css/animate.min.css linguist-vendored public/static/css/animate.min.css linguist-vendored
public/static/css/bootstrap.min.css linguist-vendored public/static/css/bootstrap.min.css linguist-vendored
public/static/css/light-bootstrap-dashboard.css linguist-vendored public/static/css/light-bootstrap-dashboard.css linguist-vendored
public/static/js/bootstrap-notify.js linguist-vendored
public/static/js/bootstrap-select.js linguist-vendored
public/static/js/bootstrap.min.js linguist-vendored
public/static/js/chartist.min.js linguist-vendored
public/static/js/jquery.3.2.1.min.js linguist-vendored
public/static/js/light-bootstrap-dashboard.js linguist-vendored

View File

@ -125,6 +125,40 @@ func CreatePanel(ctx *gin.Context) {
return return
} }
// insert role mention data
// string is role ID or "user" to mention the ticket opener
for _, mention := range data.Mentions {
if mention == "user" {
if err = dbclient.Client.PanelUserMention.Set(msgId, true); err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
} else {
roleId, err := strconv.ParseUint(mention, 10, 64)
if err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
// should we check the role is a valid role in the guild?
// not too much of an issue if it isnt
if err = dbclient.Client.PanelRoleMentions.Add(msgId, roleId); err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
}
}
ctx.JSON(200, gin.H{ ctx.JSON(200, gin.H{
"success": true, "success": true,
"message_id": strconv.FormatUint(msgId, 10), "message_id": strconv.FormatUint(msgId, 10),

View File

@ -1,8 +1,11 @@
package api package api
import ( import (
"context"
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
"strconv"
) )
type panel struct { type panel struct {
@ -14,6 +17,7 @@ type panel struct {
CategoryId uint64 `json:"category_id,string"` CategoryId uint64 `json:"category_id,string"`
Emote string `json:"emote"` Emote string `json:"emote"`
WelcomeMessage *string `json:"welcome_message"` WelcomeMessage *string `json:"welcome_message"`
Mentions []string `json:"mentions"`
} }
func ListPanels(ctx *gin.Context) { func ListPanels(ctx *gin.Context) {
@ -30,17 +34,56 @@ func ListPanels(ctx *gin.Context) {
wrapped := make([]panel, len(panels)) wrapped := make([]panel, len(panels))
// we will need to lookup role mentions
group, _ := errgroup.WithContext(context.Background())
for i, p := range panels { for i, p := range panels {
group.Go(func() (err error) {
var mentions []string
// get role mentions
roles, err := database.Client.PanelRoleMentions.GetRoles(p.MessageId)
if err != nil {
return err
}
// convert to strings
for _, roleId := range roles {
mentions = append(mentions, strconv.FormatUint(roleId, 10))
}
// get if we should mention the ticket opener
shouldMention, err := database.Client.PanelUserMention.ShouldMentionUser(p.MessageId)
if err != nil {
return err
}
if shouldMention {
mentions = append(mentions, "user")
}
wrapped[i] = panel{ wrapped[i] = panel{
ChannelId: p.ChannelId,
MessageId: p.MessageId, MessageId: p.MessageId,
ChannelId: p.ChannelId,
Title: p.Title, Title: p.Title,
Content: p.Content, Content: p.Content,
Colour: uint32(p.Colour), Colour: uint32(p.Colour),
CategoryId: p.TargetCategory, CategoryId: p.TargetCategory,
Emote: p.ReactionEmote, Emote: p.ReactionEmote,
WelcomeMessage: p.WelcomeMessage, WelcomeMessage: p.WelcomeMessage,
Mentions: mentions,
} }
return nil
})
}
if err := group.Wait(); err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
} }
ctx.JSON(200, wrapped) ctx.JSON(200, wrapped)

View File

@ -0,0 +1,17 @@
package api
import (
"github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/gin-gonic/gin"
)
func RolesHandler(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
roles := cache.Instance.GetGuildRoles(guildId)
ctx.JSON(200, gin.H{
"success": true,
"roles": roles,
})
}

View File

@ -78,6 +78,7 @@ func StartServer() {
guildAuthApi.GET("/channels", api.ChannelsHandler) guildAuthApi.GET("/channels", api.ChannelsHandler)
guildAuthApi.GET("/premium", api.PremiumHandler) guildAuthApi.GET("/premium", api.PremiumHandler)
guildAuthApi.GET("/user/:user", api.UserHandler) guildAuthApi.GET("/user/:user", api.UserHandler)
guildAuthApi.GET("/roles", api.RolesHandler)
guildAuthApi.GET("/settings", api.GetSettingsHandler) guildAuthApi.GET("/settings", api.GetSettingsHandler)
guildAuthApi.POST("/settings", api.UpdateSettingsHandler) guildAuthApi.POST("/settings", api.UpdateSettingsHandler)

2
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/TicketsBot/archiverclient v0.0.0-20200425115930-0ca198cc8306 github.com/TicketsBot/archiverclient v0.0.0-20200425115930-0ca198cc8306
github.com/TicketsBot/common v0.0.0-20200529141045-7426ad13f1a4 github.com/TicketsBot/common v0.0.0-20200529141045-7426ad13f1a4
github.com/TicketsBot/database v0.0.0-20200614182426-d117a0e3f769 github.com/TicketsBot/database v0.0.0-20200619194554-a6db672a94cf
github.com/apex/log v1.1.2 github.com/apex/log v1.1.2
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2058,7 +2058,7 @@ fieldset[disabled] .btn-neutral.active {
margin: 10px 10px 10px 0px; margin: 10px 10px 10px 0px;
} }
.dropdown-menu { /*.dropdown-menu {
visibility: hidden; visibility: hidden;
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -2167,7 +2167,7 @@ fieldset[disabled] .btn-neutral.active {
.dropdown-menu.dropdown-red > li > a:hover, .dropdown-menu.dropdown-red > li > a:hover,
.dropdown-menu.dropdown-red > li > a:focus { .dropdown-menu.dropdown-red > li > a:focus {
background-color: rgba(255, 74, 85, 0.2); background-color: rgba(255, 74, 85, 0.2);
} }*/
.dropdown-with-icons > li > a { .dropdown-with-icons > li > a {
padding-left: 0px; padding-left: 0px;
@ -3211,11 +3211,6 @@ fieldset[disabled] .btn-neutral.active {
padding-right: 5px; padding-right: 5px;
} }
.navbar-nav > li > .dropdown-menu, .dropdown .dropdown-menu { .navbar-nav > li > .dropdown-menu, .dropdown .dropdown-menu {
-webkit-transform: scale(0);
-moz-transform: scale(0);
-o-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
-webkit-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1); -webkit-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1);
-moz-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1); -moz-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1);
-o-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1); -o-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1);

View File

@ -15,7 +15,7 @@ body {
color: white; color: white;
} }
::-webkit-scrollbar { html > ::-webkit-scrollbar {
display: none; display: none;
} }
@ -161,3 +161,37 @@ body {
.content { .content {
display: none; display: none;
} }
/* drop down menu */
.dropdown-menu {
background-color: #2e3136 !important;
}
.btn-light:not(:disabled):not(.disabled).active, .btn-light:not(:disabled):not(.disabled):active, .show>.btn-light.dropdown-toggle {
background-color: #2e3136 !important;
}
.bs-searchbox input {
background-color: white !important;
color: #2e3136 !important;
}
.dropdown-item > .text {
color: white !important;
}
.dropdown-item:hover {
background-color: #272727 !important;
}
.filter-option-inner-inner {
color: white !important;
}
.dropdown-toggle {
border-color: #2e3136 !important;
}
.bootstrap-select .bs-ok-default:after {
color: #2ECC71 !important;
}

27
public/static/js/auth.js Normal file
View File

@ -0,0 +1,27 @@
async function getToken() {
let token = window.localStorage.getItem('token');
if (token == null) {
let res = await axios.post('/token', {
withCredentials: true
});
if (res.status !== 200 || !res.data.success) {
console.log("An error occurred whilst retrieving an authentication token. Please contact the developer");
console.log(res);
return;
}
token = res.data.token;
localStorage.setItem('token', token);
}
return token;
}
async function setDefaultHeader() {
const token = await getToken();
axios.defaults.headers.common['Authorization'] = token;
axios.defaults.validateStatus = false;
}
setDefaultHeader();

View File

@ -1,404 +0,0 @@
/*
Creative Tim Modifications
Lines: 239, 240 was changed from top: 5px to top: 50% and we added margin-top: -13px. In this way the close button will be aligned vertically
Line:242 - modified when the icon is set, we add the class "alert-with-icon", so there will be enough space for the icon.
*/
/*
* Project: Bootstrap Notify = v3.1.5
* Description: Turns standard Bootstrap alerts into "Growl-like" notifications.
* Author: Mouse0270 aka Robert McIntosh
* License: MIT License
* Website: https://github.com/mouse0270/bootstrap-growl
*/
/* global define:false, require: false, jQuery:false */
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS
factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
// Create the defaults once
var defaults = {
element: 'body',
position: null,
type: "info",
allow_dismiss: true,
allow_duplicates: true,
newest_on_top: false,
showProgressbar: false,
placement: {
from: "top",
align: "right"
},
offset: 20,
spacing: 10,
z_index: 1031,
delay: 5000,
timer: 1000,
url_target: '_blank',
mouse_over: null,
animate: {
enter: 'animated fadeInDown',
exit: 'animated fadeOutUp'
},
onShow: null,
onShown: null,
onClose: null,
onClosed: null,
icon_type: 'class',
template: '<div data-notify="container" class="col-xs-11 col-sm-4 alert alert-{0}" role="alert"><button type="button" aria-hidden="true" class="close" data-notify="dismiss">&times;</button><span data-notify="icon"></span> <span data-notify="title">{1}</span> <span data-notify="message">{2}</span><div class="progress" data-notify="progressbar"><div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div></div><a href="{3}" target="{4}" data-notify="url"></a></div>'
};
String.format = function () {
var str = arguments[0];
for (var i = 1; i < arguments.length; i++) {
str = str.replace(RegExp("\\{" + (i - 1) + "\\}", "gm"), arguments[i]);
}
return str;
};
function isDuplicateNotification(notification) {
var isDupe = false;
$('[data-notify="container"]').each(function (i, el) {
var $el = $(el);
var title = $el.find('[data-notify="title"]').text().trim();
var message = $el.find('[data-notify="message"]').html().trim();
// The input string might be different than the actual parsed HTML string!
// (<br> vs <br /> for example)
// So we have to force-parse this as HTML here!
var isSameTitle = title === $("<div>" + notification.settings.content.title + "</div>").html().trim();
var isSameMsg = message === $("<div>" + notification.settings.content.message + "</div>").html().trim();
var isSameType = $el.hasClass('alert-' + notification.settings.type);
if (isSameTitle && isSameMsg && isSameType) {
//we found the dupe. Set the var and stop checking.
isDupe = true;
}
return !isDupe;
});
return isDupe;
}
function Notify(element, content, options) {
// Setup Content of Notify
var contentObj = {
content: {
message: typeof content === 'object' ? content.message : content,
title: content.title ? content.title : '',
icon: content.icon ? content.icon : '',
url: content.url ? content.url : '#',
target: content.target ? content.target : '-'
}
};
options = $.extend(true, {}, contentObj, options);
this.settings = $.extend(true, {}, defaults, options);
this._defaults = defaults;
if (this.settings.content.target === "-") {
this.settings.content.target = this.settings.url_target;
}
this.animations = {
start: 'webkitAnimationStart oanimationstart MSAnimationStart animationstart',
end: 'webkitAnimationEnd oanimationend MSAnimationEnd animationend'
};
if (typeof this.settings.offset === 'number') {
this.settings.offset = {
x: this.settings.offset,
y: this.settings.offset
};
}
//if duplicate messages are not allowed, then only continue if this new message is not a duplicate of one that it already showing
if (this.settings.allow_duplicates || (!this.settings.allow_duplicates && !isDuplicateNotification(this))) {
this.init();
}
}
$.extend(Notify.prototype, {
init: function () {
var self = this;
this.buildNotify();
if (this.settings.content.icon) {
this.setIcon();
}
if (this.settings.content.url != "#") {
this.styleURL();
}
this.styleDismiss();
this.placement();
this.bind();
this.notify = {
$ele: this.$ele,
update: function (command, update) {
var commands = {};
if (typeof command === "string") {
commands[command] = update;
} else {
commands = command;
}
for (var cmd in commands) {
switch (cmd) {
case "type":
this.$ele.removeClass('alert-' + self.settings.type);
this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass('progress-bar-' + self.settings.type);
self.settings.type = commands[cmd];
this.$ele.addClass('alert-' + commands[cmd]).find('[data-notify="progressbar"] > .progress-bar').addClass('progress-bar-' + commands[cmd]);
break;
case "icon":
var $icon = this.$ele.find('[data-notify="icon"]');
if (self.settings.icon_type.toLowerCase() === 'class') {
$icon.removeClass(self.settings.content.icon).addClass(commands[cmd]);
} else {
if (!$icon.is('img')) {
$icon.find('img');
}
$icon.attr('src', commands[cmd]);
}
break;
case "progress":
var newDelay = self.settings.delay - (self.settings.delay * (commands[cmd] / 100));
this.$ele.data('notify-delay', newDelay);
this.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', commands[cmd]).css('width', commands[cmd] + '%');
break;
case "url":
this.$ele.find('[data-notify="url"]').attr('href', commands[cmd]);
break;
case "target":
this.$ele.find('[data-notify="url"]').attr('target', commands[cmd]);
break;
default:
this.$ele.find('[data-notify="' + cmd + '"]').html(commands[cmd]);
}
}
var posX = this.$ele.outerHeight() + parseInt(self.settings.spacing) + parseInt(self.settings.offset.y);
self.reposition(posX);
},
close: function () {
self.close();
}
};
},
buildNotify: function () {
var content = this.settings.content;
this.$ele = $(String.format(this.settings.template, this.settings.type, content.title, content.message, content.url, content.target));
this.$ele.attr('data-notify-position', this.settings.placement.from + '-' + this.settings.placement.align);
if (!this.settings.allow_dismiss) {
this.$ele.find('[data-notify="dismiss"]').css('display', 'none');
}
if ((this.settings.delay <= 0 && !this.settings.showProgressbar) || !this.settings.showProgressbar) {
this.$ele.find('[data-notify="progressbar"]').remove();
}
},
setIcon: function () {
this.$ele.addClass('alert-with-icon');
if (this.settings.icon_type.toLowerCase() === 'class') {
this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon);
} else {
if (this.$ele.find('[data-notify="icon"]').is('img')) {
this.$ele.find('[data-notify="icon"]').attr('src', this.settings.content.icon);
} else {
this.$ele.find('[data-notify="icon"]').append('<img src="' + this.settings.content.icon + '" alt="Notify Icon" />');
}
}
},
styleDismiss: function () {
this.$ele.find('[data-notify="dismiss"]').css({
position: 'absolute',
right: '10px',
top: '50%',
marginTop: '-13px',
zIndex: this.settings.z_index + 2
});
},
styleURL: function () {
this.$ele.find('[data-notify="url"]').css({
backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)',
height: '100%',
left: 0,
position: 'absolute',
top: 0,
width: '100%',
zIndex: this.settings.z_index + 1
});
},
placement: function () {
var self = this,
offsetAmt = this.settings.offset.y,
css = {
display: 'inline-block',
margin: '0px auto',
position: this.settings.position ? this.settings.position : (this.settings.element === 'body' ? 'fixed' : 'absolute'),
transition: 'all .5s ease-in-out',
zIndex: this.settings.z_index
},
hasAnimation = false,
settings = this.settings;
$('[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])').each(function () {
offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + parseInt($(this).outerHeight()) + parseInt(settings.spacing));
});
if (this.settings.newest_on_top === true) {
offsetAmt = this.settings.offset.y;
}
css[this.settings.placement.from] = offsetAmt + 'px';
switch (this.settings.placement.align) {
case "left":
case "right":
css[this.settings.placement.align] = this.settings.offset.x + 'px';
break;
case "center":
css.left = 0;
css.right = 0;
break;
}
this.$ele.css(css).addClass(this.settings.animate.enter);
$.each(Array('webkit-', 'moz-', 'o-', 'ms-', ''), function (index, prefix) {
self.$ele[0].style[prefix + 'AnimationIterationCount'] = 1;
});
$(this.settings.element).append(this.$ele);
if (this.settings.newest_on_top === true) {
offsetAmt = (parseInt(offsetAmt) + parseInt(this.settings.spacing)) + this.$ele.outerHeight();
this.reposition(offsetAmt);
}
if ($.isFunction(self.settings.onShow)) {
self.settings.onShow.call(this.$ele);
}
this.$ele.one(this.animations.start, function () {
hasAnimation = true;
}).one(this.animations.end, function () {
if ($.isFunction(self.settings.onShown)) {
self.settings.onShown.call(this);
}
});
setTimeout(function () {
if (!hasAnimation) {
if ($.isFunction(self.settings.onShown)) {
self.settings.onShown.call(this);
}
}
}, 600);
},
bind: function () {
var self = this;
this.$ele.find('[data-notify="dismiss"]').on('click', function () {
self.close();
});
this.$ele.mouseover(function () {
$(this).data('data-hover', "true");
}).mouseout(function () {
$(this).data('data-hover', "false");
});
this.$ele.data('data-hover', "false");
if (this.settings.delay > 0) {
self.$ele.data('notify-delay', self.settings.delay);
var timer = setInterval(function () {
var delay = parseInt(self.$ele.data('notify-delay')) - self.settings.timer;
if ((self.$ele.data('data-hover') === 'false' && self.settings.mouse_over === "pause") || self.settings.mouse_over != "pause") {
var percent = ((self.settings.delay - delay) / self.settings.delay) * 100;
self.$ele.data('notify-delay', delay);
self.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', percent).css('width', percent + '%');
}
if (delay <= -(self.settings.timer)) {
clearInterval(timer);
self.close();
}
}, self.settings.timer);
}
},
close: function () {
var self = this,
posX = parseInt(this.$ele.css(this.settings.placement.from)),
hasAnimation = false;
this.$ele.data('closing', 'true').addClass(this.settings.animate.exit);
self.reposition(posX);
if ($.isFunction(self.settings.onClose)) {
self.settings.onClose.call(this.$ele);
}
this.$ele.one(this.animations.start, function () {
hasAnimation = true;
}).one(this.animations.end, function () {
$(this).remove();
if ($.isFunction(self.settings.onClosed)) {
self.settings.onClosed.call(this);
}
});
setTimeout(function () {
if (!hasAnimation) {
self.$ele.remove();
if (self.settings.onClosed) {
self.settings.onClosed(self.$ele);
}
}
}, 600);
},
reposition: function (posX) {
var self = this,
notifies = '[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])',
$elements = this.$ele.nextAll(notifies);
if (this.settings.newest_on_top === true) {
$elements = this.$ele.prevAll(notifies);
}
$elements.each(function () {
$(this).css(self.settings.placement.from, posX);
posX = (parseInt(posX) + parseInt(self.settings.spacing)) + $(this).outerHeight();
});
}
});
$.notify = function (content, options) {
var plugin = new Notify(this, content, options);
return plugin.notify;
};
$.notifyDefaults = function (options) {
defaults = $.extend(true, {}, defaults, options);
return defaults;
};
$.notifyClose = function (command) {
if (typeof command === "undefined" || command === "all") {
$('[data-notify]').find('[data-notify="dismiss"]').trigger('click');
} else {
$('[data-notify-position="' + command + '"]').find('[data-notify="dismiss"]').trigger('click');
}
};
}));

View File

@ -1,438 +0,0 @@
!function($) {
var Selectpicker = function(element, options, e) {
if (e ) {
e.stopPropagation();
e.preventDefault();
}
this.$element = $(element);
this.$newElement = null;
this.button = null;
//Merge defaults, options and data-attributes to make our options
this.options = $.extend({}, $.fn.selectpicker.defaults, this.$element.data(), typeof options == 'object' && options);
//If we have no title yet, check the attribute 'title' (this is missed by jq as its not a data-attribute
if(this.options.title==null)
this.options.title = this.$element.attr('title');
//Expose public methods
this.val = Selectpicker.prototype.val;
this.render = Selectpicker.prototype.render;
this.init();
};
Selectpicker.prototype = {
constructor: Selectpicker,
init: function (e) {
var _this = this;
this.$element.hide();
this.multiple = this.$element.prop('multiple');
var classList = this.$element.attr('class') !== undefined ? this.$element.attr('class').split(/\s+/) : '';
var id = this.$element.attr('id');
this.$element.after( this.createView() );
this.$newElement = this.$element.next('.select');
var select = this.$newElement;
var menu = this.$newElement.find('.dropdown-menu');
var menuArrow = this.$newElement.find('.dropdown-arrow');
var menuA = menu.find('li > a');
var liHeight = select.addClass('open').find('.dropdown-menu li > a').outerHeight();
select.removeClass('open');
var divHeight = menu.find('li .divider').outerHeight(true);
var selectOffset_top = this.$newElement.offset().top;
var size = 0;
var menuHeight = 0;
var selectHeight = this.$newElement.outerHeight();
this.button = this.$newElement.find('> button');
if (id !== undefined) {
this.button.attr('id', id);
$('label[for="' + id + '"]').click(function(){ select.find('button#'+id).focus(); })
}
for (var i = 0; i < classList.length; i++) {
if(classList[i] != 'selectpicker') {
this.$newElement.addClass(classList[i]);
}
}
//If we are multiple, then add the show-tick class by default
if(this.multiple) {
this.$newElement.addClass('select-multiple');
}
this.button.addClass(this.options.style);
menu.addClass(this.options.menuStyle);
menuArrow.addClass(function() {
if (_this.options.menuStyle) {
return _this.options.menuStyle.replace('dropdown-', 'dropdown-arrow-');
}
});
this.checkDisabled();
this.checkTabIndex();
this.clickListener();
var menuPadding = parseInt(menu.css('padding-top')) + parseInt(menu.css('padding-bottom')) + parseInt(menu.css('border-top-width')) + parseInt(menu.css('border-bottom-width'));
if (this.options.size == 'auto') {
// Creative Tim Changes: We changed the regular function made in bootstrap-select with this function so the getSize() will not be triggered one million times per second while you scroll.
var getSize = debounce(function() {
var selectOffset_top_scroll = selectOffset_top - $(window).scrollTop();
var windowHeight = $(window).innerHeight();
var menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2;
var selectOffset_bot = windowHeight - selectOffset_top_scroll - selectHeight - menuExtras;
menuHeight = selectOffset_bot;
if (select.hasClass('dropup')) {
menuHeight = selectOffset_top_scroll - menuExtras;
}
//limit menuHeight to 300px to have a smooth transition with cubic bezier on dropdown
if(menuHeight >= 300){
menuHeight = 300;
}
menu.css({'max-height' : menuHeight + 'px', 'overflow-y' : 'auto', 'min-height' : liHeight * 3 + 'px'});
}, 50);
getSize;
$(window).on('scroll', getSize);
$(window).on('resize', getSize);
if (window.MutationObserver) {
new MutationObserver(getSize).observe(this.$element.get(0), {
childList: true
});
} else {
this.$element.bind('DOMNodeInserted', getSize);
}
} else if (this.options.size && this.options.size != 'auto' && menu.find('li').length > this.options.size) {
var optIndex = menu.find("li > *").filter(':not(.divider)').slice(0,this.options.size).last().parent().index();
var divLength = menu.find("li").slice(0,optIndex + 1).find('.divider').length;
menuHeight = liHeight*this.options.size + divLength*divHeight + menuPadding;
menu.css({'max-height' : menuHeight + 'px', 'overflow-y' : 'scroll'});
//console.log('sunt in if');
}
// Listen for updates to the DOM and re render... (Use Mutation Observer when availiable)
if (window.MutationObserver) {
new MutationObserver($.proxy(this.reloadLi, this)).observe(this.$element.get(0), {
childList: true
});
} else {
this.$element.bind('DOMNodeInserted', $.proxy(this.reloadLi, this));
}
this.render();
},
createDropdown: function() {
var drop =
"<div class='btn-group select'>" +
"<button class='btn dropdown-toggle clearfix' data-toggle='dropdown'>" +
"<span class='filter-option'></span>&nbsp;" +
"<span class='caret'></span>" +
"</button>" +
"<span class='dropdown-arrow'></span>" +
"<ul class='dropdown-menu' role='menu'>" +
"</ul>" +
"</div>";
return $(drop);
},
createView: function() {
var $drop = this.createDropdown();
var $li = this.createLi();
$drop.find('ul').append($li);
return $drop;
},
reloadLi: function() {
//Remove all children.
this.destroyLi();
//Re build
$li = this.createLi();
this.$newElement.find('ul').append( $li );
//render view
this.render();
},
destroyLi:function() {
this.$newElement.find('li').remove();
},
createLi: function() {
var _this = this;
var _li = [];
var _liA = [];
var _liHtml = '';
this.$element.find('option').each(function(){
_li.push($(this).text());
});
this.$element.find('option').each(function(index) {
//Get the class and text for the option
var optionClass = $(this).attr("class") !== undefined ? $(this).attr("class") : '';
var text = $(this).text();
var subtext = $(this).data('subtext') !== undefined ? '<small class="muted">'+$(this).data('subtext')+'</small>' : '';
//Append any subtext to the main text.
text+=subtext;
if ($(this).parent().is('optgroup') && $(this).data('divider') != true) {
if ($(this).index() == 0) {
//Get the opt group label
var label = $(this).parent().attr('label');
var labelSubtext = $(this).parent().data('subtext') !== undefined ? '<small class="muted">'+$(this).parent().data('subtext')+'</small>' : '';
label += labelSubtext;
if ($(this)[0].index != 0) {
_liA.push(
'<div class="divider"></div>'+
'<dt>'+label+'</dt>'+
_this.createA(text, "opt " + optionClass )
);
} else {
_liA.push(
'<dt>'+label+'</dt>'+
_this.createA(text, "opt " + optionClass ));
}
} else {
_liA.push( _this.createA(text, "opt " + optionClass ) );
}
} else if ($(this).data('divider') == true) {
_liA.push('<div class="divider"></div>');
} else if ($(this).data('hidden') == true) {
_liA.push('');
} else {
_liA.push( _this.createA(text, optionClass ) );
}
});
if (_li.length > 0) {
for (var i = 0; i < _li.length; i++) {
var $option = this.$element.find('option').eq(i);
_liHtml += "<li rel=" + i + ">" + _liA[i] + "</li>";
}
}
//If we dont have a selected item, and we dont have a title, select the first element so something is set in the button
if(this.$element.find('option:selected').length==0 && !_this.options.title) {
this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected');
}
return $(_liHtml);
},
createA:function(test, classes) {
return '<a tabindex="-1" href="#" class="'+classes+'">' +
'<span class="">' + test + '</span>' +
'</a>';
},
render:function() {
var _this = this;
//Set width of select
if (this.options.width == 'auto') {
var ulWidth = this.$newElement.find('.dropdown-menu').css('width');
this.$newElement.css('width',ulWidth);
} else if (this.options.width && this.options.width != 'auto') {
this.$newElement.css('width',this.options.width);
}
//Update the LI to match the SELECT
this.$element.find('option').each(function(index) {
_this.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled') );
_this.setSelected(index, $(this).is(':selected') );
});
var selectedItems = this.$element.find('option:selected').map(function(index,value) {
if($(this).attr('title')!=undefined) {
return $(this).attr('title');
} else {
return $(this).text();
}
}).toArray();
//Convert all the values into a comma delimited string
var title = selectedItems.join(", ");
//If this is multi select, and the selectText type is count, the show 1 of 2 selected etc..
if(_this.multiple && _this.options.selectedTextFormat.indexOf('count') > -1) {
var max = _this.options.selectedTextFormat.split(">");
if( (max.length>1 && selectedItems.length > max[1]) || (max.length==1 && selectedItems.length>=2)) {
title = selectedItems.length +' of ' + this.$element.find('option').length + ' selected';
}
}
//If we dont have a title, then use the default, or if nothing is set at all, use the not selected text
if(!title) {
title = _this.options.title != undefined ? _this.options.title : _this.options.noneSelectedText;
}
this.$element.next('.select').find('.filter-option').html( title );
},
setSelected:function(index, selected) {
if(selected) {
this.$newElement.find('li').eq(index).addClass('selected');
} else {
this.$newElement.find('li').eq(index).removeClass('selected');
}
},
setDisabled:function(index, disabled) {
if(disabled) {
this.$newElement.find('li').eq(index).addClass('disabled');
} else {
this.$newElement.find('li').eq(index).removeClass('disabled');
}
},
checkDisabled: function() {
if (this.$element.is(':disabled')) {
this.button.addClass('disabled');
this.button.click(function(e) {
e.preventDefault();
});
}
},
checkTabIndex: function() {
if (this.$element.is('[tabindex]')) {
var tabindex = this.$element.attr("tabindex");
this.button.attr('tabindex', tabindex);
}
},
clickListener: function() {
var _this = this;
$('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stopPropagation(); });
this.$newElement.on('click', 'li a', function(e){
var clickedIndex = $(this).parent().index(),
$this = $(this).parent(),
$select = $this.parents('.select');
//Dont close on multi choice menu
if(_this.multiple) {
e.stopPropagation();
}
e.preventDefault();
//Dont run if we have been disabled
if ($select.prev('select').not(':disabled') && !$(this).parent().hasClass('disabled')){
//Deselect all others if not multi select box
if (!_this.multiple) {
$select.prev('select').find('option').removeAttr('selected');
$select.prev('select').find('option').eq(clickedIndex).prop('selected', true).attr('selected', 'selected');
}
//Else toggle the one we have chosen if we are multi selet.
else {
var selected = $select.prev('select').find('option').eq(clickedIndex).prop('selected');
if(selected) {
$select.prev('select').find('option').eq(clickedIndex).removeAttr('selected');
} else {
$select.prev('select').find('option').eq(clickedIndex).prop('selected', true).attr('selected', 'selected');
}
}
$select.find('.filter-option').html($this.text());
$select.find('button').focus();
// Trigger select 'change'
$select.prev('select').trigger('change');
}
});
this.$newElement.on('click', 'li.disabled a, li dt, li .divider', function(e) {
e.preventDefault();
e.stopPropagation();
$select = $(this).parent().parents('.select');
$select.find('button').focus();
});
this.$element.on('change', function(e) {
_this.render();
});
},
val:function(value) {
if(value!=undefined) {
this.$element.val( value );
this.$element.trigger('change');
return this.$element;
} else {
return this.$element.val();
}
}
};
$.fn.selectpicker = function(option, event) {
//get the args of the outer function..
var args = arguments;
var value;
var chain = this.each(function () {
var $this = $(this),
data = $this.data('selectpicker'),
options = typeof option == 'object' && option;
if (!data) {
$this.data('selectpicker', (data = new Selectpicker(this, options, event)));
} else {
for(var i in option) {
data[i]=option[i];
}
}
if (typeof option == 'string') {
//Copy the value of option, as once we shift the arguments
//it also shifts the value of option.
property = option;
if(data[property] instanceof Function) {
[].shift.apply(args);
value = data[property].apply(data, args);
} else {
value = data[property];
}
}
});
if(value!=undefined) {
return value;
} else {
return chain;
}
};
$.fn.selectpicker.defaults = {
style: null,
size: 'auto',
title: null,
selectedTextFormat : 'values',
noneSelectedText : 'Nothing selected',
width: null,
menuStyle: null,
toggleSize: null
}
}(window.jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,154 +0,0 @@
/*!
=========================================================
* Light Bootstrap Dashboard - v1.4.0
=========================================================
* Product Page: http://www.creative-tim.com/product/light-bootstrap-dashboard
* Copyright 2017 Creative Tim (http://www.creative-tim.com)
* Licensed under MIT (https://github.com/creativetimofficial/light-bootstrap-dashboard/blob/master/LICENSE.md)
=========================================================
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
var searchVisible = 0;
var transparent = true;
var transparentDemo = true;
var fixedTop = false;
var navbar_initialized = false;
$(document).ready(function(){
window_width = $(window).width();
// check if there is an image set for the sidebar's background
lbd.checkSidebarImage();
// Init navigation toggle for small screens
lbd.initRightMenu();
// Activate the tooltips
$('[rel="tooltip"]').tooltip();
$('.form-control').on("focus", function(){
$(this).parent('.input-group').addClass("input-group-focus");
}).on("blur", function(){
$(this).parent(".input-group").removeClass("input-group-focus");
});
// Fixes sub-nav not working as expected on IOS
$('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stopPropagation(); });
});
$(document).on('click', '.navbar-toggle', function(){
$toggle = $(this);
if(lbd.misc.navbar_menu_visible == 1) {
$('html').removeClass('nav-open');
lbd.misc.navbar_menu_visible = 0;
$('#bodyClick').remove();
setTimeout(function(){
$toggle.removeClass('toggled');
}, 550);
} else {
setTimeout(function(){
$toggle.addClass('toggled');
}, 580);
div = '<div id="bodyClick"></div>';
$(div).appendTo('body').click(function() {
$('html').removeClass('nav-open');
lbd.misc.navbar_menu_visible = 0;
setTimeout(function(){
$toggle.removeClass('toggled');
$('#bodyClick').remove();
}, 550);
});
$('html').addClass('nav-open');
lbd.misc.navbar_menu_visible = 1;
}
});
$(window).on('resize', function(){
if(navbar_initialized){
lbd.initRightMenu();
navbar_initialized = true;
}
});
lbd = {
misc:{
navbar_menu_visible: 0
},
checkSidebarImage: function(){
$sidebar = $('.sidebar');
image_src = $sidebar.data('image');
if(image_src !== undefined){
sidebar_container = '<div class="sidebar-background" style="background-image: url(' + image_src + ') "/>'
$sidebar.append(sidebar_container);
}
},
initRightMenu: debounce(function(){
if(!navbar_initialized){
$sidebar_wrapper = $('.sidebar-wrapper');
$navbar = $('nav').find('.navbar-collapse').html();
mobile_menu_content = '';
nav_content = $navbar;
nav_content = '<ul class="nav nav-mobile-menu">' + nav_content + '</ul>';
// navbar_form = $('nav').find('.navbar-form').get(0).outerHTML;
$sidebar_nav = $sidebar_wrapper.find(' > .nav');
// insert the navbar form before the sidebar list
$nav_content = $(nav_content);
// $navbar_form = $(navbar_form);
$nav_content.insertBefore($sidebar_nav);
// $navbar_form.insertBefore($nav_content);
$(".sidebar-wrapper .dropdown .dropdown-menu > li > a").click(function(event) {
event.stopPropagation();
});
mobile_menu_initialized = true;
} else {
if($(window).width() > 991){
// reset all the additions that we made for the sidebar wrapper only if the screen is bigger than 991px
// $sidebar_wrapper.find('.navbar-form').remove();
$sidebar_wrapper.find('.nav-mobile-menu').remove();
mobile_menu_initialized = false;
}
}
},200)
}
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
};

View File

@ -0,0 +1,23 @@
function showLoadingScreen() {
const content = document.getElementsByClassName('content')[0];
content.style.display = 'none';
document.getElementById('loading-container').style.display = 'block';
}
function hideLoadingScreen() {
document.getElementById('loading-container').style.display = 'none';
const content = document.getElementsByClassName('content')[0];
content.style.display = 'block';
content.classList.add('fade-in');
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function withLoadingScreen(func) {
showLoadingScreen();
await func();
hideLoadingScreen();
}

38
public/static/js/utils.js Normal file
View File

@ -0,0 +1,38 @@
function showToast(title, content) {
const container = document.getElementById('toast-container');
container.innerHTML += `
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header">
<strong class="mr-auto">${title}</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="toast-body">
${content}
</div>
</div>
`;
$('.toast').toast('show');
}
function appendTd(tr, content) {
const td = document.createElement('td');
td.appendChild(document.createTextNode(content));
td.classList.add('white');
tr.appendChild(td);
return td
}
function appendButton(tr, content, onclick) {
const tdRemove = document.createElement('td');
const removeButton = document.createElement('button');
removeButton.type = 'submit';
removeButton.classList.add('btn', 'btn-primary', 'btn-fill', 'mx-auto');
removeButton.appendChild(document.createTextNode(content));
removeButton.onclick = onclick;
tdRemove.appendChild(removeButton);
tr.appendChild(tdRemove);
}

View File

@ -22,87 +22,43 @@
background-size: cover; background-size: cover;
border-radius: 50%; border-radius: 50%;
float: right; float: right;
} }
</style> </style>
<!-- Vue
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"
integrity="sha256-T/f7Sju1ZfNNfBh7skWn0idlCBcI3RwdLSS4/I7NQKQ=" crossorigin="anonymous"></script> integrity="sha256-T/f7Sju1ZfNNfBh7skWn0idlCBcI3RwdLSS4/I7NQKQ=" crossorigin="anonymous"></script>
<script> <script src="/assets/js/auth.js"></script>
async function getToken() { <script src="/assets/js/loadingscreen.js"></script>
let token = window.localStorage.getItem('token'); <script src="/assets/js/utils.js"></script>
if (token == null) {
let res = await axios.post('/token', {
withCredentials: true
});
if (res.status !== 200 || !res.data.success) { <!-- Jquery -->
console.log("An error occurred whilst retrieving an authentication token. Please contact the developer"); <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.slim.min.js"
console.log(res); integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous"></script>
return;
}
token = res.data.token;
localStorage.setItem('token', token);
}
return token;
}
async function setDefaultHeader() {
const token = await getToken();
axios.defaults.headers.common['Authorization'] = token;
axios.defaults.validateStatus = false;
}
setDefaultHeader();
</script>
<script>
function showLoadingScreen() {
const content = document.getElementsByClassName('content')[0];
content.style.display = 'none';
document.getElementById('loading-container').style.display = 'block';
}
function hideLoadingScreen() {
document.getElementById('loading-container').style.display = 'none';
const content = document.getElementsByClassName('content')[0];
content.style.display = 'block';
content.classList.add('fade-in');
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function withLoadingScreen(func) {
showLoadingScreen();
await func();
hideLoadingScreen();
}
</script>
<!-- Bootstrap --> <!-- Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> integrity="sha256-aAr2Zpq8MZ+YA/D6JtRD3xtrwpEz2IqOS+pWD/7XKIw=" crossorigin="anonymous"/>
<link href="/assets/css/light-bootstrap-dashboard.css?v=1.4.0" rel="stylesheet"/> <link href="/assets/css/light-bootstrap-dashboard.css" rel="stylesheet"/>
<link href="/assets/css/animate.min.css" rel="stylesheet"/> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css"
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha256-6hqHMqXTVEds1R8HgKisLm3l/doneQs+rS1a5NLmwwo=" crossorigin="anonymous"/>
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"
integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" integrity="sha256-OFRAJNoaD8L3Br5lglV7VyLRf0itmoBzWUoM+Sji4/8=" crossorigin="anonymous"></script>
crossorigin="anonymous"></script>
<!-- Bootstrap select -->
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.17/css/bootstrap-select.min.css"
integrity="sha256-VMPhaMmJn7coDSbzwqB0jflvb+CDnoAlfStC5RogOQo=" crossorigin="anonymous"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.17/js/bootstrap-select.min.js"
integrity="sha256-QOE02Glo1C1gHzP96JOaxyIMt4XSFv/exZaYLY4dwO0=" crossorigin="anonymous"></script>
<script>
$.fn.selectpicker.Constructor.BootstrapVersion = '4';
</script>
<!-- Discord theme --> <!-- Discord theme -->
<link href="/assets/css/discordmock.css" rel="stylesheet"/> <link href="/assets/css/discordmock.css" rel="stylesheet"/>
@ -112,54 +68,15 @@
integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous"> integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
<!-- GA --> <!-- GA -->
<script async src='https://www.google-analytics.com/analytics.js'
type="80be96f83bbfbba3d4097e23-text/javascript"></script>
<script type="80be96f83bbfbba3d4097e23-text/javascript"> <script type="80be96f83bbfbba3d4097e23-text/javascript">
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date; window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-161945537-1', 'auto'); ga('create', 'UA-161945537-1', 'auto');
ga('send', 'pageview'); ga('send', 'pageview');
</script>
<script async src='https://www.google-analytics.com/analytics.js'
type="80be96f83bbfbba3d4097e23-text/javascript"></script>
<script>
function showToast(title, content) {
const container = document.getElementById('toast-container');
container.innerHTML += `
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header">
<strong class="mr-auto">${title}</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="toast-body">
${content}
</div>
</div>
`;
$('.toast').toast('show');
}
function appendTd(tr, content) {
const td = document.createElement('td');
td.appendChild(document.createTextNode(content));
td.classList.add('white');
tr.appendChild(td);
return td
}
function appendButton(tr, content, onclick) {
const tdRemove = document.createElement('td');
const removeButton = document.createElement('button');
removeButton.type = 'submit';
removeButton.classList.add('btn', 'btn-primary', 'btn-fill', 'mx-auto');
removeButton.appendChild(document.createTextNode(content));
removeButton.onclick = onclick;
tdRemove.appendChild(removeButton);
tr.appendChild(tdRemove);
}
</script> </script>
{{end}} {{end}}

View File

@ -97,6 +97,8 @@
<div class="row"> <div class="row">
<div class="collapse" id="advanced" style="width: 100%"> <div class="collapse" id="advanced" style="width: 100%">
<div class="container-fluid">
<div class="row">
<div class="col-md-6 pr-1"> <div class="col-md-6 pr-1">
<div class="form-group"> <div class="form-group">
<label class="black">Welcome Message <label class="black">Welcome Message
@ -105,6 +107,16 @@
<textarea type="text" class="form-control" placeholder="If not provided, your server's default welcome message will be used" id="welcome_message"></textarea> <textarea type="text" class="form-control" placeholder="If not provided, your server's default welcome message will be used" id="welcome_message"></textarea>
</div> </div>
</div> </div>
<div class="col-md-6 pr-1">
<div class="form-group">
<label class="black" for="mentions">Mention On Open</label>
<select class="selectpicker form-control" id="mentions" multiple data-live-search="true" data-dropup-auto="false" data-size="5" data-display="static">
</select>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
@ -168,7 +180,8 @@
colour: parseInt(`0x${document.getElementById('colour').value.slice(1)}`), colour: parseInt(`0x${document.getElementById('colour').value.slice(1)}`),
channel_id: document.getElementById('channel-container').options[document.getElementById('channel-container').selectedIndex].value, channel_id: document.getElementById('channel-container').options[document.getElementById('channel-container').selectedIndex].value,
category_id: document.getElementById('category-container').options[document.getElementById('category-container').selectedIndex].value, category_id: document.getElementById('category-container').options[document.getElementById('category-container').selectedIndex].value,
welcome_message: welcomeMessage === '' ? null : welcomeMessage welcome_message: welcomeMessage === '' ? null : welcomeMessage,
mentions: $('.selectpicker').val()
}; };
const res = await axios.put('/api/{{.guildId}}/panels', data); const res = await axios.put('/api/{{.guildId}}/panels', data);
@ -256,13 +269,42 @@
return res.data.length; return res.data.length;
} }
async function fillMentions() {
const select = document.getElementById('mentions');
// ticket opener
const ticketOpener = document.createElement('option');
ticketOpener.value = 'user';
ticketOpener.appendChild(document.createTextNode('Ticket Opener'));
select.appendChild(ticketOpener);
// roles
const res = await axios.get('/api/{{.guildId}}/roles');
if (res.status !== 200 || !res.data.success) {
showToast("Error", res.data);
return;
}
if (res.data.roles !== null) {
for (const role of res.data.roles) {
const option = document.createElement('option');
option.value = role.id;
option.appendChild(document.createTextNode(role.name));
select.appendChild(option);
}
}
$('.selectpicker').selectpicker('refresh');
}
async function loadData() { async function loadData() {
const channels = await getChannels(); const channels = await getChannels();
const panelCount = await fillPanels(channels); const panelCount = await fillPanels(channels);
await fillPanelQuota(panelCount); await fillPanelQuota(panelCount);
await fillChannels(channels); await fillChannels(channels);
await fillCategories(channels); await fillCategories(channels);
await fillMentions();
} }
withLoadingScreen(loadData); withLoadingScreen(loadData);

View File

@ -39,7 +39,7 @@
const members = ticket.members.map(member => `${member.username}#${member.discrim}`).join(', '); const members = ticket.members.map(member => `${member.username}#${member.discrim}`).join(', ');
appendTd(tr, members); appendTd(tr, members);
appendButton(tr, 'View', () => { console.log(ticket); location.href = '/manage/{{.guildId}}/tickets/view/' + ticket.ticketId }); appendButton(tr, 'View', () => { location.href = '/manage/{{.guildId}}/tickets/view/' + ticket.ticketId });
container.appendChild(tr); container.appendChild(tr);
} }