forked from AkkomaGang/admin-fe
Merge branch 'feature/reports-user-moderate-actions' into 'master'
Reporting enhancements See merge request pleroma/admin-fe!24
This commit is contained in:
commit
8b3a6ef4fd
14 changed files with 396 additions and 20 deletions
26
.gitlab-ci.yml
Normal file
26
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
image: node:10
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- lint
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
|
||||||
|
lint:
|
||||||
|
stage: lint
|
||||||
|
script:
|
||||||
|
- yarn
|
||||||
|
- yarn lint
|
||||||
|
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
variables:
|
||||||
|
APT_CACHE_DIR: apt-cache
|
||||||
|
script:
|
||||||
|
- yarn
|
||||||
|
- yarn test
|
||||||
|
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- yarn
|
||||||
|
- npm run build:prod
|
|
@ -42,7 +42,7 @@
|
||||||
"driver.js": "0.8.1",
|
"driver.js": "0.8.1",
|
||||||
"dropzone": "5.2.0",
|
"dropzone": "5.2.0",
|
||||||
"echarts": "4.1.0",
|
"echarts": "4.1.0",
|
||||||
"element-ui": "^2.7.0",
|
"element-ui": "^2.10.0",
|
||||||
"file-saver": "1.3.8",
|
"file-saver": "1.3.8",
|
||||||
"fuse.js": "3.4.2",
|
"fuse.js": "3.4.2",
|
||||||
"js-cookie": "2.2.0",
|
"js-cookie": "2.2.0",
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
const reports = [
|
const reports = [
|
||||||
{ created_at: '2019-05-21T21:35:33.000Z', account: { acct: 'benj', display_name: 'Benjamin Fame' }, actor: { acct: 'admin' }, state: 'open', id: '2', content: 'This is a report', statuses: [] },
|
{ created_at: '2019-05-21T21:35:33.000Z', account: { acct: 'benj', display_name: 'Benjamin Fame', tags: [] }, actor: { acct: 'admin' }, state: 'open', id: '2', content: 'This is a report', statuses: [] },
|
||||||
{ created_at: '2019-05-20T22:45:33.000Z', account: { acct: 'alice', display_name: 'Alice Pool' }, actor: { acct: 'admin2' }, state: 'resolved', id: '1', content: 'Please block this user', statuses: [] },
|
{ created_at: '2019-05-20T22:45:33.000Z', account: { acct: 'alice', display_name: 'Alice Pool', tags: [] }, actor: { acct: 'admin2' }, state: 'resolved', id: '1', content: 'Please block this user', statuses: [] },
|
||||||
{ created_at: '2019-05-18T13:01:33.000Z', account: { acct: 'nick', display_name: 'Nick Keys' }, actor: { acct: 'admin' }, state: 'closed', id: '3', content: '', statuses: [] },
|
{ created_at: '2019-05-18T13:01:33.000Z', account: { acct: 'nick', display_name: 'Nick Keys', tags: [] }, actor: { acct: 'admin' }, state: 'closed', id: '3', content: '', statuses: [] },
|
||||||
{ created_at: '2019-05-21T21:35:33.000Z', account: { acct: 'benj', display_name: 'Benjamin Fame' }, actor: { acct: 'admin' }, state: 'open', id: '5', content: 'This is a report', statuses: [] },
|
{ created_at: '2019-05-21T21:35:33.000Z', account: { acct: 'benj', display_name: 'Benjamin Fame', tags: [] }, actor: { acct: 'admin' }, state: 'open', id: '5', content: 'This is a report', statuses: [] },
|
||||||
{ created_at: '2019-05-20T22:45:33.000Z', account: { acct: 'alice', display_name: 'Alice Pool' }, actor: { acct: 'admin2' }, state: 'resolved', id: '7', content: 'Please block this user', statuses: [
|
{ created_at: '2019-05-20T22:45:33.000Z', account: { acct: 'alice', display_name: 'Alice Pool', tags: [] }, actor: { acct: 'admin2' }, state: 'resolved', id: '7', content: 'Please block this user', statuses: [
|
||||||
{ account: { display_name: 'Alice Pool', avatar: '' }, visibility: 'public', sensitive: false, id: '11', content: 'Hey!', url: '', created_at: '2019-05-10T21:35:33.000Z' },
|
{ account: { display_name: 'Alice Pool', avatar: '' }, visibility: 'public', sensitive: false, id: '11', content: 'Hey!', url: '', created_at: '2019-05-10T21:35:33.000Z' },
|
||||||
{ account: { display_name: 'Alice Pool', avatar: '' }, visibility: 'unlisted', sensitive: true, id: '10', content: 'Bye!', url: '', created_at: '2019-05-10T21:00:33.000Z' }
|
{ account: { display_name: 'Alice Pool', avatar: '' }, visibility: 'unlisted', sensitive: true, id: '10', content: 'Bye!', url: '', created_at: '2019-05-10T21:00:33.000Z' }
|
||||||
] },
|
] },
|
||||||
{ created_at: '2019-05-18T13:01:33.000Z', account: { acct: 'nick', display_name: 'Nick Keys' }, actor: { acct: 'admin' }, state: 'closed', id: '6', content: '', statuses: [] },
|
{ created_at: '2019-05-18T13:01:33.000Z', account: { acct: 'nick', display_name: 'Nick Keys', tags: [] }, actor: { acct: 'admin' }, state: 'closed', id: '6', content: '', statuses: [] },
|
||||||
{ created_at: '2019-05-18T13:01:33.000Z', account: { acct: 'nick', display_name: 'Nick Keys' }, actor: { acct: 'admin' }, state: 'closed', id: '4', content: '', statuses: [] }
|
{ created_at: '2019-05-18T13:01:33.000Z', account: { acct: 'nick', display_name: 'Nick Keys', tags: [] }, actor: { acct: 'admin' }, state: 'closed', id: '4', content: '', statuses: [] }
|
||||||
]
|
]
|
||||||
|
|
||||||
export async function fetchReports(limit, max_id, authHost, token) {
|
export async function fetchReports(limit, max_id, authHost, token) {
|
||||||
|
|
|
@ -39,6 +39,15 @@ export async function deleteUser(nickname, authHost, token) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchUser(id, authHost, token) {
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(authHost),
|
||||||
|
url: `/api/pleroma/admin/users/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
headers: authHeaders(token)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchUsers(filters, authHost, token, page = 1) {
|
export async function fetchUsers(filters, authHost, token, page = 1) {
|
||||||
return await request({
|
return await request({
|
||||||
baseURL: baseName(authHost),
|
baseURL: baseName(authHost),
|
||||||
|
@ -86,4 +95,13 @@ export async function untagUser(nicknames, tags, authHost, token) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchUserStatuses(id, authHost, godmode, token) {
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(authHost),
|
||||||
|
url: `/api/pleroma/admin/users/${id}/statuses?godmode=${godmode}`,
|
||||||
|
method: 'get',
|
||||||
|
headers: authHeaders(token)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}
|
const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}
|
||||||
|
|
|
@ -221,7 +221,16 @@ export default {
|
||||||
emptyPasswordError: 'Please input the password',
|
emptyPasswordError: 'Please input the password',
|
||||||
emptyNicknameError: 'Please input the username',
|
emptyNicknameError: 'Please input the username',
|
||||||
invalidNicknameError: 'Username can include "a-z", "A-Z" and "0-9" characters'
|
invalidNicknameError: 'Username can include "a-z", "A-Z" and "0-9" characters'
|
||||||
|
},
|
||||||
|
userProfile: {
|
||||||
|
tags: 'Tags',
|
||||||
|
moderator: 'Moderator',
|
||||||
|
admin: 'Admin',
|
||||||
|
local: 'Local',
|
||||||
|
nickname: 'Nickname',
|
||||||
|
deactivated: 'Deactivated',
|
||||||
|
recentStatuses: 'Recent Statues',
|
||||||
|
showPrivateStatuses: 'Show private statuses'
|
||||||
},
|
},
|
||||||
usersFilter: {
|
usersFilter: {
|
||||||
inputPlaceholder: 'Select filter',
|
inputPlaceholder: 'Select filter',
|
||||||
|
@ -245,8 +254,9 @@ export default {
|
||||||
deleteCompleted: 'Delete comleted',
|
deleteCompleted: 'Delete comleted',
|
||||||
deleteCanceled: 'Delete canceled',
|
deleteCanceled: 'Delete canceled',
|
||||||
noNotes: 'No notes to display',
|
noNotes: 'No notes to display',
|
||||||
changeState: 'Change state',
|
changeState: 'Change report state',
|
||||||
changeScope: 'Change scope',
|
changeScope: 'Change scope',
|
||||||
|
moderateUser: 'Moderate user',
|
||||||
resolve: 'Resolve',
|
resolve: 'Resolve',
|
||||||
reopen: 'Reopen',
|
reopen: 'Reopen',
|
||||||
close: 'Close',
|
close: 'Close',
|
||||||
|
|
|
@ -76,5 +76,17 @@ export const asyncRouterMap = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/users/:id',
|
||||||
|
component: Layout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
name: 'UsersShow',
|
||||||
|
component: () => import('@/views/users/show')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
{ path: '*', redirect: '/404', hidden: true }
|
{ path: '*', redirect: '/404', hidden: true }
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,6 +6,7 @@ import permission from './modules/permission'
|
||||||
import reports from './modules/reports'
|
import reports from './modules/reports'
|
||||||
import tagsView from './modules/tagsView'
|
import tagsView from './modules/tagsView'
|
||||||
import user from './modules/user'
|
import user from './modules/user'
|
||||||
|
import userProfile from './modules/userProfile'
|
||||||
import users from './modules/users'
|
import users from './modules/users'
|
||||||
import getters from './getters'
|
import getters from './getters'
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ const store = new Vuex.Store({
|
||||||
reports,
|
reports,
|
||||||
tagsView,
|
tagsView,
|
||||||
user,
|
user,
|
||||||
|
userProfile,
|
||||||
users
|
users
|
||||||
},
|
},
|
||||||
getters
|
getters
|
||||||
|
|
36
src/store/modules/userProfile.js
Normal file
36
src/store/modules/userProfile.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { fetchUser, fetchUserStatuses } from '@/api/users'
|
||||||
|
|
||||||
|
const userProfile = {
|
||||||
|
state: {
|
||||||
|
user: {},
|
||||||
|
loading: true,
|
||||||
|
statuses: []
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_USER: (state, user) => {
|
||||||
|
state.user = user
|
||||||
|
},
|
||||||
|
SET_LOADING: (state, status) => {
|
||||||
|
state.loading = status
|
||||||
|
},
|
||||||
|
SET_STATUSES: (state, statuses) => {
|
||||||
|
state.statuses = statuses
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async FetchData({ commit, getters }, { id, godmode }) {
|
||||||
|
commit('SET_LOADING', true)
|
||||||
|
|
||||||
|
const [userResponse, statusesResponse] = await Promise.all([
|
||||||
|
fetchUser(id, getters.authHost, getters.token),
|
||||||
|
fetchUserStatuses(id, getters.authHost, godmode, getters.token)
|
||||||
|
])
|
||||||
|
|
||||||
|
commit('SET_USER', userResponse.data)
|
||||||
|
commit('SET_STATUSES', statusesResponse.data)
|
||||||
|
commit('SET_LOADING', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default userProfile
|
|
@ -48,6 +48,9 @@ const users = {
|
||||||
},
|
},
|
||||||
SET_USERS_FILTERS: (state, filters) => {
|
SET_USERS_FILTERS: (state, filters) => {
|
||||||
state.filters = filters
|
state.filters = filters
|
||||||
|
},
|
||||||
|
SET_USER_PROFILE: (state, user) => {
|
||||||
|
state.userProfile = user
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
@ -4,20 +4,42 @@
|
||||||
:placeholder="$t('reportsFilter.inputPlaceholder')"
|
:placeholder="$t('reportsFilter.inputPlaceholder')"
|
||||||
clearable
|
clearable
|
||||||
class="select-field"
|
class="select-field"
|
||||||
|
value-key="value"
|
||||||
@change="toggleFilters">
|
@change="toggleFilters">
|
||||||
<el-option value="open">{{ $t('reportsFilter.open') }}</el-option>
|
<el-option
|
||||||
<el-option value="closed">{{ $t('reportsFilter.closed') }}</el-option>
|
v-for="item in options"
|
||||||
<el-option value="resolved">{{ $t('reportsFilter.resolved') }}</el-option>
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">{{ item.label }}</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import i18n from '@/lang'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
filter: []
|
filter: 'open',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: 'open',
|
||||||
|
label: i18n.t('reportsFilter.open')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'closed',
|
||||||
|
label: i18n.t('reportsFilter.closed')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'resolved',
|
||||||
|
label: i18n.t('reportsFilter.resolved')
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.$store.dispatch('SetFilter', this.$data.filter)
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleFilters() {
|
toggleFilters() {
|
||||||
this.$store.dispatch('SetFilter', this.$data.filter)
|
this.$store.dispatch('SetFilter', this.$data.filter)
|
||||||
|
|
|
@ -16,6 +16,60 @@
|
||||||
<el-dropdown-item v-if="report.state !== 'closed'" @click.native="changeReportState('closed', report.id)">{{ $t('reports.close') }}</el-dropdown-item>
|
<el-dropdown-item v-if="report.state !== 'closed'" @click.native="changeReportState('closed', report.id)">{{ $t('reports.close') }}</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
|
<el-dropdown trigger="click">
|
||||||
|
<el-button plain size="small" icon="el-icon-files">{{ $t('reports.moderateUser') }}<i class="el-icon-arrow-down el-icon--right"/></el-button>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item
|
||||||
|
v-if="showDeactivatedButton(report.account)"
|
||||||
|
@click.native="handleDeactivation(report.account)">
|
||||||
|
{{ report.account.deactivated ? $t('users.activateAccount') : $t('users.deactivateAccount') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-if="showDeactivatedButton(report.account.id)"
|
||||||
|
@click.native="handleDeletion(report.account.id)">
|
||||||
|
{{ $t('users.deleteAccount') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
:divided="true"
|
||||||
|
:class="{ 'active-tag': report.account.tags.includes('force_nsfw') }"
|
||||||
|
@click.native="toggleTag(report.account, 'force_nsfw')">
|
||||||
|
{{ $t('users.forceNsfw') }}
|
||||||
|
<i v-if="report.account.tags.includes('force_nsfw')" class="el-icon-check"/>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
:class="{ 'active-tag': report.account.tags.includes('strip_media') }"
|
||||||
|
@click.native="toggleTag(report.account, 'strip_media')">
|
||||||
|
{{ $t('users.stripMedia') }}
|
||||||
|
<i v-if="report.account.tags.includes('strip_media')" class="el-icon-check"/>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
:class="{ 'active-tag': report.account.tags.includes('force_unlisted') }"
|
||||||
|
@click.native="toggleTag(report.account, 'force_unlisted')">
|
||||||
|
{{ $t('users.forceUnlisted') }}
|
||||||
|
<i v-if="report.account.tags.includes('force_unlisted')" class="el-icon-check"/>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
:class="{ 'active-tag': report.account.tags.includes('sandbox') }"
|
||||||
|
@click.native="toggleTag(report.account, 'sandbox')">
|
||||||
|
{{ $t('users.sandbox') }}
|
||||||
|
<i v-if="report.account.tags.includes('sandbox')" class="el-icon-check"/>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-if="report.account.local"
|
||||||
|
:class="{ 'active-tag': report.account.tags.includes('disable_remote_subscription') }"
|
||||||
|
@click.native="toggleTag(report.account, 'disable_remote_subscription')">
|
||||||
|
{{ $t('users.disableRemoteSubscription') }}
|
||||||
|
<i v-if="report.account.tags.includes('disable_remote_subscription')" class="el-icon-check"/>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-if="report.account.local"
|
||||||
|
:class="{ 'active-tag': report.account.tags.includes('disable_any_subscription') }"
|
||||||
|
@click.native="toggleTag(report.account, 'disable_any_subscription')">
|
||||||
|
{{ $t('users.disableAnySubscription') }}
|
||||||
|
<i v-if="report.account.tags.includes('disable_any_subscription')" class="el-icon-check"/>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -86,7 +140,21 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parseTimestamp(timestamp) {
|
parseTimestamp(timestamp) {
|
||||||
return moment(timestamp).format('YYYY-MM-DD HH:mm')
|
return moment(timestamp).format('L HH:mm')
|
||||||
|
},
|
||||||
|
showDeactivatedButton(id) {
|
||||||
|
return this.$store.state.user.id !== id
|
||||||
|
},
|
||||||
|
handleDeactivation({ nickname }) {
|
||||||
|
this.$store.dispatch('ToggleUserActivation', nickname)
|
||||||
|
},
|
||||||
|
handleDeletion(user) {
|
||||||
|
this.$store.dispatch('DeleteUser', user)
|
||||||
|
},
|
||||||
|
toggleTag(user, tag) {
|
||||||
|
user.tags.includes(tag)
|
||||||
|
? this.$store.dispatch('RemoveTag', { users: [user], tag })
|
||||||
|
: this.$store.dispatch('AddTag', { users: [user], tag })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
<el-table-column :min-width="width" :label="$t('users.id')" prop="id" />
|
<el-table-column :min-width="width" :label="$t('users.id')" prop="id" />
|
||||||
<el-table-column :label="$t('users.name')" prop="nickname">
|
<el-table-column :label="$t('users.name')" prop="nickname">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ scope.row.nickname }}
|
<router-link :to="{ name: 'UsersShow', params: { id: scope.row.id }}">{{ scope.row.nickname }}</router-link>
|
||||||
<el-tag v-if="isDesktop" type="info" size="mini">
|
<el-tag v-if="isDesktop" type="info" size="mini">
|
||||||
<span>{{ scope.row.local ? $t('users.local') : $t('users.external') }}</span>
|
<span>{{ scope.row.local ? $t('users.local') : $t('users.external') }}</span>
|
||||||
</el-tag>
|
</el-tag>
|
||||||
|
|
179
src/views/users/show.vue
Normal file
179
src/views/users/show.vue
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
<template>
|
||||||
|
<main v-if="!loading">
|
||||||
|
<header>
|
||||||
|
<el-avatar :src="user.avatar" size="large" />
|
||||||
|
<h1>{{ user.display_name }}</h1>
|
||||||
|
</header>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="6">
|
||||||
|
<div class="el-table el-table--fit el-table--enable-row-hover el-table--enable-row-transition el-table--medium">
|
||||||
|
<table class="el-table__body">
|
||||||
|
<tbody>
|
||||||
|
<tr class="el-table__row">
|
||||||
|
<td class="name-col">ID</td>
|
||||||
|
<td class="value-col">
|
||||||
|
{{ user.id }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="el-table__row">
|
||||||
|
<td>{{ $t('userProfile.tags') }}</td>
|
||||||
|
<td>
|
||||||
|
<el-tag v-for="tag in user.tags" :key="tag">{{ tag }}</el-tag>
|
||||||
|
<span v-if="user.tags.length === 0">None</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="el-table__row">
|
||||||
|
<td>{{ $t('userProfile.moderator') }}</td>
|
||||||
|
<td>
|
||||||
|
<el-tag v-if="user.roles.moderator" type="success"><i class="el-icon-check" /></el-tag>
|
||||||
|
<el-tag v-if="!user.roles.moderator" type="danger"><i class="el-icon-error" /></el-tag>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="el-table__row">
|
||||||
|
<td>{{ $t('userProfile.admin') }}</td>
|
||||||
|
<td>
|
||||||
|
<el-tag v-if="user.roles.admin" type="success"><i class="el-icon-check" /></el-tag>
|
||||||
|
<el-tag v-if="!user.roles.admin" type="danger"><i class="el-icon-error" /></el-tag>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="el-table__row">
|
||||||
|
<td>{{ $t('userProfile.local') }}</td>
|
||||||
|
<td>
|
||||||
|
<el-tag v-if="user.local" type="success"><i class="el-icon-check" /></el-tag>
|
||||||
|
<el-tag v-if="!user.local" type="danger"><i class="el-icon-error" /></el-tag>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="el-table__row">
|
||||||
|
<td>{{ $t('userProfile.deactivated') }}</td>
|
||||||
|
<td>
|
||||||
|
<el-tag v-if="user.deactivated" type="success"><i class="el-icon-check" /></el-tag>
|
||||||
|
<el-tag v-if="!user.deactivated" type="danger"><i class="el-icon-error" /></el-tag>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="el-table__row">
|
||||||
|
<td>{{ $t('userProfile.nickname') }}</td>
|
||||||
|
<td>
|
||||||
|
{{ user.nickname }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-row type="flex" class="row-bg" justify="space-between">
|
||||||
|
<el-col :span="18"><h2>{{ $t('userProfile.recentStatuses') }}</h2></el-col>
|
||||||
|
<el-col :span="6" class="show-private">
|
||||||
|
<el-checkbox v-model="showPrivate" @change="onTogglePrivate">
|
||||||
|
{{ $t('userProfile.showPrivateStatuses') }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-col :span="18">
|
||||||
|
<el-timeline class="statuses">
|
||||||
|
<el-timeline-item v-for="status in statuses" :timestamp="createdAtLocaleString(status.created_at)" :key="status.id">
|
||||||
|
<el-card>
|
||||||
|
<strong v-if="status.spoiler_text">{{ status.spoiler_text }}</strong>
|
||||||
|
<p v-if="status.content" v-html="status.content" />
|
||||||
|
<div v-if="status.poll" class="poll">
|
||||||
|
<ul>
|
||||||
|
<li v-for="(option, index) in status.poll.options" :key="index">
|
||||||
|
{{ option.title }}
|
||||||
|
<el-progress :percentage="optionPercent(status.poll, option)" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-for="(attachment, index) in status.media_attachments" :key="index" class="image">
|
||||||
|
<img :src="attachment.preview_url">
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'UsersShow',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showPrivate: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
loading() {
|
||||||
|
return this.$store.state.userProfile.loading
|
||||||
|
},
|
||||||
|
user() {
|
||||||
|
return this.$store.state.userProfile.user
|
||||||
|
},
|
||||||
|
statuses() {
|
||||||
|
return this.$store.state.userProfile.statuses
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function() {
|
||||||
|
this.$store.dispatch('FetchData', { id: this.$route.params.id, godmode: false })
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
optionPercent(poll, pollOption) {
|
||||||
|
const allVotes = poll.options.reduce((acc, option) => (acc + option.votes_count), 0)
|
||||||
|
if (allVotes === 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return +(pollOption.votes_count / allVotes * 100).toFixed(1)
|
||||||
|
},
|
||||||
|
createdAtLocaleString(createdAt) {
|
||||||
|
const date = new Date(createdAt)
|
||||||
|
|
||||||
|
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`
|
||||||
|
},
|
||||||
|
onTogglePrivate() {
|
||||||
|
console.log(this.showPrivate)
|
||||||
|
|
||||||
|
this.$store.dispatch('FetchData', { id: this.$route.params.id, godmode: this.showPrivate })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel='stylesheet/scss' lang='scss' scoped>
|
||||||
|
header {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
margin: 22px 0;
|
||||||
|
padding-left: 15px;
|
||||||
|
h1 {
|
||||||
|
margin: 0 0 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
margin: 10px 0 0 15px;
|
||||||
|
.name-col {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-table--border::after, .el-table--group::after, .el-table::before {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.poll ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
.image {
|
||||||
|
width: 20%;
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.statuses {
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
.show-private {
|
||||||
|
text-align: right;
|
||||||
|
line-height: 67px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -3446,10 +3446,10 @@ elegant-spinner@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
|
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
|
||||||
integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
|
integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
|
||||||
|
|
||||||
element-ui@^2.7.0:
|
element-ui@^2.10.0:
|
||||||
version "2.7.0"
|
version "2.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.7.0.tgz#6bfcdfa5c75bfc4cda835186f2a1f98b93cd5d14"
|
resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.10.0.tgz#e6129f6b6d6ffe0dbad125a4a8d17d447a5f639c"
|
||||||
integrity sha512-FalWzOmT/K4w4C/8tw2kGvzzQnRJ5MqEvSL5rEKNa081PFGIcUS9exyVpYrNPKF8ua/W6qaqrXPC6DQ8sNcmOQ==
|
integrity sha512-uthsnJ1CIdQvLWphr67uwFSfSYoRBjxFcEhXhy+2/EwKNsqO7MRN+mYqroNLz5WJuLqVy1aOpJ8Lv4B32qKthQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
async-validator "~1.8.1"
|
async-validator "~1.8.1"
|
||||||
babel-helper-vue-jsx-merge-props "^2.0.0"
|
babel-helper-vue-jsx-merge-props "^2.0.0"
|
||||||
|
|
Loading…
Reference in a new issue