forked from AkkomaGang/admin-fe
Merge branch 'feature/add-reports' into 'master'
Add ability to read reports Closes #14 See merge request pleroma/admin-fe!11
This commit is contained in:
commit
3a8a032e81
20 changed files with 1217 additions and 8 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -19,3 +19,4 @@ selenium-debug.log
|
|||
*.sln
|
||||
|
||||
package-lock.json
|
||||
coverage/
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
"driver.js": "0.8.1",
|
||||
"dropzone": "5.2.0",
|
||||
"echarts": "4.1.0",
|
||||
"element-ui": "2.4.11",
|
||||
"element-ui": "^2.7.0",
|
||||
"file-saver": "1.3.8",
|
||||
"fuse.js": "3.4.2",
|
||||
"js-cookie": "2.2.0",
|
||||
|
@ -50,6 +50,7 @@
|
|||
"jszip": "3.1.5",
|
||||
"lodash": "^4.17.11",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"moment": "^2.24.0",
|
||||
"normalize.css": "7.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
"numeral": "^2.0.6",
|
||||
|
|
37
src/api/__mocks__/reports.js
Normal file
37
src/api/__mocks__/reports.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
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-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-18T13:01:33.000Z', account: { acct: 'nick', display_name: 'Nick Keys' }, 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-20T22:45:33.000Z', account: { acct: 'alice', display_name: 'Alice Pool' }, 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: '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' }, actor: { acct: 'admin' }, state: 'closed', id: '4', content: '', statuses: [] }
|
||||
]
|
||||
|
||||
export async function fetchReports(limit, max_id, authHost, token) {
|
||||
const paginatedReports = max_id.length > 0 ? reports.slice(5) : reports.slice(0, 5)
|
||||
return Promise.resolve({ data: { reports: paginatedReports }})
|
||||
}
|
||||
|
||||
export async function filterReports(filter, limit, max_id, authHost, token) {
|
||||
const filteredReports = reports.filter(report => report.state === filter)
|
||||
const paginatedReports = max_id.length > 0 ? filteredReports.slice(5) : filteredReports.slice(0, 5)
|
||||
return Promise.resolve({ data: { reports: paginatedReports }})
|
||||
}
|
||||
|
||||
export async function changeState(state, id, authHost, token) {
|
||||
const report = reports.find(report => report.id === id)
|
||||
return Promise.resolve({ data: { ...report, state }})
|
||||
}
|
||||
|
||||
export async function changeStatusScope(id, sensitive, visibility, authHost, token) {
|
||||
const status = reports[4].statuses[0]
|
||||
return Promise.resolve({ data: { ...status, sensitive, visibility }})
|
||||
}
|
||||
|
||||
export async function deleteStatus(statusId, authHost, token) {
|
||||
return Promise.resolve()
|
||||
}
|
52
src/api/reports.js
Normal file
52
src/api/reports.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import request from '@/utils/request'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { baseName } from './utils'
|
||||
|
||||
export async function changeState(state, id, authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/reports/${id}`,
|
||||
method: 'put',
|
||||
headers: authHeaders(token),
|
||||
data: { state }
|
||||
})
|
||||
}
|
||||
|
||||
export async function changeStatusScope(id, sensitive, visibility, authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/statuses/${id}`,
|
||||
method: 'put',
|
||||
headers: authHeaders(token),
|
||||
data: { sensitive, visibility }
|
||||
})
|
||||
}
|
||||
|
||||
export async function deleteStatus(id, authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/statuses/${id}`,
|
||||
method: 'delete',
|
||||
headers: authHeaders(token)
|
||||
})
|
||||
}
|
||||
|
||||
export async function fetchReports(limit, max_id, authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/reports?limit=${limit}&max_id=${max_id}`,
|
||||
method: 'get',
|
||||
headers: authHeaders(token)
|
||||
})
|
||||
}
|
||||
|
||||
export async function filterReports(filter, limit, max_id, authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/reports?state=${filter}&limit=${limit}&max_id=${max_id}`,
|
||||
method: 'get',
|
||||
headers: authHeaders(token)
|
||||
})
|
||||
}
|
||||
|
||||
const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}
|
218
src/api/reportsData.js
Normal file
218
src/api/reportsData.js
Normal file
|
@ -0,0 +1,218 @@
|
|||
export const reports = [
|
||||
{
|
||||
id: '1',
|
||||
timestamp: '2019/4/12',
|
||||
local: true,
|
||||
from: 'John', // actor nickname
|
||||
object: 'Bob', // user nickname
|
||||
header: 'Report #1', // content
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Nick', text: 'Lorem ipsum', id: '1', timestamp: '2019/4/13' },
|
||||
{ author: 'Val', text: 'dolor sit amet', id: '2', timestamp: '2019/4/13' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
timestamp: '2019/4/1',
|
||||
local: true,
|
||||
from: 'Max',
|
||||
object: 'Vic',
|
||||
header: 'Report #2',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Tony', text: 'consectetur adipiscing elit', id: '3', timestamp: '2019/4/2' },
|
||||
{ author: 'Zac', text: 'sed do eiusmod tempor incididunt', id: '4', timestamp: '2019/4/3' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
timestamp: '2019/2/28',
|
||||
local: true,
|
||||
from: 'Tim',
|
||||
object: 'Jen',
|
||||
header: 'Report #3',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [{ author: 'Bruce', text: 'ut labore et dolore magna aliqua', id: '5', timestamp: '2019/3/1' }]
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
timestamp: '2019/4/12',
|
||||
local: true,
|
||||
from: 'John', // actor nickname
|
||||
object: 'Bob', // user nickname
|
||||
header: 'Report #4', // content
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Nick', text: 'Lorem ipsum', id: '6', timestamp: '2019/4/13' },
|
||||
{ author: 'Val', text: 'dolor sit amet', id: '7', timestamp: '2019/4/13' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
timestamp: '2019/4/1',
|
||||
local: true,
|
||||
from: 'Max',
|
||||
object: 'Vic',
|
||||
header: 'Report #5',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Tony', text: 'consectetur adipiscing elit', id: '8', timestamp: '2019/4/2' },
|
||||
{ author: 'Zac', text: 'sed do eiusmod tempor incididunt', id: '9', timestamp: '2019/4/3' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
timestamp: '2019/2/28',
|
||||
local: true,
|
||||
from: 'Tim',
|
||||
object: 'Jen',
|
||||
header: 'Report #6',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [{ author: 'Bruce', text: 'ut labore et dolore magna aliqua', id: '10', timestamp: '2019/3/1' }]
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
timestamp: '2019/4/12',
|
||||
local: true,
|
||||
from: 'John', // actor nickname
|
||||
object: 'Bob', // user nickname
|
||||
header: 'Report #7', // content
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Nick', text: 'Lorem ipsum', id: '11', timestamp: '2019/4/13' },
|
||||
{ author: 'Val', text: 'dolor sit amet', id: '12', timestamp: '2019/4/13' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
timestamp: '2019/4/1',
|
||||
local: true,
|
||||
from: 'Max',
|
||||
object: 'Vic',
|
||||
header: 'Report #8',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Tony', text: 'consectetur adipiscing elit', id: '13', timestamp: '2019/4/2' },
|
||||
{ author: 'Zac', text: 'sed do eiusmod tempor incididunt', id: '14', timestamp: '2019/4/3' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
timestamp: '2019/2/28',
|
||||
local: true,
|
||||
from: 'Tim',
|
||||
object: 'Jen',
|
||||
header: 'Report #9',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [{ author: 'Bruce', text: 'ut labore et dolore magna aliqua', id: '15', timestamp: '2019/3/1' }]
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
timestamp: '2019/4/12',
|
||||
local: true,
|
||||
from: 'John', // actor nickname
|
||||
object: 'Bob', // user nickname
|
||||
header: 'Report #10', // content
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Nick', text: 'Lorem ipsum', id: '16', timestamp: '2019/4/13' },
|
||||
{ author: 'Val', text: 'dolor sit amet', id: '17', timestamp: '2019/4/13' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '11',
|
||||
timestamp: '2019/4/1',
|
||||
local: true,
|
||||
from: 'Max',
|
||||
object: 'Vic',
|
||||
header: 'Report #11',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Tony', text: 'consectetur adipiscing elit', id: '18', timestamp: '2019/4/2' },
|
||||
{ author: 'Zac', text: 'sed do eiusmod tempor incididunt', id: '19', timestamp: '2019/4/3' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '12',
|
||||
timestamp: '2019/2/28',
|
||||
local: true,
|
||||
from: 'Tim',
|
||||
object: 'Jen',
|
||||
header: 'Report #12',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [{ author: 'Bruce', text: 'ut labore et dolore magna aliqua', id: '20', timestamp: '2019/3/1' }]
|
||||
},
|
||||
{
|
||||
id: '13',
|
||||
timestamp: '2019/4/12',
|
||||
local: true,
|
||||
from: 'John', // actor nickname
|
||||
object: 'Bob', // user nickname
|
||||
header: 'Report #13', // content
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Nick', text: 'Lorem ipsum', id: '21', timestamp: '2019/4/13' },
|
||||
{ author: 'Val', text: 'dolor sit amet', id: '22', timestamp: '2019/4/13' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '14',
|
||||
timestamp: '2019/4/1',
|
||||
local: true,
|
||||
from: 'Max',
|
||||
object: 'Vic',
|
||||
header: 'Report #14',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Tony', text: 'consectetur adipiscing elit', id: '23', timestamp: '2019/4/2' },
|
||||
{ author: 'Zac', text: 'sed do eiusmod tempor incididunt', id: '24', timestamp: '2019/4/3' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '15',
|
||||
timestamp: '2019/2/28',
|
||||
local: true,
|
||||
from: 'Tim',
|
||||
object: 'Jen',
|
||||
header: 'Report #15',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [{ author: 'Bruce', text: 'ut labore et dolore magna aliqua', id: '25', timestamp: '2019/3/1' }]
|
||||
},
|
||||
{
|
||||
id: '16',
|
||||
timestamp: '2019/4/12',
|
||||
local: true,
|
||||
from: 'John', // actor nickname
|
||||
object: 'Bob', // user nickname
|
||||
header: 'Report #16', // content
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Nick', text: 'Lorem ipsum', id: '26', timestamp: '2019/4/13' },
|
||||
{ author: 'Val', text: 'dolor sit amet', id: '27', timestamp: '2019/4/13' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '17',
|
||||
timestamp: '2019/4/1',
|
||||
local: true,
|
||||
from: 'Max',
|
||||
object: 'Vic',
|
||||
header: 'Report #17',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [
|
||||
{ author: 'Tony', text: 'consectetur adipiscing elit', id: '28', timestamp: '2019/4/2' },
|
||||
{ author: 'Zac', text: 'sed do eiusmod tempor incididunt', id: '29', timestamp: '2019/4/3' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '18',
|
||||
timestamp: '2019/2/28',
|
||||
local: true,
|
||||
from: 'Tim',
|
||||
object: 'Jen',
|
||||
header: 'Report #18',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
notes: [{ author: 'Bruce', text: 'ut labore et dolore magna aliqua', id: '30', timestamp: '2019/3/1' }]
|
||||
}
|
||||
]
|
|
@ -64,7 +64,8 @@ export default {
|
|||
clipboardDemo: 'Clipboard',
|
||||
i18n: 'I18n',
|
||||
externalLink: 'External Link',
|
||||
users: 'Users'
|
||||
users: 'Users',
|
||||
reports: 'Reports'
|
||||
},
|
||||
navbar: {
|
||||
logOut: 'Log Out',
|
||||
|
@ -200,5 +201,37 @@ export default {
|
|||
byStatus: 'By status',
|
||||
active: 'Active',
|
||||
deactivated: 'Deactivated'
|
||||
},
|
||||
reports: {
|
||||
reports: 'Reports',
|
||||
reply: 'Reply',
|
||||
from: 'From',
|
||||
showNotes: 'Show notes',
|
||||
newNote: 'New note',
|
||||
submit: 'Submit',
|
||||
confirmMsg: 'Are you sure you want to delete this note?',
|
||||
delete: 'Delete',
|
||||
cancel: 'Cancel',
|
||||
deleteCompleted: 'Delete comleted',
|
||||
deleteCanceled: 'Delete canceled',
|
||||
noNotes: 'No notes to display',
|
||||
changeState: 'Change state',
|
||||
changeScope: 'Change scope',
|
||||
resolve: 'Resolve',
|
||||
reopen: 'Reopen',
|
||||
close: 'Close',
|
||||
addSensitive: 'Add Sensitive flag',
|
||||
removeSensitive: 'Remove Sensitive flag',
|
||||
public: 'Make status public',
|
||||
private: 'Make status private',
|
||||
unlisted: 'Make status unlisted',
|
||||
sensitive: 'Sensitive',
|
||||
deleteStatus: 'Delete status'
|
||||
},
|
||||
reportsFilter: {
|
||||
inputPlaceholder: 'Select filter',
|
||||
open: 'Open',
|
||||
closed: 'Closed',
|
||||
resolved: 'Resolved'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,5 +64,17 @@ export const asyncRouterMap = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/reports',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/reports/index'),
|
||||
name: 'Reports',
|
||||
meta: { title: 'reports', icon: 'documentation', noCache: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
{ path: '*', redirect: '/404', hidden: true }
|
||||
]
|
||||
|
|
|
@ -3,6 +3,7 @@ import Vuex from 'vuex'
|
|||
import app from './modules/app'
|
||||
import errorLog from './modules/errorLog'
|
||||
import permission from './modules/permission'
|
||||
import reports from './modules/reports'
|
||||
import tagsView from './modules/tagsView'
|
||||
import user from './modules/user'
|
||||
import users from './modules/users'
|
||||
|
@ -15,6 +16,7 @@ const store = new Vuex.Store({
|
|||
app,
|
||||
errorLog,
|
||||
permission,
|
||||
reports,
|
||||
tagsView,
|
||||
user,
|
||||
users
|
||||
|
|
79
src/store/modules/reports.js
Normal file
79
src/store/modules/reports.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { changeState, changeStatusScope, deleteStatus, fetchReports, filterReports } from '@/api/reports'
|
||||
|
||||
const reports = {
|
||||
state: {
|
||||
fetchedReports: [],
|
||||
idOfLastReport: '',
|
||||
page_limit: 5,
|
||||
stateFilter: '',
|
||||
loading: true
|
||||
},
|
||||
mutations: {
|
||||
SET_LAST_REPORT_ID: (state, id) => {
|
||||
state.idOfLastReport = id
|
||||
},
|
||||
SET_LOADING: (state, status) => {
|
||||
state.loading = status
|
||||
},
|
||||
SET_REPORTS: (state, reports) => {
|
||||
state.fetchedReports = reports
|
||||
},
|
||||
SET_REPORTS_FILTER: (state, filter) => {
|
||||
state.stateFilter = filter
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async ChangeReportState({ commit, getters, state }, { reportState, reportId }) {
|
||||
const { data } = await changeState(reportState, reportId, getters.authHost, getters.token)
|
||||
const updatedReports = state.fetchedReports.map(report => report.id === reportId ? data : report)
|
||||
commit('SET_REPORTS', updatedReports)
|
||||
},
|
||||
async ChangeStatusScope({ commit, getters, state }, { statusId, isSensitive, visibility, reportId }) {
|
||||
const { data } = await changeStatusScope(statusId, isSensitive, visibility, getters.authHost, getters.token)
|
||||
const updatedReports = state.fetchedReports.map(report => {
|
||||
if (report.id === reportId) {
|
||||
const statuses = report.statuses.map(status => status.id === statusId ? data : status)
|
||||
return { ...report, statuses }
|
||||
} else {
|
||||
return report
|
||||
}
|
||||
})
|
||||
commit('SET_REPORTS', updatedReports)
|
||||
},
|
||||
ClearFetchedReports({ commit }) {
|
||||
commit('SET_REPORTS', [])
|
||||
commit('SET_LAST_REPORT_ID', '')
|
||||
},
|
||||
async DeleteStatus({ commit, getters, state }, { statusId, reportId }) {
|
||||
deleteStatus(statusId, getters.authHost, getters.token)
|
||||
const updatedReports = state.fetchedReports.map(report => {
|
||||
if (report.id === reportId) {
|
||||
const statuses = report.statuses.filter(status => status.id !== statusId)
|
||||
return { ...report, statuses }
|
||||
} else {
|
||||
return report
|
||||
}
|
||||
})
|
||||
commit('SET_REPORTS', updatedReports)
|
||||
},
|
||||
async FetchReports({ commit, getters, state }) {
|
||||
commit('SET_LOADING', true)
|
||||
|
||||
const response = state.stateFilter.length === 0
|
||||
? await fetchReports(state.page_limit, state.idOfLastReport, getters.authHost, getters.token)
|
||||
: await filterReports(state.stateFilter, state.page_limit, state.idOfLastReport, getters.authHost, getters.token)
|
||||
|
||||
const reports = state.fetchedReports.concat(response.data.reports)
|
||||
const id = reports.length > 0 ? reports[reports.length - 1].id : state.idOfLastReport
|
||||
|
||||
commit('SET_REPORTS', reports)
|
||||
commit('SET_LAST_REPORT_ID', id)
|
||||
commit('SET_LOADING', false)
|
||||
},
|
||||
SetFilter({ commit }, filter) {
|
||||
commit('SET_REPORTS_FILTER', filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default reports
|
|
@ -67,11 +67,11 @@ const users = {
|
|||
commit('SWAP_USER', updatedUser)
|
||||
},
|
||||
async FetchUsers({ commit, state, getters }, { page }) {
|
||||
commit('SET_LOADING', true)
|
||||
|
||||
const filters = Object.keys(state.filters).filter(filter => state.filters[filter]).join()
|
||||
const response = await fetchUsers(filters, getters.authHost, getters.token, page)
|
||||
|
||||
commit('SET_LOADING', true)
|
||||
|
||||
loadUsers(commit, page, response.data)
|
||||
},
|
||||
async RemoveTag({ commit, getters }, { users, tag }) {
|
||||
|
|
43
src/views/reports/components/ReportsFilter.vue
Normal file
43
src/views/reports/components/ReportsFilter.vue
Normal file
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<el-select
|
||||
v-model="filter"
|
||||
:placeholder="$t('reportsFilter.inputPlaceholder')"
|
||||
clearable
|
||||
class="select-field"
|
||||
@change="toggleFilters">
|
||||
<el-option value="open">{{ $t('reportsFilter.open') }}</el-option>
|
||||
<el-option value="closed">{{ $t('reportsFilter.closed') }}</el-option>
|
||||
<el-option value="resolved">{{ $t('reportsFilter.resolved') }}</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
filter: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleFilters() {
|
||||
this.$store.dispatch('SetFilter', this.$data.filter)
|
||||
this.$store.dispatch('ClearFetchedReports')
|
||||
this.$store.dispatch('FetchReports')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss' scoped>
|
||||
.select-field {
|
||||
width: 350px;
|
||||
}
|
||||
@media
|
||||
only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
.select-field {
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
140
src/views/reports/components/Statuses.vue
Normal file
140
src/views/reports/components/Statuses.vue
Normal file
|
@ -0,0 +1,140 @@
|
|||
<template>
|
||||
<el-collapse-item :title="getStatusesTitle(report.statuses)">
|
||||
<el-card v-for="status in report.statuses" :key="status.id" class="status-card">
|
||||
<div slot="header">
|
||||
<div class="status-header">
|
||||
<div class="status-account">
|
||||
<img :src="status.account.avatar" alt="avatar" class="status-avatar-img">
|
||||
<h3 class="status-account-name">{{ status.account.display_name }}</h3>
|
||||
</div>
|
||||
<div class="status-actions">
|
||||
<el-tag v-if="status.sensitive" type="warning" size="large">{{ $t('reports.sensitive') }}</el-tag>
|
||||
<el-tag size="large">{{ capitalizeFirstLetter(status.visibility) }}</el-tag>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button plain size="small" icon="el-icon-edit">{{ $t('reports.changeScope') }}<i class="el-icon-arrow-down el-icon--right"/></el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-if="!status.sensitive"
|
||||
@click.native="changeStatus(status.id, true, status.visibility, report.id)">
|
||||
{{ $t('reports.addSensitive') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.sensitive"
|
||||
@click.native="changeStatus(status.id, false, status.visibility, report.id)">
|
||||
{{ $t('reports.removeSensitive') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.visibility !== 'public'"
|
||||
@click.native="changeStatus(status.id, status.sensitive, 'public', report.id)">
|
||||
{{ $t('reports.public') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.visibility !== 'private'"
|
||||
@click.native="changeStatus(status.id, status.sensitive, 'private', report.id)">
|
||||
{{ $t('reports.private') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.visibility !== 'unlisted'"
|
||||
@click.native="changeStatus(status.id, status.sensitive, 'unlisted', report.id)">
|
||||
{{ $t('reports.unlisted') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
@click.native="deleteStatus(status.id, report.id)">
|
||||
{{ $t('reports.deleteStatus') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<a :href="status.account.url" target="_blank" class="account">
|
||||
@{{ status.account.acct }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="status-body">
|
||||
<span class="status-content">{{ status.content }}</span>
|
||||
<a :href="status.url" target="_blank" class="account">
|
||||
{{ parseTimestamp(status.created_at) }}
|
||||
</a>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-collapse-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
name: 'Statuses',
|
||||
props: {
|
||||
report: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
capitalizeFirstLetter(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
},
|
||||
changeStatus(statusId, isSensitive, visibility, reportId) {
|
||||
this.$store.dispatch('ChangeStatusScope', { statusId, isSensitive, visibility, reportId })
|
||||
},
|
||||
deleteStatus(statusId, reportId) {
|
||||
this.$confirm('Are you sure you want to delete this status?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$store.dispatch('DeleteStatus', { statusId, reportId })
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: 'Delete completed'
|
||||
})
|
||||
}).catch(() => {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: 'Delete canceled'
|
||||
})
|
||||
})
|
||||
},
|
||||
getStatusesTitle(statuses) {
|
||||
return `Reported statuses: ${statuses.length} item(s)`
|
||||
},
|
||||
parseTimestamp(timestamp) {
|
||||
return moment(timestamp).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss'>
|
||||
.account {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.status-account {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.status-avatar-img {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.status-account-name {
|
||||
margin: 0;
|
||||
}
|
||||
.status-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.status-content {
|
||||
font-size: 15px;
|
||||
}
|
||||
.status-card {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.status-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
}
|
||||
</style>
|
192
src/views/reports/components/TimelineItem.vue
Normal file
192
src/views/reports/components/TimelineItem.vue
Normal file
|
@ -0,0 +1,192 @@
|
|||
<template>
|
||||
<el-timeline-item :timestamp="parseTimestamp(report.created_at)" placement="top" class="timeline-item-container">
|
||||
<el-card>
|
||||
<div class="header-container">
|
||||
<h3 class="report-title">Report on {{ report.account.display_name }}</h3>
|
||||
<div>
|
||||
<el-tag :type="getStateType(report.state)" size="large">{{ capitalizeFirstLetter(report.state) }}</el-tag>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button plain size="small" icon="el-icon-edit">{{ $t('reports.changeState') }}<i class="el-icon-arrow-down el-icon--right"/></el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item v-if="report.state !== 'resolved'" @click.native="changeReportState('resolved', report.id)">{{ $t('reports.resolve') }}</el-dropdown-item>
|
||||
<el-dropdown-item v-if="report.state !== 'open'" @click.native="changeReportState('open', report.id)">{{ $t('reports.reopen') }}</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>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="id">ID: {{ report.id }}</h5>
|
||||
<div>
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">Account:</span>
|
||||
<img
|
||||
:src="report.account.avatar"
|
||||
alt="avatar"
|
||||
class="avatar-img">
|
||||
<a :href="report.account.url" target="_blank" class="account">
|
||||
<span class="report-row-value">{{ report.account.acct }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="report.content.length > 0">
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">Content:
|
||||
<span class="report-row-value">{{ report.content }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">Actor:</span>
|
||||
<img
|
||||
:src="report.actor.avatar"
|
||||
alt="avatar"
|
||||
class="avatar-img">
|
||||
<a :href="report.actor.url" target="_blank" class="account">
|
||||
<span class="report-row-value">{{ report.actor.acct }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="report.statuses.length > 0" class="statuses">
|
||||
<el-collapse>
|
||||
<statuses :report="report"/>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
import Statuses from './Statuses'
|
||||
|
||||
export default {
|
||||
name: 'TimelineItem',
|
||||
components: { Statuses },
|
||||
props: {
|
||||
report: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeReportState(reportState, reportId) {
|
||||
this.$store.dispatch('ChangeReportState', { reportState, reportId })
|
||||
},
|
||||
capitalizeFirstLetter(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
},
|
||||
getStateType(state) {
|
||||
switch (state) {
|
||||
case 'closed':
|
||||
return 'info'
|
||||
case 'resolved':
|
||||
return 'success'
|
||||
default:
|
||||
return 'primary'
|
||||
}
|
||||
},
|
||||
parseTimestamp(timestamp) {
|
||||
return moment(timestamp).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss'>
|
||||
.account {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.avatar-img {
|
||||
vertical-align: bottom;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.el-card__body {
|
||||
padding: 17px;
|
||||
}
|
||||
.el-card__header {
|
||||
background-color: #FAFAFA;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
.el-collapse {
|
||||
border-bottom: none;
|
||||
}
|
||||
.el-collapse-item__header {
|
||||
height: 46px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.el-collapse-item__content {
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
.el-icon-arrow-right {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.el-icon-close {
|
||||
padding: 10px 5px 10px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
h4 {
|
||||
margin: 0;
|
||||
height: 17px;
|
||||
}
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
height: 30px;
|
||||
}
|
||||
.id {
|
||||
color: gray;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
.line {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
border: 0.5px solid #EBEEF5;
|
||||
margin: 15px 0 15px;
|
||||
}
|
||||
.new-note {
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 17px;
|
||||
margin: 13px 0 7px;
|
||||
}
|
||||
}
|
||||
.note {
|
||||
box-shadow: 0 2px 5px 0 rgba(0,0,0,.1);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.no-notes {
|
||||
font-style: italic;
|
||||
color: gray;
|
||||
}
|
||||
.report-row-key {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.report-row-key {
|
||||
font-size: 14px;
|
||||
}
|
||||
.report-title {
|
||||
margin: 0;
|
||||
}
|
||||
.statuses {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.submit-button {
|
||||
display: block;
|
||||
margin: 7px 0 17px auto;
|
||||
}
|
||||
.timestamp {
|
||||
margin: 0;
|
||||
font-style: italic;
|
||||
color: gray;
|
||||
}
|
||||
@media
|
||||
only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
.confirm-message {
|
||||
width: 98%;
|
||||
}
|
||||
}
|
||||
</style>
|
83
src/views/reports/index.vue
Normal file
83
src/views/reports/index.vue
Normal file
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<div class="reports-container">
|
||||
<h1>{{ $t('reports.reports') }}</h1>
|
||||
<div class="filter-container">
|
||||
<reports-filter/>
|
||||
</div>
|
||||
<div class="block">
|
||||
<el-timeline class="timeline">
|
||||
<timeline-item v-loading="loading" v-for="report in reports" :report="report" :key="report.id"/>
|
||||
</el-timeline>
|
||||
<div v-if="reports.length === 0" class="no-reports-message">
|
||||
<p>There are no reports to display</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TimelineItem from './components/TimelineItem'
|
||||
import ReportsFilter from './components/ReportsFilter'
|
||||
|
||||
export default {
|
||||
components: { TimelineItem, ReportsFilter },
|
||||
computed: {
|
||||
loading() {
|
||||
return this.$store.state.users.loading
|
||||
},
|
||||
reports() {
|
||||
return this.$store.state.reports.fetchedReports
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('FetchReports')
|
||||
},
|
||||
created() {
|
||||
window.addEventListener('scroll', this.handleScroll)
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('scroll', this.handleScroll)
|
||||
},
|
||||
methods: {
|
||||
handleScroll(reports) {
|
||||
const bottomOfWindow = document.documentElement.scrollHeight - document.documentElement.scrollTop === document.documentElement.clientHeight
|
||||
if (bottomOfWindow) {
|
||||
this.$store.dispatch('FetchReports')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss' scoped>
|
||||
.reports-container {
|
||||
.el-timeline {
|
||||
margin: 45px 45px 45px 19px;
|
||||
padding: 0px;
|
||||
}
|
||||
.filter-container {
|
||||
margin: 22px 15px 22px 15px;
|
||||
padding-bottom: 0
|
||||
}
|
||||
h1 {
|
||||
margin: 22px 0 0 15px;
|
||||
}
|
||||
.no-reports-message {
|
||||
color: gray;
|
||||
margin-left: 19px
|
||||
|
||||
}
|
||||
}
|
||||
@media
|
||||
only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
.reports-container {
|
||||
h1 {
|
||||
margin: 7px 10px 7px 10px;
|
||||
}
|
||||
.filter-container {
|
||||
margin: 0 10px 7px 10px
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -120,6 +120,9 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<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"
|
||||
|
@ -155,6 +158,11 @@ export default {
|
|||
search: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
search: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
loading() {
|
||||
return this.$store.state.users.loading
|
||||
|
|
52
test/views/reports/index.test.js
Normal file
52
test/views/reports/index.test.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import Vuex from 'vuex'
|
||||
import { mount, createLocalVue, config } from '@vue/test-utils'
|
||||
import Element from 'element-ui'
|
||||
import Reports from '@/views/reports/index'
|
||||
import storeConfig from './store.conf'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import flushPromises from 'flush-promises'
|
||||
|
||||
config.mocks["$t"] = () => {}
|
||||
config.stubs['reports-filter'] = '<div />'
|
||||
config.stubs['timeline-item'] = '<div />'
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Element)
|
||||
|
||||
jest.mock('@/api/reports')
|
||||
|
||||
describe('Reports', () => {
|
||||
let store
|
||||
|
||||
beforeEach(() => {
|
||||
store = new Vuex.Store(cloneDeep(storeConfig))
|
||||
})
|
||||
|
||||
it('initially fetches reports', async (done) => {
|
||||
const wrapper = mount(Reports, {
|
||||
store,
|
||||
localVue
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
const initialReports = store.state.reports.fetchedReports.length
|
||||
expect(initialReports).toEqual(5)
|
||||
done()
|
||||
})
|
||||
|
||||
it('loads more reports on scroll', async (done) => {
|
||||
const wrapper = mount(Reports, {
|
||||
store,
|
||||
localVue
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
|
||||
window.dispatchEvent(new CustomEvent('scroll', { detail: 2000 }))
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(7)
|
||||
done()
|
||||
})
|
||||
})
|
79
test/views/reports/reportsFilter.test.js
Normal file
79
test/views/reports/reportsFilter.test.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
import Vuex from 'vuex'
|
||||
import { createLocalVue, config } from '@vue/test-utils'
|
||||
import Element from 'element-ui'
|
||||
import storeConfig from './store.conf'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import flushPromises from 'flush-promises'
|
||||
|
||||
config.mocks["$t"] = () => {}
|
||||
config.stubs.transition = false
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Element)
|
||||
|
||||
jest.mock('@/api/reports')
|
||||
|
||||
describe('Reports filter', () => {
|
||||
let store
|
||||
|
||||
beforeEach(async() => {
|
||||
store = new Vuex.Store(cloneDeep(storeConfig))
|
||||
store.dispatch('FetchReports')
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('shows open reports when "Open" filter is applied', async (done) => {
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
|
||||
store.dispatch('SetFilter', 'open')
|
||||
store.dispatch('ClearFetchedReports')
|
||||
store.dispatch('FetchReports')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(2)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
it('shows resolved reports when "Resolved" filter is applied', async (done) => {
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
|
||||
store.dispatch('SetFilter', 'resolved')
|
||||
store.dispatch('ClearFetchedReports')
|
||||
store.dispatch('FetchReports')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(2)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
it('shows closed reports when "Closed" filter is applied', async (done) => {
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
|
||||
store.dispatch('SetFilter', 'closed')
|
||||
store.dispatch('ClearFetchedReports')
|
||||
store.dispatch('FetchReports')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(3)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
it('shows all users after removing filters', async (done) => {
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
|
||||
store.dispatch('SetFilter', 'open')
|
||||
store.dispatch('ClearFetchedReports')
|
||||
store.dispatch('FetchReports')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(2)
|
||||
|
||||
store.dispatch('SetFilter', '')
|
||||
store.dispatch('ClearFetchedReports')
|
||||
store.dispatch('FetchReports')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
15
test/views/reports/store.conf.js
Normal file
15
test/views/reports/store.conf.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import app from '@/store/modules/app'
|
||||
import user from '@/store/modules/user'
|
||||
import users from '@/store/modules/users'
|
||||
import reports from '@/store/modules/reports'
|
||||
import getters from '@/store/getters'
|
||||
|
||||
export default {
|
||||
modules: {
|
||||
app,
|
||||
user,
|
||||
users,
|
||||
reports
|
||||
},
|
||||
getters
|
||||
}
|
157
test/views/reports/timelineItem.test.js
Normal file
157
test/views/reports/timelineItem.test.js
Normal file
|
@ -0,0 +1,157 @@
|
|||
import Vuex from 'vuex'
|
||||
import { mount, createLocalVue, config } from '@vue/test-utils'
|
||||
import Element from 'element-ui'
|
||||
import TimelineItem from '@/views/reports/components/TimelineItem'
|
||||
import storeConfig from './store.conf'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import flushPromises from 'flush-promises'
|
||||
|
||||
config.mocks["$t"] = () => {}
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Element)
|
||||
|
||||
jest.mock('@/api/reports')
|
||||
|
||||
describe('Report in a timeline', () => {
|
||||
let store
|
||||
|
||||
beforeEach(async() => {
|
||||
store = new Vuex.Store(cloneDeep(storeConfig))
|
||||
store.dispatch('FetchReports')
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('changes report state from open to resolved', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[0]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.state).toBe('open')
|
||||
|
||||
const button = wrapper.find(`li.el-dropdown-menu__item:nth-child(${1})`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[0].state).toBe('resolved')
|
||||
done()
|
||||
})
|
||||
|
||||
it('changes report state from open to closed', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[3]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.state).toBe('open')
|
||||
|
||||
const button = wrapper.find(`li.el-dropdown-menu__item:nth-child(${2})`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[3].state).toBe('closed')
|
||||
done()
|
||||
})
|
||||
|
||||
it('shows statuses', () => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
|
||||
const statuses = wrapper.findAll(`.status-card`)
|
||||
expect(statuses.length).toEqual(2)
|
||||
})
|
||||
|
||||
it('adds sensitive flag to a status', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.statuses[0].sensitive).toBe(false)
|
||||
|
||||
const button = wrapper.find(`.status-card li.el-dropdown-menu__item`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[4].statuses[0].sensitive).toEqual(true)
|
||||
done()
|
||||
})
|
||||
|
||||
it('removes sensitive flag to a status', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.statuses[1].sensitive).toBe(true)
|
||||
|
||||
const button = wrapper.find(`.status-card:nth-child(${2}) li.el-dropdown-menu__item`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[4].statuses[1].sensitive).toEqual(false)
|
||||
done()
|
||||
})
|
||||
|
||||
it('changes status visibility from public to unlisted', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.statuses[0].visibility).toBe('public')
|
||||
|
||||
const button = wrapper.find(`.status-card li.el-dropdown-menu__item:nth-child(${3})`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[4].statuses[0].visibility).toEqual('unlisted')
|
||||
done()
|
||||
})
|
||||
|
||||
it('changes status visibility from unlisted to private', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.statuses[1].visibility).toBe('unlisted')
|
||||
|
||||
const button = wrapper.find(`.status-card:nth-child(${2}) li.el-dropdown-menu__item:nth-child(${3})`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[4].statuses[1].visibility).toEqual('private')
|
||||
done()
|
||||
})
|
||||
|
||||
it('deletes a status', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
expect(report.statuses.length).toEqual(2)
|
||||
|
||||
store.dispatch('DeleteStatus', { statusId: '11', reportId: '7'})
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[4].statuses.length).toEqual(1)
|
||||
done()
|
||||
})
|
||||
})
|
13
yarn.lock
13
yarn.lock
|
@ -3446,10 +3446,10 @@ elegant-spinner@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
|
||||
integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
|
||||
|
||||
element-ui@2.4.11:
|
||||
version "2.4.11"
|
||||
resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.4.11.tgz#db6a2d37001b8fe5fff9f176fb58bb3908cfa9c9"
|
||||
integrity sha512-RtgK0t840NAFTajGMWvylzZRSX1EkZ7V4YgAoBxhv4TtkeMscLuk/IdYOzPdlQq6IN0byx1YVBxCX+u4yYkGvw==
|
||||
element-ui@^2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.7.0.tgz#6bfcdfa5c75bfc4cda835186f2a1f98b93cd5d14"
|
||||
integrity sha512-FalWzOmT/K4w4C/8tw2kGvzzQnRJ5MqEvSL5rEKNa081PFGIcUS9exyVpYrNPKF8ua/W6qaqrXPC6DQ8sNcmOQ==
|
||||
dependencies:
|
||||
async-validator "~1.8.1"
|
||||
babel-helper-vue-jsx-merge-props "^2.0.0"
|
||||
|
@ -6645,6 +6645,11 @@ mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@
|
|||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
moment@^2.24.0:
|
||||
version "2.24.0"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
||||
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
|
||||
|
||||
move-concurrently@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
|
||||
|
|
Loading…
Reference in a new issue