Skip to content
Permalink
Browse files
Design center module
  • Loading branch information
pembemiriam committed Aug 1, 2018
1 parent 999def0 commit 8339859221fe11dc30a313d452c14743fc9b6265
Showing 32 changed files with 2,037 additions and 2 deletions.
@@ -0,0 +1,68 @@
/**
* 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 {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
import {Injectable} from '@angular/core';
import {OfficeService} from '../services/office/office.service';
import * as fromEmployees from './store';
import {Observable} from 'rxjs/Observable';
import {LoadAction} from './store/employee.actions';
import {of} from 'rxjs/observable/of';
import {EmployeesStore} from './store/index';
import {ExistsGuardService} from '../common/guards/exists-guard';

@Injectable()
export class CenterExistsGuard implements CanActivate {

constructor(private store: EmployeesStore,
private officeService: OfficeService,
private existsGuardService: ExistsGuardService) {}

hasEmployeeInStore(id: string): Observable<boolean> {
const timestamp$ = this.store.select(fromEmployees.getEmployeesLoadedAt)
.map(loadedAt => loadedAt[id]);

return this.existsGuardService.isWithinExpiry(timestamp$);
}

hasEmployeeInApi(id: string): Observable<boolean> {
const getEmployee$ = this.officeService.getEmployee(id)
.map(employeeEntity => new LoadAction({
resource: employeeEntity
}))
.do((action: LoadAction) => this.store.dispatch(action))
.map(employee => !!employee);

return this.existsGuardService.routeTo404OnError(getEmployee$);
}

hasEmployee(id: string): Observable<boolean> {
return this.hasEmployeeInStore(id)
.switchMap(inStore => {
if (inStore) {
return of(inStore);
}

return this.hasEmployeeInApi(id);
});
}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.hasEmployee(route.params['id']);
}
}
@@ -0,0 +1,15 @@
<fims-layout-card-over title="{{'Manage centers' | translate}}">
<fims-layout-card-over-header-menu>
<td-search-box #searchBox placeholder="{{'Search' | translate}}" (search)="search($event)" [alwaysVisible]="false"></td-search-box>
</fims-layout-card-over-header-menu>
<fims-data-table flex
(onFetch)="fetchEmployees($event)"
(onActionCellClick)="rowSelect($event)"
[columns]="columns"
[data]="employeeData$ | async"
[loading]="loading$ | async"
[sortable]="true"
[pageable]="true">
</fims-data-table>
</fims-layout-card-over>
<fims-fab-button title="{{'Create new center' | translate}}" icon="add" [link]="['create']" [permission]="{ id: 'office_employees', accessLevel: 'CHANGE'}"></fims-fab-button>
Empty file.
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { CenterComponent } from './center.component';

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

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CenterComponent ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(CenterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,72 @@
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Params, Router} from '@angular/router';
import {Employee} from '../services/office/domain/employee.model';
import {FetchRequest} from '../services/domain/paging/fetch-request.model';
import {TableData} from '../common/data-table/data-table.component';
import {Store} from '@ngrx/store';
import * as fromRoot from '../store';
import {Observable} from 'rxjs/Observable';
import {SEARCH} from '../store/employee/employee.actions';



@Component({
selector: 'fims-center',
templateUrl: './center.component.html',
styleUrls: ['./center.component.scss']
})
export class CenterComponent implements OnInit {

employeeData$: Observable<TableData>;

loading$: Observable<boolean>;

columns: any[] = [
{ name: 'name', label: 'Name' },
{ name: 'accountNumber', label: 'Account #' },
{ name: 'id', label: 'External Id' },
{ name: 'status', label: 'Status' },
{ name: 'office', label: 'Office' }
];

searchTerm: string;

private lastFetchRequest: FetchRequest = {};

constructor(private router: Router, private route: ActivatedRoute, private store: Store<fromRoot.State>) {}

ngOnInit(): void {

this.employeeData$ = this.store.select(fromRoot.getEmployeeSearchResults)
.map(employeePage => ({
data: employeePage.employees,
totalElements: employeePage.totalElements,
totalPages: employeePage.totalPages
}));

this.loading$ = this.store.select(fromRoot.getEmployeeSearchLoading);

this.route.queryParams.subscribe((params: Params) => {
this.search(params['term']);
});
}

search(searchTerm: string): void {
this.searchTerm = searchTerm;
this.fetchEmployees();
}

rowSelect(row: Employee): void {
this.router.navigate(['detail', row.identifier], { relativeTo: this.route });
}

fetchEmployees(fetchRequest?: FetchRequest) {
if (fetchRequest) {
this.lastFetchRequest = fetchRequest;
}

this.lastFetchRequest.searchTerm = this.searchTerm;

this.store.dispatch({ type: SEARCH, payload: this.lastFetchRequest });
}
}
@@ -0,0 +1,66 @@
import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import {CenterComponent} from './center.component';
import {CenterRoutes} from './center.routing';
import {CenterFormComponent} from './form/form.component';
import {CreateCenterFormComponent} from './form/create/create.form.component';
import {EmployeeDetailComponent} from './detail/employee.detail.component';
import {EditEmployeeFormComponent} from './form/edit/edit.form.component';
import {UserResolver} from './user.resolver';
import {FimsSharedModule} from '../common/common.module';
import {CenterExistsGuard} from './center-exists.guard';
import {Store} from '@ngrx/store';
import {EmployeesStore, employeeStoreFactory} from './store/index';
import {EmployeeNotificationEffects} from './store/effects/notification.effects';
import {EffectsModule} from '@ngrx/effects';
import {EmployeeApiEffects} from './store/effects/service.effects';
import {EmployeeRouteEffects} from './store/effects/route.effects';
import {
MatButtonModule,
MatIconModule,
MatInputModule,
MatListModule,
MatOptionModule,
MatSelectModule,
MatToolbarModule
} from '@angular/material';
import {CovalentSearchModule, CovalentStepsModule} from '@covalent/core';
import {TranslateModule} from '@ngx-translate/core';
import {CommonModule} from '@angular/common';
import {ReactiveFormsModule} from '@angular/forms';

@NgModule({
imports: [
RouterModule.forChild(CenterRoutes),
FimsSharedModule,
ReactiveFormsModule,
CommonModule,
TranslateModule,
MatIconModule,
MatListModule,
MatToolbarModule,
MatOptionModule,
MatInputModule,
MatButtonModule,
MatSelectModule,
CovalentSearchModule,
CovalentStepsModule,

EffectsModule.run(EmployeeApiEffects),
EffectsModule.run(EmployeeRouteEffects),
EffectsModule.run(EmployeeNotificationEffects)
],
declarations: [
CenterComponent,
CenterFormComponent,
CreateCenterFormComponent,
EditEmployeeFormComponent,
EmployeeDetailComponent
],
providers: [
UserResolver,
CenterExistsGuard,
{ provide: EmployeesStore, useFactory: employeeStoreFactory, deps: [Store]}
]
})
export class CenterModule {}
@@ -0,0 +1,34 @@
import {Routes} from '@angular/router';
import {CenterComponent} from './center.component';
import {CreateCenterFormComponent} from './form/create/create.form.component';
import {EmployeeDetailComponent} from './detail/employee.detail.component';
import {EditEmployeeFormComponent} from './form/edit/edit.form.component';
import {UserResolver} from './user.resolver';
import {CenterExistsGuard} from './center-exists.guard';

export const CenterRoutes: Routes = [
{
path: '',
component: CenterComponent,
data: {title: 'Manage Employees', hasPermission: {id: 'office_employees', accessLevel: 'READ'}}
},
{
path: 'create',
component: CreateCenterFormComponent,
data: {title: 'Create Employee', hasPermission: {id: 'office_employees', accessLevel: 'CHANGE'}}
},
{
path: 'detail/:id/edit',
component: EditEmployeeFormComponent,
canActivate: [CenterExistsGuard],
resolve: {user: UserResolver},
data: {title: 'Edit Employee', hasPermission: {id: 'office_employees', accessLevel: 'CHANGE'}}
},
{
path: 'detail/:id',
component: EmployeeDetailComponent,
canActivate: [CenterExistsGuard],
resolve: {user: UserResolver},
data: {title: 'View Employee', hasPermission: {id: 'office_employees', accessLevel: 'READ'}}
}
];
@@ -0,0 +1,47 @@
<!--
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.
-->

<fims-layout-card-over title="{{employee.givenName}} {{employee.surname}}" [navigateBackTo]="'/employees'">
<fims-layout-card-over-header-menu layout="row" layout-align="end center">
<td-search-box placeholder="{{'Search' | translate}}" (search)="searchEmployee($event)" [alwaysVisible]="false"></td-search-box>
<button mat-icon-button (click)="deleteEmployee()" title="{{'Delete this employee' | translate}}" *hasPermission="{ id: 'office_employees', accessLevel: 'DELETE' }"><mat-icon>delete</mat-icon></button>
</fims-layout-card-over-header-menu>
<mat-list>
<h3 mat-subheader translate>Assigned office</h3>
<mat-list-item>
<mat-icon matListAvatar>store</mat-icon>
<h3 matLine *ngIf="employee.assignedOffice">{{employee.assignedOffice}}</h3>
<h3 matLine *ngIf="!employee.assignedOffice" translate>No office assigned</h3>
</mat-list-item>
<h3 mat-subheader translate>Assigned role</h3>
<mat-list-item>
<mat-icon matListAvatar>lock</mat-icon>
<h3 matLine>{{user.role}}</h3>
</mat-list-item>
<h3 mat-subheader translate>Contact Information</h3>
<mat-list-item [ngSwitch]="detail.type" *ngFor="let detail of employee.contactDetails">
<mat-icon *ngSwitchCase="'EMAIL'" matListAvatar>email</mat-icon>
<mat-icon *ngSwitchCase="'PHONE'" matListAvatar>phone</mat-icon>
<mat-icon *ngSwitchCase="'MOBILE'" matListAvatar>smartphone</mat-icon>
<h3 matLine>{{detail.value}}</h3>
</mat-list-item>
<mat-list-item *ngIf="!employee.contactDetails.length">
<h3 matLine translate>No contact details available</h3>
</mat-list-item>
</mat-list>
</fims-layout-card-over>
<fims-fab-button title="{{'Edit member ' | translate}}" icon="mode_edit" [link]="['edit']" [permission]="{ id: 'office_employees', accessLevel: 'CHANGE'}"></fims-fab-button>

0 comments on commit 8339859

Please sign in to comment.