Add users search filter

This commit is contained in:
Chocobozzz 2018-10-08 15:51:38 +02:00
parent 791645e620
commit 24b9417cec
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
17 changed files with 100 additions and 17 deletions

View File

@ -28,7 +28,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
} }
ngOnInit () { ngOnInit () {
this.loadSort() this.initialize()
} }
protected loadData () { protected loadData () {

View File

@ -29,7 +29,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
} }
ngOnInit () { ngOnInit () {
this.loadSort() this.initialize()
} }
async removeFollowing (follow: ActorFollow) { async removeFollowing (follow: ActorFollow) {

View File

@ -34,7 +34,7 @@ export class JobsListComponent extends RestTable implements OnInit {
ngOnInit () { ngOnInit () {
this.loadJobState() this.loadJobState()
this.loadSort() this.initialize()
} }
onJobStateChanged () { onJobStateChanged () {

View File

@ -57,7 +57,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
} }
ngOnInit () { ngOnInit () {
this.loadSort() this.initialize()
} }
openModerationCommentModal (videoAbuse: VideoAbuse) { openModerationCommentModal (videoAbuse: VideoAbuse) {

View File

@ -39,7 +39,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
} }
ngOnInit () { ngOnInit () {
this.loadSort() this.initialize()
} }
getVideoUrl (videoBlacklist: VideoBlacklist) { getVideoUrl (videoBlacklist: VideoBlacklist) {

View File

@ -25,6 +25,7 @@
<div> <div>
<input <input
type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
(keyup)="onSearch($event.target.value)"
> >
</div> </div>
</div> </div>

View File

@ -35,7 +35,7 @@ export class UserListComponent extends RestTable implements OnInit {
} }
ngOnInit () { ngOnInit () {
this.loadSort() this.initialize()
this.bulkUserActions = [ this.bulkUserActions = [
{ {
@ -58,7 +58,7 @@ export class UserListComponent extends RestTable implements OnInit {
protected loadData () { protected loadData () {
this.selectedUsers = [] this.selectedUsers = []
this.userService.getUsers(this.pagination, this.sort) this.userService.getUsers(this.pagination, this.sort, this.search)
.subscribe( .subscribe(
resultList => { resultList => {
this.users = resultList.data this.users = resultList.data

View File

@ -31,7 +31,7 @@ export class MyAccountOwnershipComponent extends RestTable implements OnInit {
} }
ngOnInit () { ngOnInit () {
this.loadSort() this.initialize()
} }
protected loadData () { protected loadData () {

View File

@ -27,7 +27,7 @@ export class MyAccountVideoImportsComponent extends RestTable implements OnInit
} }
ngOnInit () { ngOnInit () {
this.loadSort() this.initialize()
} }
isVideoImportSuccess (videoImport: VideoImport) { isVideoImportSuccess (videoImport: VideoImport) {

View File

@ -1,5 +1,5 @@
<ng-container *ngIf="user && userActions.length !== 0"> <ng-container *ngIf="user && userActions.length !== 0">
<my-user-ban-modal #userBanModal (userBanned)="onUserBanned()"></my-user-ban-modal> <my-user-ban-modal #userBanModal (userBanned)="onUserBanned()"></my-user-ban-modal>
<my-action-dropdown [actions]="userActions" [entry]="user" [buttonSize]="buttonSize"></my-action-dropdown> <my-action-dropdown [actions]="userActions" [entry]="user" [buttonSize]="buttonSize" [placement]="placement"></my-action-dropdown>
</ng-container> </ng-container>

View File

@ -17,6 +17,7 @@ export class UserModerationDropdownComponent implements OnInit {
@Input() user: User @Input() user: User
@Input() buttonSize: 'normal' | 'small' = 'normal' @Input() buttonSize: 'normal' | 'small' = 'normal'
@Input() placement = 'left'
@Output() userChanged = new EventEmitter() @Output() userChanged = new EventEmitter()
@Output() userDeleted = new EventEmitter() @Output() userDeleted = new EventEmitter()

View File

@ -1,8 +1,9 @@
import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
import { LazyLoadEvent } from 'primeng/components/common/lazyloadevent' import { LazyLoadEvent } from 'primeng/components/common/lazyloadevent'
import { SortMeta } from 'primeng/components/common/sortmeta' import { SortMeta } from 'primeng/components/common/sortmeta'
import { RestPagination } from './rest-pagination' import { RestPagination } from './rest-pagination'
import { Subject } from 'rxjs'
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
export abstract class RestTable { export abstract class RestTable {
@ -11,10 +12,17 @@ export abstract class RestTable {
abstract sort: SortMeta abstract sort: SortMeta
abstract pagination: RestPagination abstract pagination: RestPagination
protected search: string
private searchStream: Subject<string>
private sortLocalStorageKey = 'rest-table-sort-' + this.constructor.name private sortLocalStorageKey = 'rest-table-sort-' + this.constructor.name
protected abstract loadData (): void protected abstract loadData (): void
initialize () {
this.loadSort()
this.initSearch()
}
loadSort () { loadSort () {
const result = peertubeLocalStorage.getItem(this.sortLocalStorageKey) const result = peertubeLocalStorage.getItem(this.sortLocalStorageKey)
@ -46,4 +54,21 @@ export abstract class RestTable {
peertubeLocalStorage.setItem(this.sortLocalStorageKey, JSON.stringify(this.sort)) peertubeLocalStorage.setItem(this.sortLocalStorageKey, JSON.stringify(this.sort))
} }
initSearch () {
this.searchStream = new Subject()
this.searchStream
.pipe(
debounceTime(400),
distinctUntilChanged()
)
.subscribe(search => {
this.search = search
this.loadData()
})
}
onSearch (search: string) {
this.searchStream.next(search)
}
} }

View File

@ -158,10 +158,12 @@ export class UserService {
.pipe(catchError(err => this.restExtractor.handleError(err))) .pipe(catchError(err => this.restExtractor.handleError(err)))
} }
getUsers (pagination: RestPagination, sort: SortMeta): Observable<ResultList<User>> { getUsers (pagination: RestPagination, sort: SortMeta, search?: string): Observable<ResultList<User>> {
let params = new HttpParams() let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort) params = this.restService.addRestGetParams(params, pagination, sort)
if (search) params = params.append('search', search)
return this.authHttp.get<ResultList<User>>(UserService.BASE_USERS_URL, { params }) return this.authHttp.get<ResultList<User>>(UserService.BASE_USERS_URL, { params })
.pipe( .pipe(
map(res => this.restExtractor.convertResultListDateToHuman(res)), map(res => this.restExtractor.convertResultListDateToHuman(res)),

View File

@ -238,7 +238,7 @@ async function autocompleteUsers (req: express.Request, res: express.Response, n
} }
async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) { async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort) const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.search)
return res.json(getFormattedObjects(resultList.data, resultList.total)) return res.json(getFormattedObjects(resultList.data, resultList.total))
} }

View File

@ -181,7 +181,25 @@ export class UserModel extends Model<UserModel> {
return this.count() return this.count()
} }
static listForApi (start: number, count: number, sort: string) { static listForApi (start: number, count: number, sort: string, search?: string) {
let where = undefined
if (search) {
where = {
[Sequelize.Op.or]: [
{
email: {
[Sequelize.Op.iLike]: '%' + search + '%'
}
},
{
username: {
[ Sequelize.Op.iLike ]: '%' + search + '%'
}
}
]
}
}
const query = { const query = {
attributes: { attributes: {
include: [ include: [
@ -204,7 +222,8 @@ export class UserModel extends Model<UserModel> {
}, },
offset: start, offset: start,
limit: count, limit: count,
order: getSort(sort) order: getSort(sort),
where
} }
return UserModel.findAndCountAll(query) return UserModel.findAndCountAll(query)

View File

@ -322,6 +322,40 @@ describe('Test users', function () {
expect(users[ 1 ].nsfwPolicy).to.equal('display') expect(users[ 1 ].nsfwPolicy).to.equal('display')
}) })
it('Should search user by username', async function () {
const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'oot')
const users = res.body.data as User[]
expect(res.body.total).to.equal(1)
expect(users.length).to.equal(1)
expect(users[ 0 ].username).to.equal('root')
})
it('Should search user by email', async function () {
{
const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'r_1@exam')
const users = res.body.data as User[]
expect(res.body.total).to.equal(1)
expect(users.length).to.equal(1)
expect(users[ 0 ].username).to.equal('user_1')
expect(users[ 0 ].email).to.equal('user_1@example.com')
}
{
const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'example')
const users = res.body.data as User[]
expect(res.body.total).to.equal(2)
expect(users.length).to.equal(2)
expect(users[ 0 ].username).to.equal('root')
expect(users[ 1 ].username).to.equal('user_1')
}
})
it('Should update my password', async function () { it('Should update my password', async function () {
await updateMyUser({ await updateMyUser({
url: server.url, url: server.url,

View File

@ -112,7 +112,7 @@ function getUsersList (url: string, accessToken: string) {
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
} }
function getUsersListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string) { function getUsersListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string, search?: string) {
const path = '/api/v1/users' const path = '/api/v1/users'
return request(url) return request(url)
@ -120,6 +120,7 @@ function getUsersListPaginationAndSort (url: string, accessToken: string, start:
.query({ start }) .query({ start })
.query({ count }) .query({ count })
.query({ sort }) .query({ sort })
.query({ search })
.set('Accept', 'application/json') .set('Accept', 'application/json')
.set('Authorization', 'Bearer ' + accessToken) .set('Authorization', 'Bearer ' + accessToken)
.expect(200) .expect(200)