forked from AkkomaGang/admin-fe
Add stats page (status counts by scope)
This commit is contained in:
parent
7731d5c158
commit
c1de536cc0
11 changed files with 158 additions and 33 deletions
|
@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Ability to confirm users' emails and resend confirmation emails
|
- Ability to confirm users' emails and resend confirmation emails
|
||||||
- Report notes
|
- Report notes
|
||||||
- Ability to moderate users on the statuses page
|
- Ability to moderate users on the statuses page
|
||||||
|
- Stats page: status counts are displayed here
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ Features, that can be disabled:
|
||||||
- moderation log: `DISABLED_FEATURES: '["moderationLog"]'`
|
- moderation log: `DISABLED_FEATURES: '["moderationLog"]'`
|
||||||
- settings: `DISABLED_FEATURES: '["settings"]'`
|
- settings: `DISABLED_FEATURES: '["settings"]'`
|
||||||
- emoji packs: `DISABLED_FEATURES: '["emojiPacks"]'`
|
- emoji packs: `DISABLED_FEATURES: '["emojiPacks"]'`
|
||||||
|
- stats: `DISABLED_FEATURES: '["stats"]'`
|
||||||
|
|
||||||
Of course, you can disable multiple features just by adding to the array, e.g. `DISABLED_FEATURES: '["emojiPacks", "settings"]'` will have both emoji packs and settings disabled.
|
Of course, you can disable multiple features just by adding to the array, e.g. `DISABLED_FEATURES: '["emojiPacks", "settings"]'` will have both emoji packs and settings disabled.
|
||||||
|
|
||||||
|
|
|
@ -11,4 +11,13 @@ export async function fetchPeers(authHost, token) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchInstanceInfo(authHost, token) {
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(authHost),
|
||||||
|
url: `/api/v1/instance`,
|
||||||
|
method: 'get',
|
||||||
|
headers: authHeaders(token)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}
|
const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}
|
|
@ -67,7 +67,8 @@ export default {
|
||||||
reports: 'Reports',
|
reports: 'Reports',
|
||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
moderationLog: 'Moderation Log',
|
moderationLog: 'Moderation Log',
|
||||||
'emoji-packs': 'Emoji packs'
|
'emoji-packs': 'Emoji packs',
|
||||||
|
stats: 'Stats'
|
||||||
},
|
},
|
||||||
navbar: {
|
navbar: {
|
||||||
logOut: 'Log Out',
|
logOut: 'Log Out',
|
||||||
|
@ -432,5 +433,14 @@ export default {
|
||||||
emailSent: 'Invite was sent',
|
emailSent: 'Invite was sent',
|
||||||
submitFormError: 'There are invalid values in the form. Please fix them before continuing.',
|
submitFormError: 'There are invalid values in the form. Please fix them before continuing.',
|
||||||
inviteViaEmailAlert: 'To send invite via email make sure to enable `invites_enabled` and disable `registrations_open`'
|
inviteViaEmailAlert: 'To send invite via email make sure to enable `invites_enabled` and disable `registrations_open`'
|
||||||
|
},
|
||||||
|
stats: {
|
||||||
|
stats: 'Instance stats',
|
||||||
|
statusCounts: 'Status counts',
|
||||||
|
all: 'All',
|
||||||
|
public: 'Public',
|
||||||
|
unlisted: 'Unlisted',
|
||||||
|
direct: 'Direct',
|
||||||
|
private: 'Private'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,20 @@ const moderationLog = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const statsDisabled = disabledFeatures.includes('stats')
|
||||||
|
const stats = {
|
||||||
|
path: '/stats',
|
||||||
|
component: Layout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index',
|
||||||
|
component: () => import('@/views/stats/index'),
|
||||||
|
name: 'Stats',
|
||||||
|
meta: { title: 'stats', icon: 'chart', noCache: true }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
export const constantRouterMap = [
|
export const constantRouterMap = [
|
||||||
{
|
{
|
||||||
path: '/redirect',
|
path: '/redirect',
|
||||||
|
@ -145,6 +159,7 @@ export const asyncRouterMap = [
|
||||||
...(invitesDisabled ? [] : [invites]),
|
...(invitesDisabled ? [] : [invites]),
|
||||||
...(moderationLogDisabled ? [] : [moderationLog]),
|
...(moderationLogDisabled ? [] : [moderationLog]),
|
||||||
...(settingsDisabled ? [] : [settings]),
|
...(settingsDisabled ? [] : [settings]),
|
||||||
|
...(statsDisabled ? [] : [stats]),
|
||||||
{
|
{
|
||||||
path: '/users/:id',
|
path: '/users/:id',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
|
|
@ -49,7 +49,7 @@ const getters = {
|
||||||
http: state => state.settings.settings['http'],
|
http: state => state.settings.settings['http'],
|
||||||
httpSecurity: state => state.settings.settings['http_security'],
|
httpSecurity: state => state.settings.settings['http_security'],
|
||||||
instance: state => state.settings.settings['instance'],
|
instance: state => state.settings.settings['instance'],
|
||||||
instances: state => state.peers.fetchedPeers,
|
instances: state => state.instance.fetchedPeers,
|
||||||
kocaptcha: state => state.settings.settings['Pleroma.Captcha.Kocaptcha'],
|
kocaptcha: state => state.settings.settings['Pleroma.Captcha.Kocaptcha'],
|
||||||
level: state => state.settings.settings['level'],
|
level: state => state.settings.settings['level'],
|
||||||
ldap: state => state.settings.settings['ldap'],
|
ldap: state => state.settings.settings['ldap'],
|
||||||
|
@ -84,6 +84,7 @@ const getters = {
|
||||||
suggestions: state => state.settings.settings['suggestions'],
|
suggestions: state => state.settings.settings['suggestions'],
|
||||||
scheduledActivity: state => state.settings.settings['Pleroma.ScheduledActivity'],
|
scheduledActivity: state => state.settings.settings['Pleroma.ScheduledActivity'],
|
||||||
statuses: state => state.status.fetchedStatuses,
|
statuses: state => state.status.fetchedStatuses,
|
||||||
|
statusCounts: state => state.instance.fetchedStatusCounts,
|
||||||
teslaAdapter: state => state.settings.settings['adapter'],
|
teslaAdapter: state => state.settings.settings['adapter'],
|
||||||
twitter: state => state.settings.settings['Ueberauth.Strategy.Twitter.OAuth'],
|
twitter: state => state.settings.settings['Ueberauth.Strategy.Twitter.OAuth'],
|
||||||
ueberauth: state => state.settings.settings['Ueberauth'],
|
ueberauth: state => state.settings.settings['Ueberauth'],
|
||||||
|
|
|
@ -4,7 +4,7 @@ import app from './modules/app'
|
||||||
import errorLog from './modules/errorLog'
|
import errorLog from './modules/errorLog'
|
||||||
import moderationLog from './modules/moderationLog'
|
import moderationLog from './modules/moderationLog'
|
||||||
import invites from './modules/invites'
|
import invites from './modules/invites'
|
||||||
import peers from './modules/peers'
|
import instance from './modules/instance'
|
||||||
import permission from './modules/permission'
|
import permission from './modules/permission'
|
||||||
import relays from './modules/relays'
|
import relays from './modules/relays'
|
||||||
import reports from './modules/reports'
|
import reports from './modules/reports'
|
||||||
|
@ -25,7 +25,7 @@ const store = new Vuex.Store({
|
||||||
errorLog,
|
errorLog,
|
||||||
moderationLog,
|
moderationLog,
|
||||||
invites,
|
invites,
|
||||||
peers,
|
instance,
|
||||||
permission,
|
permission,
|
||||||
relays,
|
relays,
|
||||||
reports,
|
reports,
|
||||||
|
|
40
src/store/modules/instance.js
Normal file
40
src/store/modules/instance.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { fetchPeers, fetchInstanceInfo } from '@/api/instance'
|
||||||
|
|
||||||
|
const instance = {
|
||||||
|
state: {
|
||||||
|
fetchedPeers: [],
|
||||||
|
peersLoading: true,
|
||||||
|
fetchedStatusCounts: {},
|
||||||
|
statusCountsLoading: true
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_PEERS: (state, peers) => {
|
||||||
|
state.fetchedPeers = peers
|
||||||
|
},
|
||||||
|
SET_PEERS_LOADING: (state, status) => {
|
||||||
|
state.peersLoading = status
|
||||||
|
},
|
||||||
|
SET_STATUS_COUNTS: (state, counts) => {
|
||||||
|
state.fetchedStatusCounts = counts
|
||||||
|
},
|
||||||
|
SET_STATUS_COUNTS_LOADING: (state, status) => {
|
||||||
|
state.statusCountsLoading = status
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async FetchPeers({ commit, getters }) {
|
||||||
|
const peers = await fetchPeers(getters.authHost, getters.token)
|
||||||
|
|
||||||
|
commit('SET_PEERS', peers.data)
|
||||||
|
commit('SET_PEERS_LOADING', false)
|
||||||
|
},
|
||||||
|
async FetchStatusCounts({ commit, getters }) {
|
||||||
|
const info = await fetchInstanceInfo(getters.authHost, getters.token)
|
||||||
|
|
||||||
|
commit('SET_STATUS_COUNTS', info.data.stats.status_count)
|
||||||
|
commit('SET_STATUS_COUNTS_LOADING', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default instance
|
|
@ -1,28 +0,0 @@
|
||||||
import { fetchPeers } from '@/api/peers'
|
|
||||||
|
|
||||||
const peers = {
|
|
||||||
state: {
|
|
||||||
fetchedPeers: [],
|
|
||||||
loading: true
|
|
||||||
},
|
|
||||||
|
|
||||||
mutations: {
|
|
||||||
SET_PEERS: (state, peers) => {
|
|
||||||
state.fetchedPeers = peers
|
|
||||||
},
|
|
||||||
SET_LOADING: (state, status) => {
|
|
||||||
state.loading = status
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
async FetchPeers({ commit, getters }) {
|
|
||||||
const peers = await fetchPeers(getters.authHost, getters.token)
|
|
||||||
|
|
||||||
commit('SET_PEERS', peers.data)
|
|
||||||
commit('SET_LOADING', false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default peers
|
|
76
src/views/stats/index.vue
Normal file
76
src/views/stats/index.vue
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<template>
|
||||||
|
<div class="stats-container">
|
||||||
|
<h1>{{ $t('stats.stats') }}</h1>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="5">
|
||||||
|
<el-card v-if="!loadingStatusCounts" class="box-card">
|
||||||
|
<div slot="header">
|
||||||
|
<h4>{{ $t('stats.statusCounts') }}</h4>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('stats.all') }}</td>
|
||||||
|
<td class="number">{{ formatNumber(statusCounts.all) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('stats.public') }}</td>
|
||||||
|
<td class="number">{{ formatNumber(statusCounts.public) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('stats.unlisted') }}</td>
|
||||||
|
<td class="number">{{ formatNumber(statusCounts.unlisted) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('stats.direct') }}</td>
|
||||||
|
<td class="number">{{ formatNumber(statusCounts.direct) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('stats.private') }}</td>
|
||||||
|
<td class="number">{{ formatNumber(statusCounts.private) }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import numeral from 'numeral'
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Stats',
|
||||||
|
computed: {
|
||||||
|
loadingStatusCounts() {
|
||||||
|
return this.$store.state.instance.statusCountsLoading
|
||||||
|
},
|
||||||
|
...mapGetters([
|
||||||
|
'statusCounts'
|
||||||
|
])
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch('FetchStatusCounts')
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatNumber(num) {
|
||||||
|
return numeral(num).format('0,0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel='stylesheet/scss' lang='scss'>
|
||||||
|
.stats-container {
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
td.number {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -49,7 +49,7 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
loadingPeers() {
|
loadingPeers() {
|
||||||
return this.$store.state.peers.loading
|
return this.$store.state.instance.peersLoading
|
||||||
},
|
},
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
'instances',
|
'instances',
|
||||||
|
|
Loading…
Reference in a new issue