Admin Panel: Sidebar, overview and settings
This commit is contained in:
Родитель
361b166d1f
Коммит
310ae9c9a8
|
@ -15,3 +15,4 @@ test-queries
|
|||
|
||||
packages/viewer/example/*.js
|
||||
packages/viewer/example/*.js.map
|
||||
packages/frontend/schema.graphql
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "Speckle Schema",
|
||||
"schemaPath": "schema.graphql",
|
||||
"extensions": {
|
||||
"endpoints": {
|
||||
"Default GraphQL Endpoint": {
|
||||
"url": "http://localhost:3000/graphql",
|
||||
"headers": {
|
||||
"user-agent": "JS GraphQL"
|
||||
},
|
||||
"introspect": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10474,6 +10474,11 @@
|
|||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true
|
||||
},
|
||||
"numeral": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz",
|
||||
"integrity": "sha1-StCAk21EPCVhrtnyGX7//iX05QY="
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
|
@ -13937,6 +13942,11 @@
|
|||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"tween": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/tween/-/tween-0.9.0.tgz",
|
||||
"integrity": "sha1-+FbHqcmrhcqaK8AiglkaolE6IO0="
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"marked": "^1.2.6",
|
||||
"numeral": "^2.0.6",
|
||||
"tween": "^0.9.0",
|
||||
"v-tooltip": "^2.0.3",
|
||||
"vue": "^2.6.12",
|
||||
"vue-apexcharts": "^1.6.1",
|
||||
|
|
|
@ -22,7 +22,8 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
<style lang="scss">
|
||||
|
||||
.marked-preview h1 {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
|
|
|
@ -35,6 +35,25 @@ Vue.use(VueApexCharts)
|
|||
|
||||
Vue.component('apexchart', VueApexCharts)
|
||||
|
||||
import numeral from "numeral"
|
||||
|
||||
// Filter to turn any number into a nice string like '10k', '5.5m'
|
||||
// Accepts 'max' parameter to set it's formatting while being animated
|
||||
Vue.filter('prettynum', function (value, max) {
|
||||
const num = numeral(value)
|
||||
const abs = Math.abs(max || num.value())
|
||||
|
||||
switch (abs) {
|
||||
case abs < 1000:
|
||||
return num.value()
|
||||
case abs >= 1000 && abs <= 10000:
|
||||
return num.format('0.0a')
|
||||
default:
|
||||
return num.format('0a')
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
let AuthToken = localStorage.getItem('AuthToken')
|
||||
let RefreshToken = localStorage.getItem('RefreshToken')
|
||||
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<span>{{ tweeningValue | prettynum(value) }}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TWEEN from "tween";
|
||||
|
||||
export default {
|
||||
name: "AnimatedNumber",
|
||||
props: {
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 500
|
||||
},
|
||||
delay: {
|
||||
type: Number,
|
||||
default: 100
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tweeningValue: 0
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value(oldVal, newVal) {
|
||||
this.tween(oldVal, newVal)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(()=> this.tween(0, this.value), this.delay)
|
||||
},
|
||||
methods: {
|
||||
tween(startValue, endValue) {
|
||||
var vm = this;
|
||||
function animate() {
|
||||
if (TWEEN.update()) {
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
}
|
||||
new TWEEN.Tween({ tweeningValue: startValue })
|
||||
.to({ tweeningValue: endValue }, this.duration).easing(TWEEN.Easing.Quintic.Out)
|
||||
.onUpdate(function() {
|
||||
vm.tweeningValue = this.tweeningValue.toFixed(0);
|
||||
})
|
||||
.start();
|
||||
|
||||
animate();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,27 +1,28 @@
|
|||
<template>
|
||||
<admin-card title="Usage stats">
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<apexchart type="bar" :options="userData.options" :series="userData.series"></apexchart>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<apexchart type="bar" :options="userData.options" :series="userData.series"></apexchart>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</admin-card>
|
||||
<admin-card title="Usage stats" v-bind="$attrs">
|
||||
<v-row dense>
|
||||
<v-col cols="6">
|
||||
<apexchart type="bar" height="150" :options="userData.options" :series="userData.series"/>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<apexchart type="bar" height="150" :options="userData.options" :series="userData.series"/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</admin-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AdminCard from "@/components/admin/AdminCard";
|
||||
import AdminCard from '@/components/admin/AdminCard'
|
||||
|
||||
export default {
|
||||
name: "UsageInfoCard",
|
||||
name: 'ActivityCard',
|
||||
components: { AdminCard },
|
||||
data(){
|
||||
data() {
|
||||
return {
|
||||
userData: {
|
||||
options: {
|
||||
chart: {
|
||||
id: "newUserData",
|
||||
id: 'newUserData',
|
||||
toolbar: {
|
||||
show: false
|
||||
},
|
||||
|
@ -58,26 +59,24 @@ export default {
|
|||
plotOptions: {
|
||||
bar: {
|
||||
borderRadius: 10,
|
||||
columnWidth: "90%",
|
||||
barHeight: "10%",
|
||||
columnWidth: '90%',
|
||||
barHeight: '10%',
|
||||
dataLabels: {
|
||||
position: "top" // top, center, bottom
|
||||
position: 'top' // top, center, bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "series-1",
|
||||
data: [30, 40, 45, 50, 49, 60, 70, 91]
|
||||
name: 'series-1',
|
||||
data: [30, 40, 45, 50, 49, 60, 70, 91, 100,44,61,98, 20]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<style scoped></style>
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<v-card elevation="0" outlined rounded>
|
||||
<v-card class="rounded-lg" v-bind="$attrs">
|
||||
<v-card-title class="d-flex justify-space-between">
|
||||
<span>{{title}}</span>
|
||||
<span>{{ title }}</span>
|
||||
<span>
|
||||
<slot name="menu"></slot>
|
||||
</span>
|
||||
</v-card-title>
|
||||
<span><slot></slot></span>
|
||||
<v-card-text><slot></slot></v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
|
@ -17,6 +17,4 @@ export default {
|
|||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<admin-card title="Info" v-bind="$attrs">
|
||||
<div class="d-flex justify-space-around">
|
||||
<div v-for="(value, name) in totalData" class="flex-grow-1">
|
||||
<h4 class="primary--text text--lighten-2 text-center">
|
||||
Total {{ name }} </h4>
|
||||
<v-tooltip bottom color="primary" :disabled="value < 1000">
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<p class="primary--text text-h3 text-md-h2 text-lg-h1 text-center" v-bind="attrs" v-on="on">
|
||||
<animated-number :value="value"/>
|
||||
</p>
|
||||
</template>
|
||||
<span>{{ value }}</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</admin-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AdminCard from "@/components/admin/AdminCard";
|
||||
import AnimatedNumber from "@/components/AnimatedNumber";
|
||||
|
||||
export default {
|
||||
name: "GeneralInfoCard",
|
||||
components: { AnimatedNumber, AdminCard },
|
||||
data() {
|
||||
return {
|
||||
totalData: {
|
||||
users: 435,
|
||||
streams: 1123,
|
||||
commits: 55460
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -14,23 +14,31 @@
|
|||
Cancel
|
||||
</v-btn>
|
||||
</template>
|
||||
<div v-if="serverInfo && edit">
|
||||
<v-card-text v-for="(value,name) in serverDetails" :key="name" class="pt-0 pb-0">
|
||||
<span v-if="name == 'inviteOnly'">
|
||||
{{ name }}
|
||||
<v-btn :disabled="edit" v-model="serverInfo['name']">Enable</v-btn>
|
||||
</span>
|
||||
<v-text-field v-else :hint="value.hint" :label="value.label" dense outlined :disabled="!edit"
|
||||
v-model="serverInfo[name]"></v-text-field>
|
||||
</v-card-text>
|
||||
</div>
|
||||
<div v-else-if="serverInfo && !edit">
|
||||
<v-card-text v-for="(value,name) in serverDetails" :key="name" class="pt-0">
|
||||
<v-btn-toggle elevation="0" borderless dense>
|
||||
<v-btn class="no-events" small color="primary">{{ value.label }}</v-btn>
|
||||
<v-btn class="no-events" small>{{ serverInfo[name] }}</v-btn>
|
||||
</v-btn-toggle>
|
||||
</v-card-text>
|
||||
<div v-if="serverInfo">
|
||||
<v-fade-transition mode="out-in">
|
||||
<div v-if="edit" key="editPanel">
|
||||
<v-card-text v-for="(value,name) in serverDetails" :key="name" class="pt-0 pb-0">
|
||||
<span v-if="name === 'inviteOnly'">
|
||||
{{ name }}
|
||||
<v-btn :disabled="edit" v-model="serverInfo['name']">Enable</v-btn>
|
||||
</span>
|
||||
<v-text-field v-else
|
||||
:hint="value.hint"
|
||||
:label="value.label"
|
||||
dense
|
||||
outlined
|
||||
v-model="serverInfo[name]"/>
|
||||
</v-card-text>
|
||||
</div>
|
||||
<div v-else key="viewPanel">
|
||||
<v-card-text class="pb-0">
|
||||
<p class="d-flex rounded-lg overflow-hidden" v-for="(value,name) in serverDetails" :key="name">
|
||||
<span class="pa-3 primary lighten-2 white--text" style="min-width: 25%">{{ value.label }}</span>
|
||||
<span class="pa-3 grey lighten-3 flex-grow-1">{{ serverInfo[name] }}</span>
|
||||
</p>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-fade-transition>
|
||||
</div>
|
||||
</admin-card>
|
||||
</template>
|
||||
|
@ -104,7 +112,4 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.no-events {
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<admin-card title="Version Info">
|
||||
<admin-card title="Version Info" v-bind="$attrs">
|
||||
<template v-slot:menu>
|
||||
<span v-if="isLatestVersion" class="text--h6 success--text">
|
||||
<v-icon size="medium" color="success">mdi-check-bold</v-icon>
|
||||
|
@ -10,33 +10,29 @@
|
|||
<span class="body-2 warning--text">There's a newer version available!</span>
|
||||
</span>
|
||||
</template>
|
||||
<v-card-text>
|
||||
<div class="d-flex justify-space-between pl-4 pr-4">
|
||||
<div>
|
||||
<h4 class="primary--text text--lighten-2">
|
||||
Current
|
||||
</h4>
|
||||
<p class="primary--text text-h4 text-sm-h2">
|
||||
{{ versionInfo.current }}
|
||||
</p>
|
||||
</div>
|
||||
<v-icon color="primary lighten-1">mdi-arrow-right</v-icon>
|
||||
<div>
|
||||
<h4 class="primary--text text--lighten-2">Latest</h4>
|
||||
<p class="primary--text text-h4 text-sm-h2">
|
||||
{{ versionInfo.latest }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="d-flex justify-space-around pl-4 pr-4">
|
||||
<div>
|
||||
<h4 class="primary--text text--lighten-2">
|
||||
Current </h4>
|
||||
<p class="primary--text text-h4 text-sm-h2">
|
||||
{{ versionInfo.current }} </p>
|
||||
</div>
|
||||
<v-btn v-if="!isLatestVersion" color="primary" width="100%">
|
||||
Follow our guide on how to update your server
|
||||
</v-btn>
|
||||
</v-card-text>
|
||||
<v-icon color="primary lighten-1">mdi-arrow-right</v-icon>
|
||||
<div>
|
||||
<h4 class="primary--text text--lighten-2">Latest</h4>
|
||||
<p class="primary--text text-h4 text-sm-h2">
|
||||
{{ versionInfo.latest }} </p>
|
||||
</div>
|
||||
</div>
|
||||
<v-btn v-if="!isLatestVersion" color="primary" width="100%">
|
||||
Follow our guide on how to update your server
|
||||
</v-btn>
|
||||
</admin-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AdminCard from "@/components/admin/AdminCard";
|
||||
import gql from "graphql-tag";
|
||||
|
||||
export default {
|
||||
name: "VersionInfoCard",
|
||||
|
@ -45,13 +41,25 @@ export default {
|
|||
return {
|
||||
versionInfo: {
|
||||
current: "2.1.18",
|
||||
latest: "2.1.18"
|
||||
latest: "dev"
|
||||
}
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
currentVersion: {
|
||||
query: gql`query {
|
||||
serverInfo {
|
||||
version
|
||||
}
|
||||
}`,
|
||||
update(data) {
|
||||
this.versionInfo.current = data.serverInfo.version;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isLatestVersion() {
|
||||
return this.versionInfo.current == this.versionInfo.latest;
|
||||
return this.versionInfo.current === this.versionInfo.latest;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,43 +1,47 @@
|
|||
import '@mdi/font/css/materialdesignicons.css'
|
||||
import Vue from 'vue'
|
||||
import Vuetify from 'vuetify/lib'
|
||||
import "@mdi/font/css/materialdesignicons.css";
|
||||
import Vue from "vue";
|
||||
import Vuetify from "vuetify/lib";
|
||||
|
||||
Vue.use(Vuetify)
|
||||
Vue.use(Vuetify);
|
||||
|
||||
let darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
let hasDarkMode = localStorage.getItem('darkModeEnabled')
|
||||
let darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
let hasDarkMode = localStorage.getItem("darkModeEnabled");
|
||||
if (!hasDarkMode && darkMediaQuery) {
|
||||
localStorage.setItem('darkModeEnabled', 'dark')
|
||||
localStorage.setItem("darkModeEnabled", "dark");
|
||||
}
|
||||
|
||||
export default new Vuetify({
|
||||
icons: {
|
||||
iconfont: 'mdi'
|
||||
iconfont: "mdi"
|
||||
},
|
||||
theme: {
|
||||
dark: localStorage.getItem('darkModeEnabled') === 'dark',
|
||||
options: { customProperties: true },
|
||||
dark: localStorage.getItem("darkModeEnabled") === "dark",
|
||||
themes: {
|
||||
light: {
|
||||
primary: '#047EFB', //blue
|
||||
secondary: '#7BBCFF', //light blue
|
||||
accent: '#FCF25E', //yellow
|
||||
error: '#FF5555', //red
|
||||
warning: '#FF9100', //orange
|
||||
info: '#313BCF', //dark blue
|
||||
success: '#4caf50',
|
||||
background: '#eeeeee',
|
||||
text: '#FFFFFF'
|
||||
primary: "#047EFB", //blue
|
||||
secondary: "#7BBCFF", //light blue
|
||||
accent: "#FCF25E", //yellow
|
||||
error: "#FF5555", //red
|
||||
warning: "#FF9100", //orange
|
||||
info: "#313BCF", //dark blue
|
||||
success: "#4caf50",
|
||||
background: "#eeeeee",
|
||||
text: "#FFFFFF"
|
||||
},
|
||||
dark: {
|
||||
primary: '#047EFB', //blue
|
||||
secondary: '#7BBCFF', //light blue
|
||||
accent: '#FCF25E', //yellow
|
||||
error: '#FF5555', //red
|
||||
warning: '#FF9100', //orange
|
||||
info: '#313BCF', //dark blue
|
||||
success: '#4caf50',
|
||||
background: '#3a3b3c'
|
||||
primary: "#047EFB", //blue
|
||||
secondary: "#7BBCFF", //light blue
|
||||
accent: "#FCF25E", //yellow
|
||||
error: "#FF5555", //red
|
||||
warning: "#FF9100", //orange
|
||||
info: "#313BCF", //dark blue
|
||||
success: "#4caf50",
|
||||
background: "#3a3b3c"
|
||||
}
|
||||
}
|
||||
},
|
||||
font: {
|
||||
family: "Space Grotesk"
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
|
@ -162,6 +162,11 @@ const routes = [
|
|||
name: "Admin | Streams",
|
||||
path: "streams",
|
||||
component: () => import('../views/admin/AdminStreams.vue')
|
||||
},
|
||||
{
|
||||
name: "Admin | Settings",
|
||||
path: "settings",
|
||||
component: () => import('../views/admin/AdminSettings.vue')
|
||||
}
|
||||
],
|
||||
component: () => import('../views/admin/AdminPanel.vue')
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
$heading-font-family: 'Space Grotesk';
|
|
@ -1,18 +1,22 @@
|
|||
<template>
|
||||
<div id="admin-overview">
|
||||
<server-info-admin-card/>
|
||||
<usage-info-card/>
|
||||
<version-info-card/>
|
||||
<!-- Dynamically register page cards-->
|
||||
<component v-for="comp in cards" :key="comp.name" :is="comp" outlined/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ServerInfoAdminCard from "@/components/admin/ServerInfoCard";
|
||||
import VersionInfoCard from "@/components/admin/VersionInfoCard";
|
||||
import UsageInfoCard from "@/components/admin/UsageInfoCard";
|
||||
import ActivityCard from "@/components/admin/ActivityCard";
|
||||
import GeneralInfoCard from "@/components/admin/GeneralInfoCard";
|
||||
|
||||
export default {
|
||||
name: "AdminOverview",
|
||||
components: { UsageInfoCard, VersionInfoCard, ServerInfoAdminCard },
|
||||
data() {
|
||||
return {
|
||||
cards: [GeneralInfoCard, ActivityCard, VersionInfoCard]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -2,15 +2,28 @@
|
|||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="4" lg="3" xl="3" class="pt-10">
|
||||
<v-card id="sideMenu" elevation="0" outlined rounded>
|
||||
<v-card id="sideMenu" elevation="1" class="rounded-lg overflow-hidden">
|
||||
<v-card-title>Admin panel</v-card-title>
|
||||
<v-card-text v-for="child in childRoutes" :key="child.to" class="pa-3">
|
||||
<router-link :to="child.to">{{ child.name }}</router-link>
|
||||
</v-card-text>
|
||||
<div v-for="child in childRoutes" :key="child.to">
|
||||
<router-link :to="child.to" v-slot="{ isExactActive, route, navigate }">
|
||||
<v-hover v-slot="{ hover }" >
|
||||
<span :disabled="isExactActive"
|
||||
@click="navigate"
|
||||
:class="{'active-border primary--text': isExactActive,'primary--text': hover}"
|
||||
class="pa-2 pl-6 text-left d-flex admin-menu-item bold">
|
||||
<v-icon small class="pr-1" :color="(hover || isExactActive) ? 'primary' : null">{{ child.icon }}</v-icon>
|
||||
{{ child.name }}
|
||||
</span>
|
||||
</v-hover>
|
||||
</router-link>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="12" md="8" lg="9" xl="9" class="pt-10">
|
||||
<router-view></router-view>
|
||||
<v-fade-transition mode="out-in">
|
||||
<router-view></router-view>
|
||||
</v-fade-transition>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
@ -18,29 +31,61 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AdminPanel',
|
||||
name: "AdminPanel",
|
||||
data() {
|
||||
return {
|
||||
childRoutes: [
|
||||
{
|
||||
name: 'Dashboard',
|
||||
to: '/admin'
|
||||
name: "Dashboard",
|
||||
to: "/admin",
|
||||
icon: "mdi-view-dashboard"
|
||||
},
|
||||
{
|
||||
name: 'Users',
|
||||
to: '/admin/users'
|
||||
name: "Users",
|
||||
to: "/admin/users",
|
||||
icon: "mdi-account-multiple"
|
||||
},
|
||||
{
|
||||
name: 'Streams',
|
||||
to: '/admin/streams'
|
||||
name: "Streams",
|
||||
to: "/admin/streams",
|
||||
icon: "mdi-cloud"
|
||||
},
|
||||
{
|
||||
name: "Settings",
|
||||
to: "/admin/settings",
|
||||
icon: "mdi-cog"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#sideMenu {
|
||||
|
||||
.gray-border {
|
||||
border-top: 1pt solid var(--v-background-base) !important;
|
||||
}
|
||||
.admin-menu-item {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-top: 1pt solid var(--v-background-base) !important;
|
||||
cursor: pointer;
|
||||
transition: 0.5s all ease-out, border-top-color 0s;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: var(--v-primary-base);
|
||||
transition: all 0.5s ease-in-out, border-top-color 0s;
|
||||
}
|
||||
|
||||
&.active-border::before {
|
||||
width: 4pt;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<server-info-admin-card/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ServerInfoAdminCard from "@/components/admin/ServerInfoCard";
|
||||
export default {
|
||||
name: "AdminSettings",
|
||||
components: { ServerInfoAdminCard }
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,6 +1,6 @@
|
|||
<template lang="html">
|
||||
<v-container>
|
||||
<p>Admin Streams</p>
|
||||
<p></p>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
<template>
|
||||
<v-container>
|
||||
<p>Admin Users</p>
|
||||
<admin-card title="Test card"/>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AdminCard from "@/components/admin/AdminCard";
|
||||
export default {
|
||||
name: 'AdminUsers'
|
||||
name: 'AdminUsers',
|
||||
components: { AdminCard }
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче