feat(admin): Frontend page structure and first overview mock
This commit is contained in:
Generated
+82
@@ -2968,6 +2968,19 @@
|
||||
"picomatch": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"apexcharts": {
|
||||
"version": "3.26.3",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.26.3.tgz",
|
||||
"integrity": "sha512-zbP7RBBV2CGffoVMIuTCUG64YbEUzV8IIT7iNVLMtY/OAVXTjPksDxSqKIniTvgJoscKe6sx4P56qDpBSU19VA==",
|
||||
"requires": {
|
||||
"svg.draggable.js": "^2.2.2",
|
||||
"svg.easing.js": "^2.0.0",
|
||||
"svg.filter.js": "^2.0.2",
|
||||
"svg.pathmorphing.js": "^0.1.3",
|
||||
"svg.resize.js": "^1.4.3",
|
||||
"svg.select.js": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"apollo": {
|
||||
"version": "2.28.0",
|
||||
"resolved": "https://registry.npmjs.org/apollo/-/apollo-2.28.0.tgz",
|
||||
@@ -13408,6 +13421,70 @@
|
||||
"integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=",
|
||||
"dev": true
|
||||
},
|
||||
"svg.draggable.js": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
||||
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
||||
"requires": {
|
||||
"svg.js": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"svg.easing.js": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
|
||||
"integrity": "sha1-iqmUawqOJ4V6XEChDrpAkeVpHxI=",
|
||||
"requires": {
|
||||
"svg.js": ">=2.3.x"
|
||||
}
|
||||
},
|
||||
"svg.filter.js": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
|
||||
"integrity": "sha1-kQCOFROJ3ZIwd5/L5uLJo2LRwgM=",
|
||||
"requires": {
|
||||
"svg.js": "^2.2.5"
|
||||
}
|
||||
},
|
||||
"svg.js": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
||||
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA=="
|
||||
},
|
||||
"svg.pathmorphing.js": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
|
||||
"integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
|
||||
"requires": {
|
||||
"svg.js": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"svg.resize.js": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
||||
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
||||
"requires": {
|
||||
"svg.js": "^2.6.5",
|
||||
"svg.select.js": "^2.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"svg.select.js": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
||||
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
||||
"requires": {
|
||||
"svg.js": "^2.2.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"svg.select.js": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
||||
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
||||
"requires": {
|
||||
"svg.js": "^2.6.5"
|
||||
}
|
||||
},
|
||||
"svgo": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz",
|
||||
@@ -14357,6 +14434,11 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
|
||||
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
|
||||
},
|
||||
"vue-apexcharts": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-apexcharts/-/vue-apexcharts-1.6.1.tgz",
|
||||
"integrity": "sha512-ILn3/55IvZQUgsc7+jKDjPfHfGUlcUQi/lDrLjRe5g7gfjj99o8otXoHwMeib3CBHYdQXNG9foe1vzv7RdUzXA=="
|
||||
},
|
||||
"vue-apollo": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-apollo/-/vue-apollo-3.0.5.tgz",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"@speckle/viewer": "^2.0.5",
|
||||
"@vuejs-community/vue-filter-date-format": "^1.6.3",
|
||||
"@vuejs-community/vue-filter-date-parse": "^1.1.6",
|
||||
"apexcharts": "^3.26.3",
|
||||
"crypto-random-string": "^3.3.0",
|
||||
"dompurify": "^2.2.4",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
@@ -20,6 +21,7 @@
|
||||
"marked": "^1.2.6",
|
||||
"v-tooltip": "^2.0.3",
|
||||
"vue": "^2.6.12",
|
||||
"vue-apexcharts": "^1.6.1",
|
||||
"vue-apollo": "^3.0.5",
|
||||
"vue-infinite-loading": "^2.4.5",
|
||||
"vue-matomo": "^3.14.0-0",
|
||||
|
||||
@@ -30,6 +30,11 @@ Vue.use(VueMatomo, {
|
||||
userId: localStorage.getItem('suuid')
|
||||
})
|
||||
|
||||
import VueApexCharts from 'vue-apexcharts'
|
||||
Vue.use(VueApexCharts)
|
||||
|
||||
Vue.component('apexchart', VueApexCharts)
|
||||
|
||||
let AuthToken = localStorage.getItem('AuthToken')
|
||||
let RefreshToken = localStorage.getItem('RefreshToken')
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<v-card elevation="0" outlined rounded>
|
||||
<v-card-title class="d-flex justify-space-between">
|
||||
<span>{{title}}</span>
|
||||
<span>
|
||||
<slot name="menu"></slot>
|
||||
</span>
|
||||
</v-card-title>
|
||||
<span><slot></slot></span>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AdminCard",
|
||||
props: ["title"]
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<admin-card title="Server Info">
|
||||
<template v-slot:menu>
|
||||
<v-btn small outlined rounded color="primary" v-if="!edit" @click="edit = !edit">
|
||||
<v-icon size="medium">mdi-pencil</v-icon>
|
||||
Edit
|
||||
</v-btn>
|
||||
<v-btn small outlined rounded color="success" v-if="edit" @click="saveEdit">
|
||||
<v-icon size="medium">mdi-check-bold</v-icon>
|
||||
Save
|
||||
</v-btn>
|
||||
<v-btn small outlined rounded color="error" v-if="edit" @click="cancelEdit">
|
||||
<v-icon size="medium">mdi-close-thick</v-icon>
|
||||
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>
|
||||
</admin-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AdminCard from "@/components/admin/AdminCard";
|
||||
import gql from "graphql-tag";
|
||||
|
||||
export default {
|
||||
name: "ServerInfoAdminCard",
|
||||
components: { AdminCard },
|
||||
data() {
|
||||
return {
|
||||
edit: false,
|
||||
serverDetails: {
|
||||
name: {
|
||||
label: "Name",
|
||||
hint: "This server's public name"
|
||||
},
|
||||
description: {
|
||||
label: "Description",
|
||||
hint: "A short description of this server"
|
||||
},
|
||||
company: {
|
||||
label: "Company",
|
||||
hint: "The owner of this server"
|
||||
},
|
||||
adminContact: {
|
||||
label: "Admin contact",
|
||||
hint: "The administrator of this server"
|
||||
},
|
||||
termsOfService: {
|
||||
label: "Terms of service",
|
||||
hint: "Url pointing to the terms of service page"
|
||||
},
|
||||
inviteOnly: {
|
||||
label: "Invite-Only mode"
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
serverInfo: {
|
||||
query: gql`
|
||||
query {
|
||||
serverInfo {
|
||||
name
|
||||
company
|
||||
description
|
||||
adminContact
|
||||
termsOfService
|
||||
inviteOnly
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancelEdit() {
|
||||
this.edit = false;
|
||||
this.loading = false;
|
||||
this.saving = false;
|
||||
},
|
||||
saveEdit() {
|
||||
this.$apollo.mutate({
|
||||
mutation: gql``
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.no-events {
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AdminCard from "@/components/admin/AdminCard";
|
||||
export default {
|
||||
name: "UsageInfoCard",
|
||||
components: { AdminCard },
|
||||
data(){
|
||||
return {
|
||||
userData: {
|
||||
options: {
|
||||
chart: {
|
||||
id: "newUserData",
|
||||
toolbar: {
|
||||
show: false
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true
|
||||
},
|
||||
xaxis: {
|
||||
labels: {
|
||||
show: false
|
||||
},
|
||||
axisTicks: {
|
||||
show: false
|
||||
},
|
||||
axisBorder: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
show: false,
|
||||
axisTicks: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
show: false
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
borderRadius: 10,
|
||||
columnWidth: "90%",
|
||||
barHeight: "10%",
|
||||
dataLabels: {
|
||||
position: "top" // top, center, bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "series-1",
|
||||
data: [30, 40, 45, 50, 49, 60, 70, 91]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<admin-card title="Version Info">
|
||||
<template v-slot:menu>
|
||||
<span v-if="isLatestVersion" class="text--h6 success--text">
|
||||
<v-icon size="medium" color="success">mdi-check-bold</v-icon>
|
||||
<span class="body-2 success--text">Your server is up to date</span>
|
||||
</span>
|
||||
<span v-else class="warning--text">
|
||||
<v-icon size="medium" color="warning">mdi-alert</v-icon>
|
||||
<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>
|
||||
<v-btn v-if="!isLatestVersion" color="primary" width="100%">
|
||||
Follow our guide on how to update your server
|
||||
</v-btn>
|
||||
</v-card-text>
|
||||
</admin-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AdminCard from "@/components/admin/AdminCard";
|
||||
|
||||
export default {
|
||||
name: "VersionInfoCard",
|
||||
components: { AdminCard },
|
||||
data() {
|
||||
return {
|
||||
versionInfo: {
|
||||
current: "2.1.18",
|
||||
latest: "2.1.18"
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isLatestVersion() {
|
||||
return this.versionInfo.current == this.versionInfo.latest;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -140,6 +140,31 @@ const routes = [
|
||||
title: 'User Profile | Speckle'
|
||||
},
|
||||
component: () => import('../views/ProfileUser.vue')
|
||||
},
|
||||
{
|
||||
path: 'admin',
|
||||
meta: {
|
||||
title: 'Admin | Overview'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Admin | Overview',
|
||||
path: '',
|
||||
component: () => import('../views/admin/AdminOverview.vue')
|
||||
},
|
||||
{
|
||||
name: "Admin | Users",
|
||||
path: "users",
|
||||
component: () => import('../views/admin/AdminUsers.vue')
|
||||
|
||||
},
|
||||
{
|
||||
name: "Admin | Streams",
|
||||
path: "streams",
|
||||
component: () => import('../views/admin/AdminStreams.vue')
|
||||
}
|
||||
],
|
||||
component: () => import('../views/admin/AdminPanel.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div id="admin-overview">
|
||||
<server-info-admin-card/>
|
||||
<usage-info-card/>
|
||||
<version-info-card/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ServerInfoAdminCard from "@/components/admin/ServerInfoCard";
|
||||
import VersionInfoCard from "@/components/admin/VersionInfoCard";
|
||||
import UsageInfoCard from "@/components/admin/UsageInfoCard";
|
||||
export default {
|
||||
name: "AdminOverview",
|
||||
components: { UsageInfoCard, VersionInfoCard, ServerInfoAdminCard },
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
#admin-overview {
|
||||
.v-card:not(:last-child) {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template lang="html">
|
||||
<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-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>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="8" lg="9" xl="9" class="pt-10">
|
||||
<router-view></router-view>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AdminPanel',
|
||||
data() {
|
||||
return {
|
||||
childRoutes: [
|
||||
{
|
||||
name: 'Dashboard',
|
||||
to: '/admin'
|
||||
},
|
||||
{
|
||||
name: 'Users',
|
||||
to: '/admin/users'
|
||||
},
|
||||
{
|
||||
name: 'Streams',
|
||||
to: '/admin/streams'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#sideMenu {
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,15 @@
|
||||
<template lang="html">
|
||||
<v-container>
|
||||
<p>Admin Streams</p>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AdminStreams'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<p>Admin Users</p>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AdminUsers'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user