Merge branch 'feature/improve-user-profile-interface' into 'master'

Update user profile interface

See merge request pleroma/admin-fe!53
This commit is contained in:
Maxim Filippov 2019-11-28 16:24:27 +00:00
commit 01ccb43965
7 changed files with 196 additions and 110 deletions

View file

@ -4,6 +4,10 @@ export let users = [
{ active: false, deactivated: true, id: 'abc', nickname: 'john', local: true, external: false, roles: { admin: false, moderator: false }, tags: ['strip_media'] }
]
const userProfile = { avatar: 'avatar.jpg', display_name: 'Allis', nickname: 'allis', id: '2', tags: [], roles: { admin: true, moderator: false }, local: true, external: false }
const userStatuses = []
const filterUsers = (str) => {
const filters = str.split(',').filter(item => item.length > 0)
if (filters.length === 0) {
@ -20,6 +24,10 @@ const filterUsers = (str) => {
return applyFilters([], filters, users)
}
export async function fetchUser(id, authHost, token) {
return Promise.resolve({ data: userProfile })
}
export async function fetchUsers(filters, authHost, token, page = 1) {
const filteredUsers = filterUsers(filters)
return Promise.resolve({ data: {
@ -29,6 +37,10 @@ export async function fetchUsers(filters, authHost, token, page = 1) {
}})
}
export async function fetchUserStatuses(id, authHost, godmode, token) {
return Promise.resolve({ data: userStatuses })
}
export async function getPasswordResetToken(nickname, authHost, token) {
return Promise.resolve({ data: { token: 'g05lxnBJQnL', link: 'http://url/api/pleroma/password_reset/g05lxnBJQnL' }})
}

View file

@ -236,11 +236,17 @@ export default {
tags: 'Tags',
moderator: 'Moderator',
admin: 'Admin',
local: 'Local',
local: 'local',
external: 'external',
localUppercase: 'Local',
nickname: 'Nickname',
deactivated: 'Deactivated',
recentStatuses: 'Recent Statues',
showPrivateStatuses: 'Show private statuses'
showPrivateStatuses: 'Show private statuses',
roles: 'Roles',
activeUppercase: 'Active',
active: 'active',
deactivated: 'deactivated',
noStatuses: 'No statuses to show'
},
usersFilter: {
inputPlaceholder: 'Select filter',

View file

@ -185,54 +185,56 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
.account {
text-decoration: underline;
line-height: 26px;
font-size: 13px;
}
.image {
width: 20%;
img {
width: 100%;
}
}
.show-more-button {
margin-left: 5px;
}
.status-account {
display: flex;
align-items: center;
}
.status-avatar-img {
width: 15px;
height: 15px;
margin-right: 5px;
}
.status-account-name {
margin: 0;
height: 22px;
}
.status-body {
display: flex;
flex-direction: column;
}
.status-content {
font-size: 15px;
line-height: 26px;
}
.status-card {
margin-bottom: 15px;
}
.status-deleted {
font-style: italic;
margin-top: 3px;
}
.status-header {
display: flex;
justify-content: space-between;
}
.status-without-content {
font-style: italic;
.account {
text-decoration: underline;
line-height: 26px;
font-size: 13px;
}
.image {
width: 20%;
img {
width: 100%;
}
}
.show-more-button {
margin-left: 5px;
}
.status-account {
display: flex;
align-items: center;
}
.status-avatar-img {
width: 15px;
height: 15px;
margin-right: 5px;
}
.status-account-name {
margin: 0;
height: 22px;
}
.status-body {
display: flex;
flex-direction: column;
}
.status-content {
font-size: 15px;
line-height: 26px;
}
.status-card {
margin-bottom: 15px;
}
.status-deleted {
font-style: italic;
margin-top: 3px;
}
.status-header {
display: flex;
justify-content: space-between;
}
.status-without-content {
font-style: italic;
}
}
@media
only screen and (max-width: 760px),

View file

@ -156,9 +156,6 @@
</p>
</div>
</el-dialog>
<div v-if="users.length === 0" class="no-users-message">
<p>There are no users to display</p>
</div>
<div v-if="!loading" class="pagination">
<el-pagination
:total="usersCount"

View file

@ -5,60 +5,60 @@
<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 :span="8">
<el-card class="user-profile-card">
<div class="el-table el-table--fit el-table--enable-row-hover el-table--enable-row-transition el-table--medium">
<table class="user-profile-table">
<tbody>
<tr class="el-table__row">
<td>{{ $t('userProfile.nickname') }}</td>
<td>
{{ user.nickname }}
</td>
</tr>
<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" class="user-profile-tag">{{ tag }}</el-tag>
<span v-if="user.tags.length === 0"></span>
</td>
</tr>
<tr class="el-table__row">
<td>{{ $t('userProfile.roles') }}</td>
<td>
<el-tag v-if="user.roles.admin" class="user-profile-tag">
{{ $t('users.admin') }}
</el-tag>
<el-tag v-if="user.roles.moderator" class="user-profile-tag">
{{ $t('users.moderator') }}
</el-tag>
<span v-if="!user.roles.moderator && !user.roles.admin"></span>
</td>
</tr>
<tr class="el-table__row">
<td>{{ $t('userProfile.localUppercase') }}</td>
<td>
<el-tag v-if="user.local" type="info">{{ $t('userProfile.local') }}</el-tag>
<el-tag v-if="!user.local" type="info">{{ $t('userProfile.external') }}</el-tag>
</td>
</tr>
<tr class="el-table__row">
<td>{{ $t('userProfile.activeUppercase') }}</td>
<td>
<el-tag v-if="user.deactivated" type="success">{{ $t('userProfile.active') }}</el-tag>
<el-tag v-if="!user.deactivated" type="danger">{{ $t('userProfile.deactivated') }}</el-tag>
</td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-row type="flex" class="row-bg" justify="space-between">
<el-col :span="18">
@ -70,11 +70,12 @@
</el-checkbox>
</el-col>
</el-row>
<el-col :span="18">
<el-col :span="16">
<el-timeline v-if="!statusesLoading" class="statuses">
<el-timeline-item v-for="status in statuses" :key="status.id">
<status :status="status" :user-id="user.id" :godmode="showPrivate"/>
</el-timeline-item>
<p v-if="statuses.length === 0" class="no-statuses">{{ $t('userProfile.noStatuses') }}</p>
</el-timeline>
</el-col>
</el-row>
@ -147,8 +148,16 @@ table {
width: 100%;
}
}
.no-statuses {
margin-left: 28px;
color: #606266;
}
.recent-statuses-header {
margin-top: 10px;
}
.statuses {
padding-right: 20px;
padding: 0 20px 0 0;
}
.show-private {
text-align: right;
@ -156,6 +165,16 @@ table {
padding-right: 20px;
}
.recent-statuses {
margin-left: 40px;
margin-left: 28px;
}
.user-profile-card {
margin-left: 15px;
margin-right: 20px;
}
.user-profile-table {
margin: 0;
}
.user-profile-tag {
margin: 0 4px 4px 0;
}
</style>

View file

@ -0,0 +1,48 @@
import Vuex from 'vuex'
import { mount, createLocalVue, config } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import Element from 'element-ui'
import UsersShow from '@/views/users/show'
import storeConfig from './store.conf'
import { cloneDeep } from 'lodash'
config.mocks["$t"] = () => {}
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Element)
const $route = {
params: {
id: '2'
}
}
jest.mock('@/api/nodeInfo')
jest.mock('@/api/users')
describe('Search and filter users', () => {
let store
beforeEach(() => {
store = new Vuex.Store(cloneDeep(storeConfig))
})
it('fetches user profile', async (done) => {
const wrapper = mount(UsersShow, {
store,
localVue,
sync: false,
stubs: ['router-link'],
mocks: {
$route
}
})
await flushPromises()
expect(wrapper.find('.user-profile-card').isVisible()).toBe(true)
expect(store.state.userProfile.user.nickname).toBe('allis')
expect(store.state.userProfile.user.roles.admin).toBe(true)
done()
})
})

View file

@ -1,5 +1,6 @@
import app from '@/store/modules/app'
import user from '@/store/modules/user'
import userProfile from '@/store/modules/userProfile'
import users from '@/store/modules/users'
import getters from '@/store/getters'
@ -7,6 +8,7 @@ export default {
modules: {
app,
user,
userProfile,
users
},
getters