mirror of
https://github.com/danbulant/Nertivia-Client
synced 2026-06-19 22:31:25 +00:00
added admin tools
This commit is contained in:
parent
c43d9833f5
commit
b19b114d90
14 changed files with 621 additions and 66 deletions
|
|
@ -53,6 +53,7 @@
|
|||
import_contacts
|
||||
</div>
|
||||
<div
|
||||
data-name="Click Me"
|
||||
v-if="!user.survey_completed"
|
||||
class="item material-icons"
|
||||
@click="openSurvey"
|
||||
|
|
@ -60,6 +61,15 @@
|
|||
>
|
||||
error
|
||||
</div>
|
||||
<div
|
||||
v-if="isAdmin"
|
||||
class="item material-icons"
|
||||
:class="{ selected: currentTab == 4 }"
|
||||
@click="switchTab(4)"
|
||||
@mouseenter="localToolTipEvent('Admin Panel', $event)"
|
||||
>
|
||||
security
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -128,37 +138,8 @@ export default {
|
|||
this.$store.dispatch("servers/setSelectedServerID", serverID);
|
||||
this.$store.dispatch("openChannel", channel);
|
||||
},
|
||||
switchChannel(isServer) {
|
||||
const serverChannelID = this.$store.state.channelModule.serverChannelID;
|
||||
const DMChannelID = this.$store.state.channelModule.DMChannelID;
|
||||
|
||||
if (isServer) {
|
||||
this.$store.dispatch("selectedChannelID", serverChannelID);
|
||||
const channel = this.$store.state.channelModule.channels[
|
||||
serverChannelID
|
||||
];
|
||||
this.$store.dispatch("setChannelName", channel ? channel.name : "");
|
||||
this.dismissNotification(serverChannelID);
|
||||
} else {
|
||||
const channel = this.$store.state.channelModule.channels[DMChannelID];
|
||||
this.$store.dispatch(
|
||||
"setChannelName",
|
||||
channel ? channel.recipients[0].username : ""
|
||||
);
|
||||
this.$store.dispatch("selectedChannelID", DMChannelID);
|
||||
this.dismissNotification(DMChannelID);
|
||||
}
|
||||
},
|
||||
switchTab(index) {
|
||||
localStorage.setItem("currentTab", index);
|
||||
this.$store.dispatch("setCurrentTab", index);
|
||||
if (index == 1) {
|
||||
//1: direct message tab.
|
||||
this.switchChannel(false);
|
||||
} else if (index === 2) {
|
||||
//2: server tab
|
||||
this.switchChannel(true);
|
||||
}
|
||||
bus.$emit('tab:switch', index)
|
||||
},
|
||||
openSettings() {
|
||||
this.$store.dispatch("setPopoutVisibility", {
|
||||
|
|
@ -177,7 +158,6 @@ export default {
|
|||
this.toolTipLeftPosition = rect.left - tooltipWidth / 2 + 25;
|
||||
});
|
||||
},
|
||||
|
||||
mouseLeaveEvent() {
|
||||
this.toolTipShown = false;
|
||||
this.toolTipServerID = null;
|
||||
|
|
@ -206,32 +186,12 @@ export default {
|
|||
user() {
|
||||
return this.$store.getters.user;
|
||||
},
|
||||
isAdmin() {
|
||||
return this.user.admin === 3 || this.user.admin === 4;
|
||||
},
|
||||
currentTab() {
|
||||
return this.$store.getters.currentTab;
|
||||
},
|
||||
servers() {
|
||||
return this.$store.getters["servers/servers"];
|
||||
},
|
||||
serversArr: {
|
||||
get() {
|
||||
const data = this.servers;
|
||||
return Object.keys(data)
|
||||
.map(key => {
|
||||
return data[key];
|
||||
})
|
||||
.reverse();
|
||||
},
|
||||
set(value) {
|
||||
const reversedServers = value.reverse();
|
||||
// convert array to json
|
||||
const json = {};
|
||||
for (let index = 0; index < reversedServers.length; index++) {
|
||||
const element = reversedServers[index];
|
||||
json[element.server_id] = element;
|
||||
}
|
||||
this.$store.dispatch("servers/setServers", json);
|
||||
}
|
||||
},
|
||||
selectedServerID() {
|
||||
return this.$store.getters["servers/selectedServerID"];
|
||||
},
|
||||
|
|
|
|||
|
|
@ -121,15 +121,7 @@ export default {
|
|||
}
|
||||
},
|
||||
switchTab(index) {
|
||||
localStorage.setItem("currentTab", index);
|
||||
this.$store.dispatch("setCurrentTab", index);
|
||||
if (index == 1) {
|
||||
//1: direct message tab.
|
||||
this.switchChannel(false);
|
||||
} else if (index === 2) {
|
||||
//2: server tab
|
||||
this.switchChannel(true);
|
||||
}
|
||||
bus.$emit('tab:switch', index)
|
||||
},
|
||||
openSettings() {
|
||||
this.$store.dispatch("setPopoutVisibility", {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
<server-member-context key="smc" v-if="popouts.serverMemberContext.uniqueID"/>
|
||||
<server-context key="sc" v-if="popouts.allPopout.type === 'SERVER' && popouts.allPopout.show"/>
|
||||
<add-friend key="af" v-if="popouts.allPopout.type === 'ADD_FRIEND' && popouts.allPopout.show"/>
|
||||
<admin-css-editor key="ace" v-if="popouts.allPopout.type === 'ADMIN_CSS_EDITOR'" />
|
||||
</transition-group>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
const ServerInvitePopout = () => import('./Popouts/ServerInvitePopout.vue');
|
||||
const ServerSettings = () => import('./Popouts/ServerSettingsPanels/ServerSettings.vue');
|
||||
const GenericPopout = () => import('./Popouts/GenericPopout');
|
||||
const AdminCssEditor = () => import('./Popouts/AdminEditorPopout');
|
||||
|
||||
|
||||
|
||||
|
|
@ -61,7 +63,8 @@ export default {
|
|||
messageContextMenu,
|
||||
ServerMemberContext,
|
||||
ServerContext,
|
||||
AddFriend
|
||||
AddFriend,
|
||||
AdminCssEditor
|
||||
|
||||
},
|
||||
data() {
|
||||
|
|
|
|||
183
src/components/app/Popouts/Popouts/AdminEditorPopout.vue
Normal file
183
src/components/app/Popouts/Popouts/AdminEditorPopout.vue
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<div class="dark-background" @mousedown="backgroundClick">
|
||||
<div class="inner">
|
||||
<div class="details" v-if="details && details.updatedCss">
|
||||
<div class="item">Updated</div>
|
||||
<div class="item">Current</div>
|
||||
</div>
|
||||
<codemirror
|
||||
v-if="details"
|
||||
class="editor"
|
||||
:merge="!!details.updatedCss"
|
||||
:code="details.css"
|
||||
:options="cmOptions"
|
||||
/>
|
||||
<div class="button" @click="applyButton">Apply</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import "codemirror/mode/css/css.js";
|
||||
|
||||
import 'codemirror/addon/merge/merge.js'
|
||||
// merge css
|
||||
import 'codemirror/addon/merge/merge.css'
|
||||
// google DiffMatchPatch
|
||||
import DiffMatchPatch from 'diff-match-patch'
|
||||
|
||||
|
||||
window.diff_match_patch = DiffMatchPatch
|
||||
window.DIFF_DELETE = -1
|
||||
window.DIFF_INSERT = 1
|
||||
window.DIFF_EQUAL = 0
|
||||
|
||||
// theme css
|
||||
import "codemirror/theme/base16-dark.css";
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import { codemirror } from "vue-codemirror";
|
||||
|
||||
import config from "@/config.js";
|
||||
import { bus } from "@/main";
|
||||
import Spinner from "@/components/Spinner.vue";
|
||||
import AdminService from "@/services/adminService";
|
||||
|
||||
export default {
|
||||
components: { codemirror },
|
||||
data() {
|
||||
return {
|
||||
details: null,
|
||||
code: null,
|
||||
cmOptions: {
|
||||
value: ``,
|
||||
origLeft: null,
|
||||
orig: ``,
|
||||
// codemirror options
|
||||
tabSize: 2,
|
||||
mode: "text/css",
|
||||
theme: "base16-dark",
|
||||
lineNumbers: true,
|
||||
line: true,
|
||||
readOnly: true,
|
||||
collapseIdentical: false,
|
||||
highlightDifferences: true
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
closeMenu() {
|
||||
this.$store.dispatch("setAllPopout", {
|
||||
show: false,
|
||||
type: null,
|
||||
id: null
|
||||
});
|
||||
},
|
||||
backgroundClick(e) {
|
||||
if (e.target.classList.contains("dark-background")) {
|
||||
this.closeMenu();
|
||||
}
|
||||
},
|
||||
applyButton() {
|
||||
const css = this.details.updatedCss || this.details.css;
|
||||
const styleEl = document.createElement("style");
|
||||
styleEl.id = "theme";
|
||||
styleEl.innerHTML = css;
|
||||
|
||||
const currentStyle = document.getElementById("theme");
|
||||
if (currentStyle) {
|
||||
currentStyle.outerHTML = styleEl.outerHTML;
|
||||
} else {
|
||||
document.head.innerHTML += styleEl.outerHTML;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const { ok, result, error } = await AdminService.fetchTheme(
|
||||
this.popoutDetails.id
|
||||
);
|
||||
if (ok) {
|
||||
this.cmOptions.orig = result.data.css;
|
||||
this.cmOptions.value = result.data.updatedCss;
|
||||
this.details = result.data;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
popoutDetails() {
|
||||
return this.$store.getters.popouts.allPopout;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dark-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, 0.541);
|
||||
z-index: 111111;
|
||||
display: flex;
|
||||
}
|
||||
.inner {
|
||||
margin: auto;
|
||||
max-height: 460px;
|
||||
height: 100%;
|
||||
width: 800px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
box-shadow: 0px 0px 20px 5px #151515bd;
|
||||
background: linear-gradient(#0b4155, #01677e);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.button {
|
||||
padding: 10px;
|
||||
transition: 0.3s;
|
||||
display: inline;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
.details {
|
||||
display: flex;
|
||||
.item {
|
||||
flex: 1;
|
||||
padding: 2px;
|
||||
&:nth-child(2) {
|
||||
margin-left: 45px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.CodeMirror {
|
||||
height: 100% !important;
|
||||
}
|
||||
.CodeMirror-merge-pane {
|
||||
height: 100%;
|
||||
}
|
||||
.CodeMirror-merge-r-chunk {
|
||||
background: rgb(36, 36, 36);
|
||||
}
|
||||
.CodeMirror-merge-r-chunk-end {
|
||||
border-bottom: 1px solid rgb(66, 66, 66);
|
||||
}
|
||||
.CodeMirror-merge-r-chunk-start {
|
||||
border-top: 1px solid rgb(66, 66, 66);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
/>
|
||||
<div class="managing" v-else>
|
||||
<div class="bar">
|
||||
<div class="button">
|
||||
<div class="button" @click="exploreButton">
|
||||
<div class="material-icons">explore</div>
|
||||
Explore
|
||||
</div>
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {bus} from '@/main'
|
||||
import ThemeTemplate from "./MyThemeTemplate";
|
||||
import Editor from "./themesEditor";
|
||||
import MakePublic from "./MyThemesMakePublic";
|
||||
|
|
@ -155,6 +156,13 @@ export default {
|
|||
},
|
||||
makePublicButton() {
|
||||
this.showMakePublic = true;
|
||||
},
|
||||
exploreButton() {
|
||||
this.$store.dispatch("setPopoutVisibility", {
|
||||
name: "settings",
|
||||
visibility: false
|
||||
});
|
||||
bus.$emit('tab:switch', 0)
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
|
|
|
|||
48
src/components/app/Tabs/AdminPanel.vue
Normal file
48
src/components/app/Tabs/AdminPanel.vue
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<div class="admin-panel-tab">
|
||||
<users-panel />
|
||||
<online-users-panel />
|
||||
<themes-panel />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { bus } from "@/main";
|
||||
|
||||
import UsersPanel from './AdminPanel/UsersPanel';
|
||||
import OnlineUsersPanel from './AdminPanel/OnlineUsersPanel';
|
||||
import ThemesPanel from './AdminPanel/ThemesPanel';
|
||||
|
||||
export default {
|
||||
components: { UsersPanel, OnlineUsersPanel, ThemesPanel },
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted() {
|
||||
if (!this.isAdmin) {
|
||||
bus.$emit('tab:switch', 0)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.user;
|
||||
},
|
||||
isAdmin() {
|
||||
return this.user.admin === 3 || this.user.admin === 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.admin-panel-tab {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: white;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
49
src/components/app/Tabs/AdminPanel/OnlineUsersPanel.vue
Normal file
49
src/components/app/Tabs/AdminPanel/OnlineUsersPanel.vue
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<div class="users-panel">
|
||||
<div class="title">Online Users</div>
|
||||
<div class="list">
|
||||
<user-template v-for="user in users" :key="user.uniqueID" :user="user" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import UserTemplate from './UserTemplate';
|
||||
import adminService from '@/services/adminService';
|
||||
|
||||
export default {
|
||||
components: {UserTemplate},
|
||||
data() {
|
||||
return {
|
||||
users: null,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const {ok, error, result} = await adminService.fetchOnlineUsers();
|
||||
if (ok) {
|
||||
this.users = result.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.users-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
height: 100%;
|
||||
width: 300px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.title {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
.list {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
110
src/components/app/Tabs/AdminPanel/ThemeTemplate.vue
Normal file
110
src/components/app/Tabs/AdminPanel/ThemeTemplate.vue
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<template>
|
||||
<div class="theme" v-if="!approved">
|
||||
<div class="screenshot" @click="screenshotClicked" :style="{backgroundImage: `url(${screenshot})`}"></div>
|
||||
<div class="details">
|
||||
<div class="name"><strong>Name:</strong> {{theme.theme.name}}</div>
|
||||
<strong>Description:</strong>
|
||||
<div class="description">{{theme.description}}</div>
|
||||
<div class="buttons">
|
||||
<div class="button" v-if="theme.approved" @click="openEditor">Compare</div>
|
||||
<div class="button" v-else @click="openEditor">View code</div>
|
||||
<div class="button" v-if="theme.approved" @click="approveButton">Update</div>
|
||||
<div class="button" v-else @click="approveButton">Approve</div>
|
||||
<div class="button">Decline</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import config from "@/config.js";
|
||||
import adminService from '@/services/adminService';
|
||||
|
||||
export default {
|
||||
props: ['theme'],
|
||||
data() {
|
||||
return {
|
||||
approved: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
screenshot() {
|
||||
return config.domain + "/media/" + this.theme.screenshot;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
screenshotClicked() {
|
||||
this.$store.dispatch(
|
||||
"setImagePreviewURL",
|
||||
this.screenshot
|
||||
);
|
||||
},
|
||||
openEditor() {
|
||||
this.$store.dispatch("setAllPopout", {
|
||||
show: true,
|
||||
type: "ADMIN_CSS_EDITOR",
|
||||
id: this.theme.id
|
||||
});
|
||||
},
|
||||
async approveButton() {
|
||||
const {ok, result, error} = await adminService.approveTheme(this.theme.id);
|
||||
if (ok) {
|
||||
this.approved = true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.theme {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
|
||||
}
|
||||
.screenshot {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
flex-shrink: 0;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.name {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.description {
|
||||
max-height: 100px;
|
||||
overflow: auto;
|
||||
|
||||
}
|
||||
.buttons {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
|
||||
flex-shrink: 0;
|
||||
.button {
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
margin: auto;
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: 0.2s;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
49
src/components/app/Tabs/AdminPanel/ThemesPanel.vue
Normal file
49
src/components/app/Tabs/AdminPanel/ThemesPanel.vue
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<div class="panel">
|
||||
<div class="title">Unapproved Themes</div>
|
||||
<div class="list">
|
||||
<theme-template v-for="theme in themes" :key="theme.id" :theme="theme" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import ThemeTemplate from './ThemeTemplate';
|
||||
import adminService from '@/services/adminService';
|
||||
|
||||
export default {
|
||||
components: {ThemeTemplate},
|
||||
data() {
|
||||
return {
|
||||
themes: null,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const {ok, error, result} = await adminService.fetchWaitingThemes();
|
||||
if (ok) {
|
||||
this.themes = result.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
height: 100%;
|
||||
width: 300px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.title {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
.list {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
73
src/components/app/Tabs/AdminPanel/UserTemplate.vue
Normal file
73
src/components/app/Tabs/AdminPanel/UserTemplate.vue
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div class="user" @click="openUserInformation">
|
||||
<div class="profile-picture" :style="{backgroundImage: `url(${avatar})`}"></div>
|
||||
<div class="details">
|
||||
<div class="username-tag">{{user.username}}<span class="tag">@{{user.tag}}</span></div>
|
||||
<div class="date" v-if="!presence">{{date}}</div>
|
||||
<div class="presence" v-if="presence">{{presence}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import friendlyDate from '@/utils/date';
|
||||
import config from "@/config.js";
|
||||
import statuses from '@/utils/statuses';
|
||||
export default {
|
||||
props: ['user'],
|
||||
computed: {
|
||||
date() {
|
||||
return friendlyDate(this.user.created)
|
||||
},
|
||||
avatar() {
|
||||
return config.domain + "/avatars/" + this.user.avatar;
|
||||
},
|
||||
presence() {
|
||||
if (this.user.status === undefined) return null;
|
||||
return statuses[this.user.status].name
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openUserInformation() {
|
||||
this.$store.dispatch("setUserInformationPopout", this.user.uniqueID);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user {
|
||||
display: flex;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
.profile-picture {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.date {
|
||||
font-size: 14px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.tag {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.presence {
|
||||
opacity: 0.6;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
49
src/components/app/Tabs/AdminPanel/UsersPanel.vue
Normal file
49
src/components/app/Tabs/AdminPanel/UsersPanel.vue
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<div class="users-panel">
|
||||
<div class="title">Recently Created Accounts</div>
|
||||
<div class="list">
|
||||
<user-template v-for="user in users" :key="user.uniqueID" :user="user" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import UserTemplate from './UserTemplate';
|
||||
import adminService from '@/services/adminService';
|
||||
|
||||
export default {
|
||||
components: {UserTemplate},
|
||||
data() {
|
||||
return {
|
||||
users: null,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const {ok, error, result} = await adminService.fetchRecentCreatedUsers();
|
||||
if (ok) {
|
||||
this.users = result.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.users-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
height: 100%;
|
||||
width: 300px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.title {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
.list {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
20
src/services/adminService.js
Normal file
20
src/services/adminService.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import {instance, wrapper} from './Api';
|
||||
|
||||
export default {
|
||||
fetchRecentCreatedUsers () {
|
||||
return wrapper(instance().get(`admin/users/recent`))
|
||||
},
|
||||
fetchOnlineUsers() {
|
||||
return wrapper(instance().get(`admin/users/online`))
|
||||
},
|
||||
fetchWaitingThemes() {
|
||||
return wrapper(instance().get(`admin/themes/waiting`))
|
||||
},
|
||||
fetchTheme(id) {
|
||||
return wrapper(instance().get(`admin/themes/${id}`));
|
||||
},
|
||||
approveTheme(id) {
|
||||
return wrapper(instance().patch(`admin/themes/${id}/approve`));
|
||||
},
|
||||
|
||||
}
|
||||
|
|
@ -49,6 +49,7 @@ const state = {
|
|||
serverID: null,
|
||||
uniqueID: null,
|
||||
creatorUniqueID: null,
|
||||
id: null,
|
||||
x: null,
|
||||
y: null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
<direct-message v-if="currentTab == 1" />
|
||||
<servers v-if="currentTab == 2" />
|
||||
<explore v-if="currentTab == 0" />
|
||||
<admin-panel v-if="currentTab == 4" />
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
|
@ -57,6 +58,11 @@ const Explore = () => ({
|
|||
loading: Spinner,
|
||||
delay: 0
|
||||
});
|
||||
const AdminPanel = () => ({
|
||||
component: import("./../components/app/Tabs/AdminPanel.vue"),
|
||||
loading: Spinner,
|
||||
delay: 0
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "app",
|
||||
|
|
@ -66,6 +72,7 @@ export default {
|
|||
ConnectingScreen,
|
||||
Popouts,
|
||||
News,
|
||||
AdminPanel,
|
||||
ElectronFrameButtons,
|
||||
Explore,
|
||||
MainNav
|
||||
|
|
@ -140,7 +147,7 @@ export default {
|
|||
css = exploreThemes.result.data.css;
|
||||
id = exploreThemes.result.data.id;
|
||||
}
|
||||
if (privateThemes) {
|
||||
if (privateThemes.ok) {
|
||||
css = privateThemes.result.data.css;
|
||||
id = privateThemes.result.data.id;
|
||||
}
|
||||
|
|
@ -172,6 +179,9 @@ export default {
|
|||
bus.$on("title:change", title => {
|
||||
this.title = title;
|
||||
});
|
||||
bus.$on("tab:switch", tab => {
|
||||
this.switchTab(tab);
|
||||
});
|
||||
this.setTheme();
|
||||
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue