Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/members'
Browse files Browse the repository at this point in the history
  • Loading branch information
auumgn committed Apr 3, 2024
2 parents 1214a6a + 5e9ac84 commit b72dd9b
Show file tree
Hide file tree
Showing 12 changed files with 387 additions and 31 deletions.
1 change: 0 additions & 1 deletion ui/src/app/affiliation/affiliations.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export class AffiliationsComponent implements OnInit, OnDestroy {
importEventSubscriber: Subscription | undefined
notificationSubscription: Subscription | undefined
routeData: Subscription | undefined
links: any
totalItems: any
itemsPerPage: any
page = 1
Expand Down
2 changes: 0 additions & 2 deletions ui/src/app/affiliation/send-notifications-dialog.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import { EventService } from '../shared/service/event.service'
import { AlertService } from '../shared/service/alert.service'
import { IUser } from '../user/model/user.model'
import { faPaperPlane } from '@fortawesome/free-solid-svg-icons'
import { IMember } from '../member/model/member.model'
import { MemberService } from '../member/service/member.service'
import { LanguageService } from '../shared/service/language.service'
import { AccountService } from '../account'
import { IAccount } from '../account/model/account.model'
import { AlertType, EventType } from '../app.constants'
import { ActivatedRoute, Router } from '@angular/router'

Expand Down
1 change: 1 addition & 0 deletions ui/src/app/app.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export enum EventType {
AFFILIATION_LIST_MODIFICATION,
IMPORT_AFFILIATIONS,
SEND_NOTIFICATIONS,
MEMBER_LIST_MODIFICATION,
}

export enum AlertType {
Expand Down
3 changes: 2 additions & 1 deletion ui/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import { ErrorComponent } from './error/error.component'
import { FormsModule } from '@angular/forms'
import { UserModule } from './user/user.module'
import { AffiliationModule } from './affiliation/affiliation.module'
import { MembersComponent } from './member/members.component'

@NgModule({
declarations: [AppComponent, NavbarComponent, FooterComponent, ErrorComponent],
declarations: [AppComponent, NavbarComponent, FooterComponent, ErrorComponent, MembersComponent],
imports: [
BrowserModule,
UserModule,
Expand Down
35 changes: 35 additions & 0 deletions ui/src/app/member/member.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot, Routes } from '@angular/router'
import { AuthGuard } from '../account/auth.guard'
import { Observable, filter, of, take } from 'rxjs'
import { inject } from '@angular/core'
import { IMember, Member } from './model/member.model'
import { MemberService } from './service/member.service'
import { MembersComponent } from './members.component'

export const MemberResolver: ResolveFn<Member | null> = (
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
memberService: MemberService = inject(MemberService)
): Observable<IMember | null> => {
if (route.paramMap.get('id')) {
return memberService.find(route.paramMap.get('id')!).pipe(
filter<IMember>((member: IMember) => !!member),
take(1)
)
} else {
return of(null)
}
}

export const memberRoutes: Routes = [
{
path: 'members',
component: MembersComponent,
data: {
authorities: ['ROLE_ADMIN'],
defaultSort: 'id,asc',
pageTitle: 'gatewayApp.msUserServiceMSMember.home.title.string',
},
canActivate: [AuthGuard],
},
]
89 changes: 89 additions & 0 deletions ui/src/app/member/members.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<div>
<h1 id="page-heading" class="mt-3" i18n="@@gatewayApp.msUserServiceMSMember.home.title.string">Manage members</h1>
<div class="row justify-content-end">
<div class="col-md-12 mb-2">
<button id="jh-create-entity" class="btn btn-primary float-right jh-create-entity create-ms-member ml-1" [routerLink]="['/member/new']">
<fa-icon [icon]="'plus'"></fa-icon>
<span i18n="@@gatewayApp.msUserServiceMSMember.home.createLabel.string">
Add member
</span>
</button>
<button id="jh-upload-ms-member" class="btn btn-primary float-right jh-create-entity create-ms-member ml-1" [routerLink]="['/', 'member', { outlets: { popup: 'import'} }]">
<fa-icon [icon]="'plus'"></fa-icon>
<span i18n="@@gatewayApp.msUserServiceMSMember.home.uploadLabel.string">
Import members from CSV
</span>
</button>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="input-group filter-group justify-content-end">
<div class="filter-input">
<input type="text" (keyup.enter)="submitSearch()" [(ngModel)]="searchTerm" i18n-placeholder="@@global.form.search.string" placeholder="Search..." ng-model="selected" class="form-control">
<button *ngIf="submittedSearchTerm" class="reset" (click)="resetSearch()"><fa-icon aria-hidden="true" [icon]="faTimes" [styles]="{'color': '#2e7f9f'}"></fa-icon></button>
</div>
<button class="search" (click)="submitSearch()"><fa-icon aria-hidden="true" [icon]="faSearch" [styles]="{'color': '#2e7f9f'}"></fa-icon></button>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<app-alert></app-alert>
<div class="alert alert-warning" *ngIf="members?.length === 0">
<span i18n="@@gatewayApp.msUserServiceMSMember.home.notFound.string">No members to show</span>
</div>
</div>
</div>
<div class="table-responsive" *ngIf="members!.length > 0">
<table class="table table-striped">
<thead>
<tr>
<th (click)="updateSort('salesforceId')"><span i18n="@@gatewayApp.msUserServiceMSMember.salesforceId.string">Salesforce Id</span> <fa-icon class="ml-2" *ngIf="sortColumn === 'salesforceId'" [icon]="ascending ? faSortDown : faSortUp"></fa-icon></th>
<th (click)="updateSort('clientName')"><span i18n="@@gatewayApp.msUserServiceMSMember.clientName.string">Member Name</span> <fa-icon class="ml-2" *ngIf="sortColumn === 'clientName'" [icon]="ascending ? faSortDown : faSortUp"></fa-icon></th>
<th (click)="updateSort('isConsortiumLead')"><span i18n="@@gatewayApp.msUserServiceMSMember.isConsortiumLead.string">Consortium Lead</span> <fa-icon class="ml-2" *ngIf="sortColumn === 'isConsortiumLead'" [icon]="ascending ? faSortDown : faSortUp"></fa-icon></th>
<th (click)="updateSort('parentSalesforceId')"><span i18n="@@gatewayApp.msUserServiceMSMember.parentSalesforceId.string">Parent Salesforce Id</span> <fa-icon class="ml-2" *ngIf="sortColumn === 'parentSalesforceId'" [icon]="ascending ? faSortDown : faSortUp"></fa-icon></th>
<th (click)="updateSort('assertionServiceEnabled')"><span i18n="@@gatewayApp.msUserServiceMSMember.assertionServiceEnabled.string">Assertions Enabled</span> <fa-icon class="ml-2" *ngIf="sortColumn === 'assertionServiceEnabled'" [icon]="ascending ? faSortDown : faSortUp"></fa-icon></th>
<th (click)="updateSort('lastModifiedDate')"><span i18n="@@gatewayApp.msUserServiceMSMember.lastModifiedDate.string">Last Modified</span> <fa-icon class="ml-2" *ngIf="sortColumn === 'lastModifiedDate'" [icon]="ascending ? faSortDown : faSortUp"></fa-icon></th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let member of members ;trackBy: trackId">
<td><a [routerLink]="['/members', member.id, 'view' ]">{{member.salesforceId}}</a></td>
<td>{{member.clientName}}</td>
<td class="text-center">
<span class="sr-only">{{member.isConsortiumLead}}</span>
<fa-icon aria-hidden="true" *ngIf="member.isConsortiumLead" [icon]="faCheckCircle" [styles]="{'color': '#28a745'}"></fa-icon>
<fa-icon aria-hidden="true" *ngIf="!member.isConsortiumLead" [icon]="faTimesCircle" [styles]="{'color': '#f22112'}"></fa-icon>
</td>
<td>{{member.parentSalesforceId}}</td>
<td class="text-center">
<span class="sr-only">{{member.assertionServiceEnabled}}</span>
<fa-icon aria-hidden="true" *ngIf="member.assertionServiceEnabled" [icon]="faCheckCircle" [styles]="{'color': '#28a745'}"></fa-icon>
<fa-icon aria-hidden="true" *ngIf="!member.assertionServiceEnabled" [icon]="faTimesCircle" [styles]="{'color': '#f22112'}"></fa-icon>
</td>
<td>{{member.lastModifiedDate?.toString() | date:'medium'}}</td>
<td class="text-right">
<div class="btn-group">
<button type="submit"
[routerLink]="['/members', member.id, 'edit']"
class="btn btn-primary btn-sm ml-1">
<fa-icon [icon]="'pencil-alt'"></fa-icon>
<span class="d-none d-md-inline" i18n="@@entity.action.edit.string">Edit</span>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div [hidden]="members?.length === 0">
<div class="row justify-content-center">
<p>{{ itemCount }}</p>
</div>
<div class="row justify-content-center">
<ngb-pagination [collectionSize]="totalItems" [(page)]="page" [pageSize]="itemsPerPage" [maxSize]="5" [rotate]="true" [boundaryLinks]="true" (pageChange)="loadPage()"></ngb-pagination>
</div>
</div>
</div>
Empty file.
21 changes: 21 additions & 0 deletions ui/src/app/member/members.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { MembersComponent } from './members.component';

describe('MembersComponent', () => {
let component: MembersComponent;
let fixture: ComponentFixture<MembersComponent>;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [MembersComponent]
});
fixture = TestBed.createComponent(MembersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
176 changes: 176 additions & 0 deletions ui/src/app/member/members.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { Component, OnDestroy, OnInit } from '@angular/core'
import { IMember } from './model/member.model'
import { Subscription } from 'rxjs'
import {
faCheckCircle,
faSearch,
faSortDown,
faSortUp,
faTimes,
faTimesCircle,
} from '@fortawesome/free-solid-svg-icons'
import { MemberService } from './service/member.service'
import { ActivatedRoute, Router } from '@angular/router'
import { EventType, ITEMS_PER_PAGE } from '../app.constants'
import { AccountService } from '../account/service/account.service'
import { EventService } from '../shared/service/event.service'
import { AlertService } from '../shared/service/alert.service'
import { IMemberPage } from './model/member-page.model'

@Component({
selector: 'app-members',
templateUrl: './members.component.html',
styleUrls: ['./members.component.scss'],
})
export class MembersComponent implements OnInit {
currentAccount: any
members: IMember[] | undefined | null
error: any
eventSubscriber: Subscription | undefined
routeData: any
links: any
totalItems: any
itemsPerPage: any
page: any
predicate: any
reverse: any
faTimesCircle = faTimesCircle
faCheckCircle = faCheckCircle
faTimes = faTimes
faSearch = faSearch
faSortDown = faSortDown
faSortUp = faSortUp
itemCount: string | undefined
searchTerm: string | undefined
submittedSearchTerm: string | undefined
paginationHeaderSubscription: Subscription | undefined
sortColumn = 'salesforceId'
ascending: any

constructor(
protected memberService: MemberService,
protected alertService: AlertService,
protected accountService: AccountService,
protected activatedRoute: ActivatedRoute,
protected router: Router,
protected eventService: EventService
) {
this.itemsPerPage = ITEMS_PER_PAGE
this.routeData = this.activatedRoute.data.subscribe((data: any) => {
this.page = data.pagingParams.page
this.reverse = data.pagingParams.ascending
this.predicate = data.pagingParams.predicate
})
}

ngOnInit() {
this.loadAll()
this.accountService.getAccountData().subscribe((account) => {
this.currentAccount = account
})

this.eventSubscriber = this.eventService.on(EventType.MEMBER_LIST_MODIFICATION).subscribe(() => {
this.searchTerm = ''
this.submittedSearchTerm = ''
this.loadAll()
})
}

loadAll() {
if (this.submittedSearchTerm) {
this.searchTerm = this.submittedSearchTerm
} else {
this.searchTerm = ''
}

this.memberService
.query({
page: this.page - 1,
size: this.itemsPerPage,
sort: this.sort(),
filter: this.submittedSearchTerm ? encodeURIComponent(this.submittedSearchTerm) : '',
})
.subscribe({
next: (res) => {
if (res) {
this.paginate(res)
}
},
})
}

loadPage() {
this.transition()
}

transition() {
this.router.navigate(['/members'], {
queryParams: {
page: this.page,
size: this.itemsPerPage,
sort: this.predicate + ',' + (this.reverse ? 'asc' : 'desc'),
filter: this.submittedSearchTerm ? this.submittedSearchTerm : '',
},
})
this.loadAll()
}

clear() {
this.page = 0
this.router.navigate([
'/members',
{
page: this.page,
sort: this.predicate + ',' + (this.reverse ? 'asc' : 'desc'),
filter: this.submittedSearchTerm ? this.submittedSearchTerm : '',
},
])
this.loadAll()
}

trackId(index: number, item: IMember) {
return item.id
}

sort() {
const result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]
if (this.predicate !== 'id') {
result.push('id')
}
return result
}

resetSearch() {
this.page = 1
this.searchTerm = ''
this.submittedSearchTerm = ''
this.loadAll()
}

submitSearch() {
this.page = 1
this.submittedSearchTerm = this.searchTerm
this.loadAll()
}

protected paginate(res: IMemberPage) {
this.totalItems = res.totalItems
this.members = res.members
const first = (this.page - 1) * this.itemsPerPage === 0 ? 1 : (this.page - 1) * this.itemsPerPage + 1
const second = this.page * this.itemsPerPage < this.totalItems ? this.page * this.itemsPerPage : this.totalItems
this.itemCount = $localize`:@@global.item-count.string:Showing ${first} - ${second} of ${this.totalItems} items.`
}

protected onError(errorMessage: string) {
this.alertService.broadcast(errorMessage)
}

updateSort(columnName: string) {
if (this.sortColumn && this.sortColumn == columnName) {
this.ascending = !this.ascending
} else {
this.sortColumn = columnName
}
this.loadPage()
}
}
16 changes: 16 additions & 0 deletions ui/src/app/member/model/member-page.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Member } from './member.model'

export interface IMemberPage {
members: Member[] | null | undefined
totalItems: number | null | undefined
}

export class MemberPage implements IMemberPage {
constructor(
public members: Member[],
public totalItems: number
) {
this.members = members
this.totalItems = totalItems
}
}
Loading

0 comments on commit b72dd9b

Please sign in to comment.