Add users search filter
This commit is contained in:
parent
791645e620
commit
24b9417cec
|
@ -28,7 +28,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.loadSort()
|
this.initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected loadData () {
|
protected loadData () {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -34,7 +34,7 @@ export class JobsListComponent extends RestTable implements OnInit {
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.loadJobState()
|
this.loadJobState()
|
||||||
this.loadSort()
|
this.initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
onJobStateChanged () {
|
onJobStateChanged () {
|
||||||
|
|
|
@ -57,7 +57,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.loadSort()
|
this.initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
openModerationCommentModal (videoAbuse: VideoAbuse) {
|
openModerationCommentModal (videoAbuse: VideoAbuse) {
|
||||||
|
|
|
@ -39,7 +39,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.loadSort()
|
this.initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
getVideoUrl (videoBlacklist: VideoBlacklist) {
|
getVideoUrl (videoBlacklist: VideoBlacklist) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -31,7 +31,7 @@ export class MyAccountOwnershipComponent extends RestTable implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.loadSort()
|
this.initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected loadData () {
|
protected loadData () {
|
||||||
|
|
|
@ -27,7 +27,7 @@ export class MyAccountVideoImportsComponent extends RestTable implements OnInit
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.loadSort()
|
this.initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
isVideoImportSuccess (videoImport: VideoImport) {
|
isVideoImportSuccess (videoImport: VideoImport) {
|
||||||
|
|
|
@ -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>
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -180,7 +180,7 @@ describe('Test users', function () {
|
||||||
it('Should be able to upload a video again')
|
it('Should be able to upload a video again')
|
||||||
|
|
||||||
it('Should be able to create a new user', async function () {
|
it('Should be able to create a new user', async function () {
|
||||||
await createUser(server.url, accessToken, user.username,user.password, 2 * 1024 * 1024)
|
await createUser(server.url, accessToken, user.username, user.password, 2 * 1024 * 1024)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should be able to login with this user', async function () {
|
it('Should be able to login with this user', async function () {
|
||||||
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue