Overhaul design

This commit is contained in:
rxdn 2024-08-30 00:09:42 +01:00
parent 3fd7539d7f
commit 5eb26c3b53
20 changed files with 627 additions and 407 deletions

View File

@ -0,0 +1,29 @@
package api
import (
"github.com/TicketsBot/GoPanel/botcontext"
"github.com/TicketsBot/GoPanel/utils"
"github.com/gin-gonic/gin"
)
func GuildHandler(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
botContext, err := botcontext.ContextForGuild(guildId)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
guild, err := botContext.GetGuild(ctx, guildId)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
ctx.JSON(200, gin.H{
"id": guild.Id,
"name": guild.Name,
"icon": guild.Icon,
})
}

View File

@ -94,6 +94,7 @@ func StartServer(sm *livechat.SocketManager) {
guildAuthApiSupport := apiGroup.Group("/:id", middleware.AuthenticateGuild(permission.Support))
guildApiNoAuth := apiGroup.Group("/:id", middleware.ParseGuildId)
{
guildAuthApiSupport.GET("/guild", api.GuildHandler)
guildAuthApiSupport.GET("/channels", api.ChannelsHandler)
guildAuthApiSupport.GET("/premium", api.PremiumHandler)
guildAuthApiSupport.GET("/user/:user", api.UserHandler)

View File

@ -1,4 +1,10 @@
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
:root {
--primary: #995DF3;
--primary-gradient: linear-gradient(71.3deg, #873ef5 0%, #995DF3 100%);
--blue: #3472f7;
}
html, body {
position: relative;
@ -7,7 +13,7 @@ html, body {
}
body, h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6, p, .navbar, .brand, .btn-simple, .alert, a, .td-name, td, button.close {
font-family: 'Noto Sans', sans-serif !important;
font-family: 'Poppins', sans-serif !important;
font-weight: 400 !important;
}

View File

@ -3,7 +3,7 @@
</div>
<script>
export let colour = '#3472f7';
export let colour = '';
</script>
<style>
@ -11,7 +11,7 @@
display: flex;
align-items: center;
background-color: var(--badge-background-color, #3472f7);
background: var(--badge-background-color, var(--primary));
border-radius: 2px;
font-size: 14px;
padding: 0 4px;

View File

@ -28,9 +28,10 @@
text-align: center;
color: white;
background-color: #3472f7;
border-color: #3472f7;
border-width: 2px;
background: var(--primary-gradient);
border: none;
/*border-color: var(--primary);*/
/*border-width: 2px;*/
border-radius: .25rem;
margin: 0;
@ -39,14 +40,14 @@
box-shadow: 0 4px 4px rgb(0 0 0 / 25%);
}
button:active, button:hover:enabled {
background-color: #0062cc;
border-color: #0062cc;
}
/*button:active, button:hover:enabled {*/
/* background-color: #0062cc;*/
/* border-color: #0062cc;*/
/*}*/
button:disabled {
background-color: #6c757d;
border-color: #6c757d;
background: #6c757d;
border: #6c757d;
cursor: default;
}
@ -60,12 +61,12 @@
}
.danger {
background-color: #dc3545 !important;
background: #dc3545 !important;
border-color: #dc3545 !important;
}
.danger:hover:enabled, .danger:active {
background-color: #c32232 !important;
background: #c32232 !important;
border-color: #c32232 !important;
}

View File

@ -0,0 +1,136 @@
<section class="sidebar">
<header>
<img src="{getIconUrl()}" class="guild-icon" alt="Guild icon" width="50" height="50"/>
{guild.name}
</header>
<nav>
<ul class="nav-list">
<ManageSidebarLink {currentRoute} title="← Back to servers" href="/" />
{#if isAdmin}
<ManageSidebarLink {currentRoute} title="Settings" icon="fa-cogs" href="/manage/{guildId}/settings" />
{/if}
<ManageSidebarLink {currentRoute} title="Transcripts" icon="fa-copy" href="/manage/{guildId}/transcripts" />
{#if isAdmin}
<ManageSidebarLink {currentRoute} routePrefix="/manage/{guildId}/panels" title="Ticket Panels" icon="fa-mouse-pointer" href="/manage/{guildId}/panels" />
<ManageSidebarLink {currentRoute} title="Forms" icon="fa-poll-h" href="/manage/{guildId}/forms" />
<ManageSidebarLink {currentRoute} title="Staff Teams" icon="fa-users" href="/manage/{guildId}/teams" />
<ManageSidebarLink {currentRoute} title="Integrations" icon="fa-robot" href="/manage/{guildId}/integrations" />
{/if}
<ManageSidebarLink {currentRoute} title="Tickets" icon="fa-ticket-alt" href="/manage/{guildId}/tickets" />
<ManageSidebarLink {currentRoute} title="Blacklist" icon="fa-ban" href="/manage/{guildId}/blacklist" />
<ManageSidebarLink {currentRoute} title="Tags" icon="fa-tags" href="/manage/{guildId}/tags" />
</ul>
</nav>
<nav class="bottom">
<hr/>
<ul class="nav-list">
<ManageSidebarLink {currentRoute} title="Documentation" icon="fa-book" href="https://docs.ticketsbot.net" newWindow />
<ManageSidebarLink {currentRoute} title="Logout" icon="fa-sign-out-alt" href="/logout" />
</ul>
</nav>
</section>
<style>
.sidebar {
display: flex;
flex-direction: column;
align-self: flex-start;
background-color: #272727;
padding: 15px;
width: 275px;
border-radius: 6px;
user-select: none;
}
header {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
font-weight: bold;
padding: 6px 10px;
border-radius: 4px;
background: linear-gradient(33.3deg, #873ef5 0%, #995DF3 100%);
box-shadow: 0 6px 6px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
}
.guild-icon {
width: 48px;
height: 48px;
border-radius: 50%;
}
nav > ul {
list-style-type: none;
padding: 0;
margin: 0;
}
nav hr {
width: 40%;
padding-left: 20px;
}
</style>
<script>
import {onMount} from "svelte";
import axios from "axios";
import {API_URL} from "../js/constants";
import {notifyError, withLoadingScreen} from "../js/util";
import ManageSidebarLink from "./ManageSidebarLink.svelte";
import SubNavigation from "./SubNavigation.svelte";
import SubNavigationLink from "./SubNavigationLink.svelte";
export let currentRoute;
export let permissionLevel;
$: isAdmin = permissionLevel >= 2;
let guildId = currentRoute.namedParams.id;
let guild = {};
async function loadGuild() {
const res = await axios.get(`${API_URL}/api/${guildId}/guild`);
if (res.status !== 200) {
notifyError(res.data.error);
return;
}
guild = res.data;
}
function isAnimated() {
if (guild.icon === undefined || guild.icon === "") {
return false;
} else {
return guild.icon.startsWith('a_')
}
}
function getIconUrl() {
if (!guild.icon) {
return `https://cdn.discordapp.com/embed/avatars/${Number((BigInt(guildId) >> BigInt(22)) % BigInt(6))}.png`
}
if (isAnimated()) {
return `https:\/\/cdn.discordapp.com/icons/${guild.id}/${guild.icon}.gif?size=256`
} else {
return `https:\/\/cdn.discordapp.com/icons/${guild.id}/${guild.icon}.webp?size=256`
}
}
onMount(async () => {
await withLoadingScreen(async () => {
await loadGuild();
})
});
</script>

View File

@ -0,0 +1,52 @@
<li>
<div style="width: 100%">
<a href="{href}" class:active target="{newWindow === true ? '_blank' : '_self'}">
{#if icon}
<i class="fas {icon}"/>
{/if}
<span>{title}</span>
</a>
{#if active}
<slot/>
{/if}
</div>
</li>
<style>
a.active {
background: linear-gradient(71.3deg, #873ef5 0%, #995DF3 100%);
box-shadow: 0 6px 6px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
}
a, a:link, a:hover, a:visited, a:active {
display: block;
color: inherit;
text-decoration: none;
font-size: 16px;
padding: 5px 10px 5px 20px;
border-radius: 4px;
}
i {
width: 20px;
text-align: center;
}
</style>
<script>
export let currentRoute;
export let title;
export let icon;
export let href = "#";
export let routePrefix;
export let newWindow;
let active = href !== "/" && ((routePrefix || href)?.toLowerCase() === currentRoute.name.toLowerCase() ||
currentRoute.name.toLowerCase().startsWith((routePrefix || href).toLowerCase()));
$: active;
</script>

View File

@ -19,12 +19,7 @@
<NavElement icon="fas fa-mouse-pointer" link="/manage/{guildId}/panels" on:click={closeDropdown}>Ticket Panels</NavElement>
<NavElement icon="fas fa-poll-h" link="/manage/{guildId}/forms" on:click={closeDropdown}>Forms</NavElement>
<NavElement icon="fas fa-users" link="/manage/{guildId}/teams" on:click={closeDropdown}>Staff Teams</NavElement>
<NavElement icon="fas fa-robot" link="/manage/{guildId}/integrations" on:click={closeDropdown}>
<div style="display: flex; gap:4px">
Integrations
<Badge>New!</Badge>
</div>
</NavElement>
<NavElement icon="fas fa-robot" link="/manage/{guildId}/integrations" on:click={closeDropdown}>Integrations</NavElement>
{/if}
<NavElement icon="fas fa-ticket-alt" link="/manage/{guildId}/tickets" on:click={closeDropdown}>Tickets</NavElement>
@ -63,7 +58,7 @@
<style>
.navbar {
display: flex;
display: none;
justify-content: center;
width: 100%;
background-color: #272727;
@ -89,6 +84,10 @@
}
@media only screen and (max-width: 1154px) {
.navbar {
display: flex;
}
.nav-section {
display: none;
}

View File

@ -0,0 +1,19 @@
<div class="root">
<ul>
<slot/>
</ul>
</div>
<style>
.root {
display: flex;
flex-direction: row;
}
ul {
width: 100%;
list-style-type: none;
margin: 2px 0 0 55px;
padding: 0;
}
</style>

View File

@ -0,0 +1,53 @@
<li>
<div class="wrapper">
<a href="{href}" class:active>
{#if icon}
<i class="fas {icon}"/>
{/if}
<span>
<slot/>
</span>
</a>
</div>
</li>
<style>
li {
/*padding: 2px 0;*/
}
.wrapper {
width: 100%;
}
a.active {
font-weight: 600 !important;
}
a:not(.active) {
opacity: 0.75;
}
a, a:link, a:hover, a:visited, a:active {
color: inherit;
text-decoration: none;
}
i {
width: 20px;
text-align: center;
}
</style>
<script>
export let currentRoute;
export let icon;
export let href = "#";
export let routePrefix;
let active = href !== "/" && ((routePrefix || href).toLowerCase() === currentRoute.name.toLowerCase() ||
currentRoute.name.toLowerCase().startsWith((routePrefix || href).toLowerCase()));
$: active;
</script>

View File

@ -5,6 +5,7 @@
<div class="super-container" class:dropdown={$dropdown}>
<LoadingScreen/>
<div class="content-container" class:hide={$loadingScreen}>
<ManageSidebar {currentRoute} {permissionLevel} />
<Route {currentRoute} {params}/>
</div>
<NotifyModal/>
@ -23,8 +24,13 @@
height: 100%;
}
.super-container {
padding: 30px;
}
.content-container {
display: flex;
gap: 30px;
width: 100%;
height: 100%;
}
@ -48,6 +54,7 @@
import {setDefaultHeaders} from '../includes/Auth.svelte'
import {permissionLevelCache} from '../js/stores';
import {get} from 'svelte/store';
import ManageSidebar from "../includes/ManageSidebar.svelte";
export let currentRoute;
export let params = {};

View File

@ -55,7 +55,6 @@
{/if}
{#if data}
<div class="parent">
<div class="content">
<div class="main-col">
<Card footer={false}>
@ -87,7 +86,8 @@
{/if}
<td>
<Button type="button" danger icon="fas fa-trash-can" on:click={() => removeRoleBlacklist(roleId, role)}>
<Button type="button" danger icon="fas fa-trash-can"
on:click={() => removeRoleBlacklist(roleId, role)}>
Remove
</Button>
</td>
@ -113,7 +113,8 @@
{/if}
<td>
<Button type="button" danger icon="fas fa-trash-can" on:click={() => removeUserBlacklist(user)}>
<Button type="button" danger icon="fas fa-trash-can"
on:click={() => removeUserBlacklist(user)}>
Remove
</Button>
</td>
@ -135,7 +136,6 @@
</Card>
</div>
</div>
</div>
{/if}
<script>
@ -296,21 +296,10 @@
</script>
<style>
.parent {
display: flex;
justify-content: flex-start;
padding-left: 2%;
width: 100%;
height: 100%;
}
.content {
display: flex;
justify-content: space-between;
width: 60%;
width: 100%;
height: 100%;
margin-top: 30px;
padding-bottom: 4%;
}
.main-col {
@ -359,6 +348,7 @@
justify-content: center;
align-items: center;
gap: 2px;
margin-top: 20px;
}
.pagination-chevron {

View File

@ -1,4 +1,3 @@
<div class="parent">
<div class="content">
<Card footer footerRight>
<span slot="title">Forms</span>
@ -22,7 +21,9 @@
<div class="row form-name-edit-wrapper">
<Input col4 label="Form Title" placeholder="Form Title" bind:value={renamedTitle}/>
<div class="form-name-save-wrapper">
<Button icon="fas fa-floppy-disk" fullWidth={windowWidth <= 950} on:click={updateTitle}>Save</Button>
<Button icon="fas fa-floppy-disk" fullWidth={windowWidth <= 950} on:click={updateTitle}>
Save
</Button>
</div>
</div>
{:else}
@ -58,7 +59,8 @@
{/if}
{#if activeFormId !== null}
<div class="row" style="justify-content: center; align-items: center; gap: 10px; margin-top: 10px">
<div class="row"
style="justify-content: center; align-items: center; gap: 10px; margin-top: 10px">
<hr class="fill">
<div class="row add-input-container" class:add-input-disabled={formLength >= 5}>
<i class="fas fa-plus"></i>
@ -78,7 +80,6 @@
</div>
</Card>
</div>
</div>
<svelte:window bind:innerWidth={windowWidth}/>
@ -282,20 +283,10 @@
</script>
<style>
.parent {
display: flex;
justify-content: center;
width: 100%;
height: 100%;
}
.content {
display: flex;
justify-content: space-between;
width: 96%;
width: 100%;
height: 100%;
margin-top: 30px;
margin-bottom: 50px;
}
.body-wrapper {

View File

@ -19,8 +19,6 @@
flex-direction: row;
height: 100%;
width: 100%;
padding: 0 45px;
justify-content: space-between;
}
.main-col {
@ -28,7 +26,6 @@
flex-direction: column;
height: 100%;
width: 100%;
margin-top: 30px;
}
.right-col {

View File

@ -4,9 +4,7 @@
<TagEditor {isPremium} bind:data={editData} on:cancel={cancelEdit} on:confirm={editTag}/>
{/if}
<div class="parent">
<div class="content">
<div class="main-col">
<Card footer footerRight>
<span slot="title">Tags</span>
<div slot="body" class="body-wrapper">
@ -35,8 +33,6 @@
</div>
</Card>
</div>
</div>
</div>
<script>
import Card from "../components/Card.svelte";
@ -45,7 +41,6 @@
import axios from "axios";
import {API_URL} from "../js/constants";
import {setDefaultHeaders} from '../includes/Auth.svelte'
import {fade} from "svelte/transition";
import TagEditor from "../components/manage/TagEditor.svelte";
export let currentRoute;
@ -192,27 +187,10 @@
</script>
<style>
.parent {
display: flex;
justify-content: center;
width: 100%;
height: 100%;
}
.content {
display: flex;
justify-content: space-between;
width: 96%;
width: 100%;
height: 100%;
margin-top: 30px;
}
.main-col {
display: flex;
flex-direction: column;
width: 64%;
height: 100%;
padding-bottom: 4%;
}
.body-wrapper {

View File

@ -1,4 +1,3 @@
<div class="parent">
<div class="content">
<Card footer={false}>
<span slot="title">Support Teams</span>
@ -76,7 +75,6 @@
</div>
</Card>
</div>
</div>
<script>
import Card from "../components/Card.svelte";
@ -226,19 +224,10 @@
</script>
<style>
.parent {
display: flex;
justify-content: center;
width: 100%;
height: 100%;
}
.content {
display: flex;
justify-content: space-between;
width: 96%;
width: 100%;
height: 100%;
margin-top: 30px;
}
.body-wrapper {

View File

@ -1,4 +1,3 @@
<div class="parent">
<div class="content">
<Card footer={false}>
<span slot="title">Open Tickets</span>
@ -34,11 +33,10 @@
</div>
</Card>
</div>
</div>
<script>
import Card from "../components/Card.svelte";
import {notifyError, notifySuccess, withLoadingScreen} from '../js/util'
import {notifyError, withLoadingScreen} from '../js/util'
import axios from "axios";
import {API_URL} from "../js/constants";
import {setDefaultHeaders} from '../includes/Auth.svelte'
@ -67,20 +65,10 @@
</script>
<style>
.parent {
display: flex;
justify-content: center;
width: 100%;
height: 100%;
margin-top: 30px;
}
.content {
display: flex;
justify-content: space-between;
width: 96%;
width: 100%;
height: 100%;
margin-top: 30px;
}
.body-wrapper {

View File

@ -242,9 +242,8 @@
.col {
display: flex;
flex-direction: column;
width: 95%;
height: 100%;
margin-top: 30px;
width: 100%;
}
.main-col {

View File

@ -1,4 +1,3 @@
<div class="parent">
<div class="content">
<div class="container">
<div class="spread">
@ -55,10 +54,11 @@
</div>
<div class="pagination">
<i class="fas fa-chevron-left pagination-chevron" class:disabled-chevron={page === 1} on:click={previousPage}></i>
<i class="fas fa-chevron-left pagination-chevron" class:disabled-chevron={page === 1}
on:click={previousPage}></i>
<p>Page {page}</p>
<i class="fas fa-chevron-right pagination-chevron" class:disabled-chevron={!hasNextPage} on:click={nextPage}></i>
</div>
<i class="fas fa-chevron-right pagination-chevron" class:disabled-chevron={!hasNextPage}
on:click={nextPage}></i>
</div>
</div>
@ -161,21 +161,12 @@
</script>
<style>
.parent {
display: flex;
justify-content: center;
width: 100%;
height: 100%;
}
.content {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 96%;
width: 100%;
height: 100%;
margin-top: 30px;
padding-bottom: 5vh;
row-gap: 4vh;
}
@ -193,7 +184,7 @@
}
.integration {
flex: 0 0 23.5%;
flex: 0 0 32%;
}
.my-integrations {
@ -231,19 +222,13 @@
cursor: default !important;
}
@media only screen and (max-width: 1180px) {
.integration {
flex: 0 0 32%;
}
}
@media only screen and (max-width: 930px) {
@media only screen and (max-width: 1200px) {
.integration {
flex: 0 0 49%;
}
}
@media only screen and (max-width: 576px) {
@media only screen and (max-width: 850px) {
.integration {
flex: 0 0 100%;
}

View File

@ -207,7 +207,7 @@
flex-direction: row;
height: 100%;
width: 100%;
margin-top: 30px;
gap: 2%;
}
.col {
@ -219,7 +219,7 @@
.row {
display: flex;
width: 96%;
width: 100%;
margin-bottom: 2%;
}