Merge branch 'feature/stats' into 'develop'
Add stats page (status counts by scope) See merge request pleroma/admin-fe!74
This commit is contained in:
commit
0cae34b62d
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
|
||||
- Report notes
|
||||
- Ability to moderate users on the statuses page
|
||||
- Stats page: status counts are displayed here
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ Features, that can be disabled:
|
|||
- moderation log: `DISABLED_FEATURES: '["moderationLog"]'`
|
||||
- settings: `DISABLED_FEATURES: '["settings"]'`
|
||||
- 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.
|
||||
|
||||
|
|
|
@ -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()}` } : {}
|
|
@ -67,7 +67,8 @@ export default {
|
|||
reports: 'Reports',
|
||||
settings: 'Settings',
|
||||
moderationLog: 'Moderation Log',
|
||||
'emoji-packs': 'Emoji packs'
|
||||
'emoji-packs': 'Emoji packs',
|
||||
stats: 'Stats'
|
||||
},
|
||||
navbar: {
|
||||
logOut: 'Log Out',
|
||||
|
@ -432,5 +433,14 @@ export default {
|
|||
emailSent: 'Invite was sent',
|
||||
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`'
|
||||
},
|
||||
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 = [
|
||||
{
|
||||
path: '/redirect',
|
||||
|
@ -145,6 +159,7 @@ export const asyncRouterMap = [
|
|||
...(invitesDisabled ? [] : [invites]),
|
||||
...(moderationLogDisabled ? [] : [moderationLog]),
|
||||
...(settingsDisabled ? [] : [settings]),
|
||||
...(statsDisabled ? [] : [stats]),
|
||||
{
|
||||
path: '/users/:id',
|
||||
component: Layout,
|
||||
|
|
|
@ -49,7 +49,7 @@ const getters = {
|
|||
http: state => state.settings.settings['http'],
|
||||
httpSecurity: state => state.settings.settings['http_security'],
|
||||
instance: state => state.settings.settings['instance'],
|
||||
instances: state => state.peers.fetchedPeers,
|
||||
instances: state => state.instance.fetchedPeers,
|
||||
kocaptcha: state => state.settings.settings['Pleroma.Captcha.Kocaptcha'],
|
||||
level: state => state.settings.settings['level'],
|
||||
ldap: state => state.settings.settings['ldap'],
|
||||
|
@ -84,6 +84,7 @@ const getters = {
|
|||
suggestions: state => state.settings.settings['suggestions'],
|
||||
scheduledActivity: state => state.settings.settings['Pleroma.ScheduledActivity'],
|
||||
statuses: state => state.status.fetchedStatuses,
|
||||
statusCounts: state => state.instance.fetchedStatusCounts,
|
||||
teslaAdapter: state => state.settings.settings['adapter'],
|
||||
twitter: state => state.settings.settings['Ueberauth.Strategy.Twitter.OAuth'],
|
||||
ueberauth: state => state.settings.settings['Ueberauth'],
|
||||
|
|
|
@ -4,7 +4,7 @@ import app from './modules/app'
|
|||
import errorLog from './modules/errorLog'
|
||||
import moderationLog from './modules/moderationLog'
|
||||
import invites from './modules/invites'
|
||||
import peers from './modules/peers'
|
||||
import instance from './modules/instance'
|
||||
import permission from './modules/permission'
|
||||
import relays from './modules/relays'
|
||||
import reports from './modules/reports'
|
||||
|
@ -25,7 +25,7 @@ const store = new Vuex.Store({
|
|||
errorLog,
|
||||
moderationLog,
|
||||
invites,
|
||||
peers,
|
||||
instance,
|
||||
permission,
|
||||
relays,
|
||||
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: {
|
||||
loadingPeers() {
|
||||
return this.$store.state.peers.loading
|
||||
return this.$store.state.instance.peersLoading
|
||||
},
|
||||
...mapGetters([
|
||||
'instances',
|
||||
|
|
Loading…
Reference in a new issue