diff options
author | Martin Stockhammer <martin_s@apache.org> | 2020-11-09 00:35:36 +0100 |
---|---|---|
committer | Martin Stockhammer <martin_s@apache.org> | 2020-11-09 00:35:36 +0100 |
commit | ec828c8745637e7cc51ea4cb3a1717f0588f0f1a (patch) | |
tree | ff9f36e95e1856cdeefc572e9d3945bff8111aa5 /archiva-modules | |
parent | 0e2187bf0efde67e9e51289f224183164b570ca0 (diff) | |
download | archiva-ec828c8745637e7cc51ea4cb3a1717f0588f0f1a.tar.gz archiva-ec828c8745637e7cc51ea4cb3a1717f0588f0f1a.zip |
Components for new angular app
Diffstat (limited to 'archiva-modules')
15 files changed, 551 insertions, 105 deletions
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts index 9e351627c..f3ddcc7f9 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts @@ -43,6 +43,8 @@ import { ManageUsersListComponent } from './modules/user/users/manage-users-list import { ManageUsersAddComponent } from './modules/user/users/manage-users-add/manage-users-add.component'; import { NgbPaginationModule, NgbTooltipModule} from "@ng-bootstrap/ng-bootstrap"; import { PaginatedEntitiesComponent } from './modules/general/paginated-entities/paginated-entities.component'; +import { SortedTableHeaderComponent } from './modules/general/sorted-table-header/sorted-table-header.component'; +import { SortedTableHeaderRowComponent } from './modules/general/sorted-table-header-row/sorted-table-header-row.component'; @NgModule({ @@ -65,6 +67,8 @@ import { PaginatedEntitiesComponent } from './modules/general/paginated-entities ManageUsersListComponent, ManageUsersAddComponent, PaginatedEntitiesComponent, + SortedTableHeaderComponent, + SortedTableHeaderRowComponent, ], imports: [ BrowserModule, diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/entity-service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/entity-service.ts index a9e7ce8b0..bdf649edc 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/entity-service.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/entity-service.ts @@ -24,5 +24,5 @@ import {Observable} from "rxjs"; * @typeparam T The type of the entity that is returned from the service */ export interface EntityService<T> { - (searchTerm:string,offset:number,limit:number,orderBy:string,order:string):Observable<PagedResult<T>> + (searchTerm:string,offset:number,limit:number,orderBy:string[],order:string):Observable<PagedResult<T>> } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/field-toggle.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/field-toggle.ts new file mode 100644 index 000000000..e0d0e3f0a --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/field-toggle.ts @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export interface FieldToggle { + toggleField(fieldName: string[]); +} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/paginated-entities/paginated-entities.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/paginated-entities/paginated-entities.component.ts index 2e5123bb0..1d0cba1e2 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/paginated-entities/paginated-entities.component.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/paginated-entities/paginated-entities.component.ts @@ -16,12 +16,11 @@ * under the License. */ -import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core'; +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {merge, Observable, Subject} from "rxjs"; -import {UserInfo} from "../../../model/user-info"; -import {TranslateService} from "@ngx-translate/core"; import {debounceTime, distinctUntilChanged, map, mergeMap, pluck, share, startWith} from "rxjs/operators"; import {EntityService} from "../../../model/entity-service"; +import {FieldToggle} from "../../../model/field-toggle"; /** @@ -44,95 +43,154 @@ import {EntityService} from "../../../model/entity-service"; * @typeparam T The type of the retrieved entity elements. */ @Component({ - selector: 'app-paginated-entities', - templateUrl: './paginated-entities.component.html', - styleUrls: ['./paginated-entities.component.scss'] + selector: 'app-paginated-entities', + templateUrl: './paginated-entities.component.html', + styleUrls: ['./paginated-entities.component.scss'] }) -export class PaginatedEntitiesComponent<T> implements OnInit { - - /** - * This must be set, if you use the component. This service retrieves the entity data. - */ - @Input() service : EntityService<T>; - - /** - * The number of elements per page retrieved - */ - @Input() pageSize = 10; - - /** - * Pagination controls - */ - @Input() pagination = { - maxSize:5, - rotate:true, - boundaryLinks:true, - ellipses:false - } - - /** - * The current page that is selected - */ - page = 1; - /** - * The current search term entered in the search field - */ - searchTerm: string; - - /** - * Event thrown, if the page value changes - */ - @Output() pageEvent : EventEmitter<number> = new EventEmitter<number>(); - /** - * Event thrown, if the search term changes - */ - @Output() searchTermEvent: EventEmitter<string> = new EventEmitter<string>(); - - /** - * The total number of elements available for the given search term - */ - total$: Observable<number>; - /** - * The entity items retrieved from the service - */ - items$: Observable<T[]>; - - private pageStream: Subject<number> = new Subject<number>(); - private searchTermStream: Subject<string> = new Subject<string>(); - - constructor() { } - - ngOnInit(): void { - // We combine the sources for the page and the search input field to a observable 'source' - const pageSource = this.pageStream.pipe(map(pageNumber => { - return {search: this.searchTerm, page: pageNumber} - })); - const searchSource = this.searchTermStream.pipe( - debounceTime(1000), - distinctUntilChanged(), - map(searchTerm => { - this.searchTerm = searchTerm; - return {search: searchTerm, page: 1} +export class PaginatedEntitiesComponent<T> implements OnInit, FieldToggle { + + /** + * This must be set, if you use the component. This service retrieves the entity data. + */ + @Input() service: EntityService<T>; + + /** + * The number of elements per page retrieved + */ + @Input() pageSize = 10; + + /** + * Two-Way-Binding attribute for sorting field + */ + @Input() sortField = []; + /** + * Two-Way Binding attribute for sort order + */ + @Input() sortOrder = "asc"; + + /** + * Pagination controls + */ + @Input() pagination = { + maxSize: 5, + rotate: true, + boundaryLinks: true, + ellipses: false + } + + /** + * The current page that is selected + */ + page = 1; + /** + * The current search term entered in the search field + */ + searchTerm: string; + + /** + * Event thrown, if the page value changes + */ + @Output() pageChange: EventEmitter<number> = new EventEmitter<number>(); + /** + * Event thrown, if the search term changes + */ + @Output() searchTermChange: EventEmitter<string> = new EventEmitter<string>(); + + @Output() sortFieldChange: EventEmitter<string[]> = new EventEmitter<string[]>(); + + @Output() sortOrderChange: EventEmitter<string> = new EventEmitter<string>(); + + /** + * The total number of elements available for the given search term + */ + total$: Observable<number>; + /** + * The entity items retrieved from the service + */ + items$: Observable<T[]>; + + private pageStream: Subject<number> = new Subject<number>(); + private searchTermStream: Subject<string> = new Subject<string>(); + + constructor() { + } + + ngOnInit(): void { + // We combine the sources for the page and the search input field to a observable 'source' + const pageSource = this.pageStream.pipe(map(pageNumber => { + return {search: this.searchTerm, page: pageNumber} })); - const source = merge(pageSource, searchSource).pipe( - startWith({search: this.searchTerm, page: this.page}), - mergeMap((params: { search: string, page: number }) => { - return this.service(params.search, (params.page - 1) * this.pageSize, this.pageSize, "", "asc"); - }),share()); - this.total$ = source.pipe(pluck('pagination','totalCount')); - this.items$ = source.pipe(pluck('data')); - } - - search(terms: string) { - // console.log("Keystroke " + terms); - this.searchTermEvent.emit(terms); - this.searchTermStream.next(terms) - } - - changePage(pageNumber : number) { - // console.log("Page change " +pageNumber); - this.pageEvent.emit(pageNumber); - this.pageStream.next(pageNumber); - } + const searchSource = this.searchTermStream.pipe( + debounceTime(1000), + distinctUntilChanged(), + map(searchTerm => { + this.searchTerm = searchTerm; + return {search: searchTerm, page: 1} + })); + const source = merge(pageSource, searchSource).pipe( + startWith({search: this.searchTerm, page: this.page}), + mergeMap((params: { search: string, page: number }) => { + return this.service(params.search, (params.page - 1) * this.pageSize, this.pageSize, this.sortField, this.sortOrder); + }), share()); + this.total$ = source.pipe(pluck('pagination', 'totalCount')); + this.items$ = source.pipe(pluck('data')); + } + + search(terms: string) { + // console.log("Keystroke " + terms); + this.searchTermChange.emit(terms); + this.searchTermStream.next(terms) + } + + changePage(pageNumber: number) { + // console.log("Page change " +pageNumber); + this.pageChange.emit(pageNumber); + this.pageStream.next(pageNumber); + } + + private compareArrays(a1: string[], a2: string[]) { + let i = a1.length; + while (i--) { + if (a1[i] !== a2[i]) return false; + } + return true + } + + toggleSortField(fieldName: string) { + this.toggleField([fieldName]); + } + + toggleField(fieldArray: string[]) { + console.log("Changing sort field " + fieldArray); + let sortOrderChanged: boolean = false; + let sortFieldChanged: boolean = false; + if (!this.compareArrays(this.sortField, fieldArray)) { + console.log("Fields differ: " + this.sortField + " - " + fieldArray); + this.sortField = fieldArray; + if (this.sortOrder != 'asc') { + this.sortOrder = 'asc'; + sortOrderChanged = true; + } + sortFieldChanged = true; + } else { + if (this.sortOrder == "asc") { + this.sortOrder = "desc"; + } else { + this.sortOrder = "asc"; + } + console.log("Toggled sort order: " + this.sortOrder); + sortOrderChanged = true; + } + if (sortOrderChanged) { + console.log("Sort order changed: "+this.sortOrder) + this.sortOrderChange.emit(this.sortOrder); + } + if (sortFieldChanged) { + this.sortFieldChange.emit(this.sortField); + } + if (sortFieldChanged || sortOrderChanged) { + this.changePage(1); + } + } } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.html new file mode 100644 index 000000000..ff210c900 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.html @@ -0,0 +1,19 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --> + +<ng-content ></ng-content> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.scss b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.scss new file mode 100644 index 000000000..343c3b1c0 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.scss @@ -0,0 +1,18 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.spec.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.spec.ts new file mode 100644 index 000000000..a8cb6fb56 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.spec.ts @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SortedTableHeaderRowComponent } from './sorted-table-header-row.component'; + +describe('SortedTableHeaderRowComponent', () => { + let component: SortedTableHeaderRowComponent; + let fixture: ComponentFixture<SortedTableHeaderRowComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SortedTableHeaderRowComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SortedTableHeaderRowComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.ts new file mode 100644 index 000000000..4de4c74ff --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header-row/sorted-table-header-row.component.ts @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + AfterViewChecked, AfterViewInit, + Component, + Input, + OnInit, + QueryList, + TemplateRef, + ViewChild, + ViewChildren, + ViewContainerRef, + ContentChildren, AfterContentInit, AfterContentChecked, ChangeDetectorRef, Output, EventEmitter +} from '@angular/core'; +import {FieldToggle} from "../../../model/field-toggle"; +import {SortedTableHeaderComponent} from "../sorted-table-header/sorted-table-header.component"; +import { delay, startWith } from 'rxjs/operators'; + +@Component({ + selector: 'tr[sorted]', + templateUrl: './sorted-table-header-row.component.html', + styleUrls: ['./sorted-table-header-row.component.scss'] +}) +export class SortedTableHeaderRowComponent implements OnInit, AfterViewInit, AfterContentInit, AfterContentChecked { + + @Input() sortFieldEmitter: EventEmitter<string[]>; + @Input() sortOrderEmitter: EventEmitter<string>; + @Input() sortFields: string[]; + @Input() sortOrder: string; + @Input() toggleObserver: FieldToggle; + + @ContentChildren(SortedTableHeaderComponent, { descendants: true }) contentChilds: QueryList<SortedTableHeaderComponent>; + + constructor(private readonly viewContainer: ViewContainerRef) { + } + + ngAfterContentChecked(): void { + + + } + + ngAfterContentInit(): void { + this.contentChilds.changes.pipe(startWith(this.contentChilds), delay(0)).subscribe(() => { + this.contentChilds.forEach((colComponent, index) => { + console.log("Children " + colComponent); + colComponent.registerSortFieldEmitter(this.sortFieldEmitter); + colComponent.registerSortOrderEmitter(this.sortOrderEmitter); + colComponent.sortOrder = this.sortOrder; + colComponent.currentFieldArray = this.sortFields; + colComponent.toggleObserver = this.toggleObserver; + }); + }); + + } + + ngOnInit(): void { + } + + ngAfterViewInit(): void { + + } + +} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.html new file mode 100644 index 000000000..ff896b26b --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.html @@ -0,0 +1,24 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --> + +<ng-template #content> +<th scope="col" (click)="toggleSortField()"> + <ng-container *ngIf="contentText!=null && contentText!=''" >{{contentText | translate}}</ng-container> + <ng-content></ng-content> + <span *ngIf="sortCheck()" class="fas" [ngClass]="isAscending()?'fa-sort-alpha-up':'fa-sort-alpha-down'"></span></th> +</ng-template>
\ No newline at end of file diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.scss b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.scss new file mode 100644 index 000000000..343c3b1c0 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.scss @@ -0,0 +1,18 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.spec.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.spec.ts new file mode 100644 index 000000000..96bbe92fc --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.spec.ts @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SortedTableHeaderComponent } from './sorted-table-header.component'; + +describe('SortedTableHeaderComponent', () => { + let component: SortedTableHeaderComponent; + let fixture: ComponentFixture<SortedTableHeaderComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SortedTableHeaderComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SortedTableHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.ts new file mode 100644 index 000000000..bc297d576 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sorted-table-header/sorted-table-header.component.ts @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + Component, + OnInit, + Input, + ViewContainerRef, + ViewChild, + TemplateRef, + ChangeDetectorRef, + AfterViewChecked, EventEmitter, Output +} from '@angular/core'; +import {FieldToggle} from "../../../model/field-toggle"; +import { ChangeDetectionStrategy } from '@angular/core'; + +@Component({ + host: { style: 'display:none' }, + selector: 'app-th-sorted', + templateUrl: './sorted-table-header.component.html', + styleUrls: ['./sorted-table-header.component.scss'] +}) +export class SortedTableHeaderComponent implements OnInit, AfterViewChecked { + + @Input() fieldArray: string[]; + currentFieldArray: string[]; + sortOrder: string; + toggleObserver: FieldToggle; + @Input() contentText:string; + + @ViewChild('content', { static: true }) content: TemplateRef<{}>; + + + constructor(private readonly viewContainer: ViewContainerRef) { } + + ngOnInit(): void { + this.viewContainer.createEmbeddedView(this.content); + } + ngAfterViewChecked() { + } + + toggleSortField() { + console.log("Toggling sort field " + this.fieldArray); + this.toggleObserver.toggleField(this.fieldArray); + } + + private compareArrays(a1: string[], a2: string[]) { + if (a1==null || a2==null) { + return false; + } + let i = a1.length; + while (i--) { + if (a1[i] !== a2[i]) return false; + } + return true + } + + sortCheck() { + return this.compareArrays(this.fieldArray, this.currentFieldArray); + } + + isAscending() :boolean { + console.log("Is ascending: " + this.sortOrder); + return this.sortOrder == 'asc'; + } + + registerSortOrderEmitter(emitter : EventEmitter<string>) { + emitter.subscribe((field) => this.sortOrder = field); + } + + registerSortFieldEmitter(emitter : EventEmitter<string[]>) { + emitter.subscribe((field) => this.currentFieldArray = field); + } + +} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html index 56b02aee5..bfdc6fbaa 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html @@ -17,24 +17,28 @@ ~ under the License. --> -<app-paginated-entities [service]="service" pageSize="5"We #parent> +<app-paginated-entities [service]="service" pageSize="5" [(sortField)]="sortField" [(sortOrder)]="sortOrder" + #parent> <table class="table table-striped table-bordered"> <thead class="thead-light"> - <tr> - <th scope="col">{{'users.list.table.head.user_id' | translate}}</th> - <th scope="col">{{'users.list.table.head.fullName' | translate}}</th> - <th scope="col">{{'users.list.table.head.email' | translate}}</th> - <th scope="col"><span class="fas fa-check" placement="top" - [ngbTooltip]="heads.validated" [attr.aria-label]="heads.validated"></span> - </th> + <tr sorted [sortFieldEmitter]="parent.sortFieldChange" [sortOrder]="sortOrder" [sortFields]="sortField" + [sortOrderEmitter]="parent.sortOrderChange" [toggleObserver]="parent" > + <app-th-sorted [fieldArray]="['user_id']" contentText="users.list.table.head.user_id"></app-th-sorted> + <app-th-sorted contentText="users.list.table.head.fullName" [fieldArray]="['fullName']" ></app-th-sorted> + <app-th-sorted contentText="users.list.table.head.email" [fieldArray]="['email']"></app-th-sorted> + <app-th-sorted [fieldArray]="['validated','user_id']"> + <span class="fas fa-check" placement="top" + [ngbTooltip]="heads.validated" [attr.aria-label]="heads.validated"> + </span> + </app-th-sorted> <th scope="col"><span class="fas fa-lock" placement="top" [ngbTooltip]="heads.locked" [attr.aria-label]="heads.locked"></span></th> <th scope="col"><span class="fa fa-chevron-circle-right" placement="top" [ngbTooltip]="heads.pwchange" [attr.aria-label]="heads.pwchange"></span> </th> <th scope="col">{{'users.list.table.head.lastLogin' | translate}}</th> - <th scope="col">{{'users.list.table.head.created' | translate}}</th> + <app-th-sorted contentText="users.list.table.head.created" [fieldArray]="['created']" ></app-th-sorted> <th scope="col">{{'users.list.table.head.lastPwChange' | translate}}</th> </tr> </thead> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.ts index 8e91dd32e..d2a073a2f 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.ts @@ -25,7 +25,6 @@ import {EntityService} from "../../../../model/entity-service"; import {Observable, of} from "rxjs"; import {PagedResult} from "../../../../model/paged-result"; - @Component({ selector: 'app-manage-users-list', templateUrl: './manage-users-list.component.html', @@ -35,10 +34,13 @@ export class ManageUsersListComponent implements OnInit { @Input() heads: any; service : EntityService<UserInfo>; + sortField = ["user_id"]; + sortOrder = "asc"; constructor(private translator: TranslateService, private userService : UserService) { - this.service = function (searchTerm: string, offset: number, limit: number, orderBy: string, order: string) : Observable<PagedResult<UserInfo>> { + this.service = function (searchTerm: string, offset: number, limit: number, orderBy: string[], order: string) : Observable<PagedResult<UserInfo>> { + console.log("Retrieving data " + searchTerm + "," + offset + "," + limit + "," + orderBy + "," + order); return userService.query(searchTerm, offset, limit, orderBy, order); } @@ -53,9 +55,29 @@ export class ManageUsersListComponent implements OnInit { this.heads[suffix] = this.translator.instant('users.list.table.head.' + suffix); } }); + } + + changeSortOrder(order:string) { + if (this.sortOrder!=order) { + this.sortOrder = order; + } + } + private compareArrays(a1: string[], a2: string[]) { + let i = a1.length; + while (i--) { + if (a1[i] !== a2[i]) return false; + } + return true + } + + sortCheck(fieldArray:string[]) { + return this.compareArrays(this.sortField, fieldArray); + } + isAscending() : boolean { + return this.sortOrder == "asc"; } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts index bdeeafac4..68f9857fc 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts @@ -258,12 +258,15 @@ export class UserService implements OnInit, OnDestroy { this.authenticated = false; } - public query(searchTerm : string, offset : number = 0, limit : number = 10, orderBy : string = 'user_id', order: string = 'asc') : Observable<PagedResult<UserInfo>> { + public query(searchTerm : string, offset : number = 0, limit : number = 10, orderBy : string[] = ['user_id'], order: string = 'asc') : Observable<PagedResult<UserInfo>> { console.log("getUserList " + searchTerm + "," + offset + "," + limit + "," + orderBy + "," + order); if (searchTerm==null) { searchTerm="" } - return this.rest.executeRestCall<PagedResult<UserInfo>>("get", "redback", "users", {'q':searchTerm, 'offset':offset,'limit':limit}); + if (orderBy==null || orderBy.length==0) { + orderBy = ['user_id']; + } + return this.rest.executeRestCall<PagedResult<UserInfo>>("get", "redback", "users", {'q':searchTerm, 'offset':offset,'limit':limit,'orderBy':orderBy,'order':order}); } } |