Skip to content

Commit

Permalink
basic user detail component and test
Browse files Browse the repository at this point in the history
  • Loading branch information
bobcaprice committed Jan 25, 2024
1 parent 06d29c9 commit 1995019
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 4 deletions.
82 changes: 82 additions & 0 deletions ui/src/app/user/user-detail.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<div class="row justify-content-center">
<div class="col-8">
<div *ngIf="user">
<h1 class="mt-5" i18n="@@gatewayApp.msUserServiceMSUser.detail.title.string">User Settings</h1>
<hr>
<app-error-alert></app-error-alert>
<div class="row">
<dl class="jh-entity-details">
<dt class="col-md-2" i18n="@@gatewayApp.msUserServiceMSUser.email.string">Email</dt>
<dd class="col-md-10">
{{user.email}}
</dd>
<dt class="col-md-2" i18n="@@gatewayApp.msUserServiceMSUser.firstName.string">First Name</dt>
<dd class="col-md-10">
{{user.firstName}}
</dd>
<dt class="col-md-2" i18n="@@gatewayApp.msUserServiceMSUser.lastName.string">Last Name</dt>
<dd class="col-md-10">
{{user.lastName}}
</dd>
<dt class="col-md-2" i18n="@@gatewayApp.msUserServiceMSUser.mainContact.string">Main Contact</dt>
<dd class="col-md-10">
<span class="sr-only" *ngIf="user.mainContact" i18n="@@global.true.string">True</span>
<span class="sr-only" *ngIf="!user.mainContact" i18n="@@global.false.string">False</span>
<fa-icon aria-hidden="true" *ngIf="user.mainContact" [icon]="faCheckCircle" [styles]="{'color': '#28a745'}"></fa-icon>
<fa-icon aria-hidden="true" *ngIf="!user.mainContact" [icon]="faTimesCircle" [styles]="{'color': '#f22112'}"></fa-icon>
</dd>
<dt class="col-md-2" i18n="@@gatewayApp.msUserServiceMSUser.salesforceId.string">Salesforce Id</dt>
<dd class="col-md-10">
{{user.memberName}}
</dd>
<dt class="col-md-2" i18n="@@gatewayApp.msUserServiceMSUser.activated.string">Activated</dt>
<dd class="col-md-10">
<span class="sr-only" *ngIf="user.activated" i18n="@@global.true.string">True</span>
<span class="sr-only" *ngIf="!user.activated" i18n="@@global.false.string">False</span>
<fa-icon aria-hidden="true" *ngIf="user.activated" [icon]="faCheckCircle" [styles]="{'color': '#28a745'}"></fa-icon>
<fa-icon aria-hidden="true" *ngIf="!user.activated" [icon]="faTimesCircle" [styles]="{'color': '#f22112'}"></fa-icon>
</dd>
<ng-container *ngIf="superAdmin">
<dt class="col-md-2" i18n="@@gatewayApp.msUserServiceMSUser.isAdmin.string">Admin</dt>
<dd class="col-md-10">
<span class="sr-only" *ngIf="user.isAdmin" i18n="@@global.true.string">True</span>
<span class="sr-only" *ngIf="!user.isAdmin" i18n="@@global.false.string">False</span>
<fa-icon aria-hidden="true" *ngIf="user.isAdmin" [icon]="faCheckCircle" [styles]="{'color': '#28a745'}"></fa-icon>
<fa-icon aria-hidden="true" *ngIf="!user.isAdmin" [icon]="faTimesCircle" [styles]="{'color': '#f22112'}"></fa-icon>
</dd>
</ng-container>
<dt class="col-md-2" i18n="@@gatewayApp.msUserServiceMSUser.createdDate.string">Created Date</dt>
<dd class="col-md-10">
{{user.createdDate!.toString() | date:'medium'}} <i><span i18n="@@global.by.string">by</span> {{user.createdBy}}</i>
</dd>
<dt class="col-md-2" i18n="@@gatewayApp.msUserServiceMSUser.lastModifiedDate.string">Last Modified Date</dt>
<dd class="col-md-10">
<span>{{user.lastModifiedDate!.toString() | date:'medium'}} <i><span i18n="@@global.by.string">by</span> {{user.lastModifiedBy}}</i></span>
</dd>
</dl>
</div>
<hr>
<div class="form-group">
<button type="submit"
(click)="previousState()"
class="btn btn-outline-primary">
<fa-icon [icon]="faArrowLeft"></fa-icon>&nbsp;<span i18n="@@entity.action.back.string"> Back</span>
</button>
<button type="button"
[routerLink]="['/user', user.id, 'edit']"
class="btn btn-primary"
[disabled]="isDefaultAdmin(user)"
>
<fa-icon [icon]="faPencilAlt"></fa-icon>&nbsp;<span i18n="@@entity.action.edit.string"> Edit</span>
</button>
<button *ngIf="!user.activated" type="button"
(click)="sendActivate()"
class="btn btn-primary btn-sm ml-1"
>
<fa-icon [icon]="faCheckCircle"></fa-icon>
<span class="d-none d-md-inline" i18n="@@entity.action.activate.string">Activate</span>
</button>
</div>
</div>
</div>
</div>
Empty file.
67 changes: 67 additions & 0 deletions ui/src/app/user/user-detail.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'

import { UserDetailComponent } from './user-detail.component'
import { RouterTestingModule } from '@angular/router/testing'
import { HttpClientModule } from '@angular/common/http'
import { AlertService } from '../shared/service/alert.service'
import { UserService } from './service/user.service'
import { MemberService } from '../member/service/member.service'
import { User } from './model/user.model'
import { of } from 'rxjs'
import { Member } from '../member/model/member.model'

describe('UserDetailComponent', () => {
let component: UserDetailComponent
let fixture: ComponentFixture<UserDetailComponent>
let userServiceSpy: jasmine.SpyObj<UserService>
let alertServiceSpy: jasmine.SpyObj<AlertService>
let memberServiceSpy: jasmine.SpyObj<MemberService>

beforeEach(() => {
memberServiceSpy = jasmine.createSpyObj('MemberService', ['find'])
userServiceSpy = jasmine.createSpyObj('UserService', ['find', 'sendActivate'])
alertServiceSpy = jasmine.createSpyObj('AlertService', ['broadcast'])

TestBed.configureTestingModule({
declarations: [UserDetailComponent],
imports: [RouterTestingModule, HttpClientModule],
providers: [
{ provide: UserService, useValue: userServiceSpy },
{ provide: MemberService, useValue: memberServiceSpy },
{ provide: AlertService, useValue: alertServiceSpy },
],
}).compileComponents()

fixture = TestBed.createComponent(UserDetailComponent)
component = fixture.componentInstance
fixture.detectChanges()

userServiceSpy = TestBed.inject(UserService) as jasmine.SpyObj<UserService>
memberServiceSpy = TestBed.inject(MemberService) as jasmine.SpyObj<MemberService>
alertServiceSpy = TestBed.inject(AlertService) as jasmine.SpyObj<AlertService>
})

it('should create', () => {
expect(component).toBeTruthy()
})

it('when user exists, sendActivate should call userService and alertService', () => {
component.user = new User()
userServiceSpy.sendActivate.and.returnValue(of(new User()))

component.sendActivate()

expect(userServiceSpy.sendActivate).toHaveBeenCalled()
expect(alertServiceSpy.broadcast).toHaveBeenCalled()
})

it('when user does not exist, sendActivate should not call userService or alertService', () => {
component.user = null
userServiceSpy.sendActivate.and.returnValue(of(new User()))

component.sendActivate()

expect(userServiceSpy.sendActivate).toHaveBeenCalledTimes(0)
expect(alertServiceSpy.broadcast).toHaveBeenCalledTimes(0)
})
})
57 changes: 57 additions & 0 deletions ui/src/app/user/user-detail.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Component } from '@angular/core'
import { IUser } from './model/user.model'
import { faCheckCircle, faTimesCircle, faPencilAlt, faArrowLeft } from '@fortawesome/free-solid-svg-icons'
import { ActivatedRoute } from '@angular/router'
import { map, switchMap, tap } from 'rxjs'
import { UserService } from './service/user.service'
import { AlertService } from '../shared/service/alert.service'
import { MemberService } from '../member/service/member.service'

@Component({
selector: 'app-user-detail',
templateUrl: './user-detail.component.html',
styleUrls: ['./user-detail.component.scss'],
})
export class UserDetailComponent {
user: IUser | null = null
faTimesCircle = faTimesCircle
faCheckCircle = faCheckCircle
faPencilAlt = faPencilAlt
faArrowLeft = faArrowLeft

DEFAULT_ADMIN = 'admin'
superAdmin: boolean | undefined = false

constructor(
protected activatedRoute: ActivatedRoute,
protected userService: UserService,
protected alertService: AlertService,
protected memberService: MemberService
) {}

ngOnInit() {
this.activatedRoute.data.subscribe(({ user }) => {
this.user = user
})
}

sendActivate() {
if (this.user) {
this.userService.sendActivate(this.user).subscribe((res) => {
this.alertService.broadcast('gatewayApp.msUserServiceMSUser.sendActivate.success.string')
this.previousState()
})
}
}

isDefaultAdmin(user: IUser) {
if (user.email === this.DEFAULT_ADMIN) {
return true
}
return false
}

previousState() {
window.history.back()
}
}
5 changes: 3 additions & 2 deletions ui/src/app/user/user.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import { BrowserModule } from '@angular/platform-browser'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'
import { FormsModule } from '@angular/forms'
import { HasAnyAuthorityDirective } from '../shared/directive/has-any-authority.directive'
import { HasAnyAuthorityDirective } from '../shared/directive/has-any-authority.directive';
import { UserDetailComponent } from './user-detail.component'

@NgModule({
declarations: [UsersComponent],
declarations: [UsersComponent, UserDetailComponent],
imports: [CommonModule, SharedModule, RouterModule.forChild(routes), FontAwesomeModule, FormsModule],
})
export class UserModule {}
35 changes: 34 additions & 1 deletion ui/src/app/user/user.route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
import { Routes } from '@angular/router'
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot, Routes } from '@angular/router'
import { UsersComponent } from './users.component'
import { AuthGuard } from '../account/auth.guard'
import { UserDetailComponent } from './user-detail.component'
import { EMPTY, Observable, filter, take } from 'rxjs'
import { User } from './model/user.model'
import { UserService } from './service/user.service'
import { Injectable, inject } from '@angular/core'

export const UserResolver: ResolveFn<User> = (
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
userService: UserService = inject(UserService)
): Observable<User> => {
if (route.paramMap.get('id')) {
return userService.find(route.paramMap.get('id')!).pipe(
filter<User>((user: User) => !!user),
take(1)
)
} else {
return EMPTY
}
}

export const routes: Routes = [
{
Expand All @@ -10,6 +30,19 @@ export const routes: Routes = [
authorities: ['ROLE_ADMIN', 'ROLE_ORG_OWNER', 'ROLE_CONSORTIUM_LEAD'],
defaultSort: 'id,asc',
pageTitle: 'gatewayApp.msUserServiceMSUser.home.title.string',
ascending: true,
},
canActivate: [AuthGuard],
},
{
path: ':id/view',
component: UserDetailComponent,
resolve: {
user: UserResolver,
},
data: {
authorities: ['ROLE_ADMIN', 'ROLE_ORG_OWNER', 'ROLE_CONSORTIUM_LEAD'],
pageTitle: 'gatewayApp.msUserServiceMSUser.home.title.string',
},
canActivate: [AuthGuard],
},
Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/user/users.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ <h1 id="page-heading" class="mt-3" i18n="@@gatewayApp.msUserServiceMSUser.home.t
</button>
</td>
<td>
<a [routerLink]="['/user', user.id, 'view']">{{ user.email }}</a>
<a [routerLink]="['/users', user.id, 'view']">{{ user.email }}</a>
</td>
<td>{{ user.firstName }}</td>
<td>{{ user.lastName }}</td>
Expand Down

0 comments on commit 1995019

Please sign in to comment.