Design center module
diff --git a/src/app/centers/center-exists.guard.ts b/src/app/centers/center-exists.guard.ts
new file mode 100644
index 0000000..6936b08
--- /dev/null
+++ b/src/app/centers/center-exists.guard.ts
@@ -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']);
+  }
+}
diff --git a/src/app/centers/center.component.html b/src/app/centers/center.component.html
new file mode 100644
index 0000000..bd1065a
--- /dev/null
+++ b/src/app/centers/center.component.html
@@ -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>
\ No newline at end of file
diff --git a/src/app/centers/center.component.scss b/src/app/centers/center.component.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/app/centers/center.component.scss
diff --git a/src/app/centers/center.component.spec.ts b/src/app/centers/center.component.spec.ts
new file mode 100644
index 0000000..da7698d
--- /dev/null
+++ b/src/app/centers/center.component.spec.ts
@@ -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();
+  });
+});
diff --git a/src/app/centers/center.component.ts b/src/app/centers/center.component.ts
new file mode 100644
index 0000000..ab58913
--- /dev/null
+++ b/src/app/centers/center.component.ts
@@ -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 });
+  }
+}
diff --git a/src/app/centers/center.module.ts b/src/app/centers/center.module.ts
new file mode 100644
index 0000000..10ded43
--- /dev/null
+++ b/src/app/centers/center.module.ts
@@ -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 {}
\ No newline at end of file
diff --git a/src/app/centers/center.routing.ts b/src/app/centers/center.routing.ts
new file mode 100644
index 0000000..3b7718a
--- /dev/null
+++ b/src/app/centers/center.routing.ts
@@ -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'}}
+  }
+];
diff --git a/src/app/centers/detail/employee.detail.component.html b/src/app/centers/detail/employee.detail.component.html
new file mode 100644
index 0000000..f4d6824
--- /dev/null
+++ b/src/app/centers/detail/employee.detail.component.html
@@ -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>
diff --git a/src/app/centers/detail/employee.detail.component.ts b/src/app/centers/detail/employee.detail.component.ts
new file mode 100644
index 0000000..c019833
--- /dev/null
+++ b/src/app/centers/detail/employee.detail.component.ts
@@ -0,0 +1,96 @@
+/**
+ * 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 {ActivatedRoute, Router} from '@angular/router';
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {Center} from '../../services/office/domain/employee.model';
+import {TdDialogService} from '@covalent/core';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import {User} from '../../services/identity/domain/user.model';
+import * as fromEmployee from '../store';
+import {DELETE, SelectAction} from '../store/employee.actions';
+import {EmployeesStore} from '../store/index';
+
+@Component({
+  selector: 'fims-employee-detail',
+  templateUrl: './employee.detail.component.html'
+})
+export class EmployeeDetailComponent implements OnInit, OnDestroy {
+
+  private actionsSubscription: Subscription;
+
+  private employeeSubscription: Subscription;
+
+  employee: Center;
+
+  user: User;
+
+  constructor(private route: ActivatedRoute, private router: Router, private dialogService: TdDialogService,
+              private store: EmployeesStore) {}
+
+  ngOnInit(): void {
+    this.actionsSubscription = this.route.params
+      .map(params => new SelectAction(params['id']))
+      .subscribe(this.store);
+
+    this.employeeSubscription = this.store.select(fromEmployee.getSelectedEmployee)
+      .filter(employee => !!employee)
+      .subscribe(employee => this.employee = employee);
+
+    // TODO load user via store
+    this.route.data.subscribe(( data: { user: User }) => {
+      this.user = data.user;
+    });
+  }
+
+  ngOnDestroy(): void {
+    this.actionsSubscription.unsubscribe();
+    this.employeeSubscription.unsubscribe();
+  }
+
+  searchEmployee(term): void {
+    if (!term) {
+      return;
+    }
+    this.goToOverviewPage(term);
+  }
+
+  confirmDeletion(): Observable<boolean> {
+    return this.dialogService.openConfirm({
+      message: 'Do you want to delete employee "' + this.employee.givenName + ' ' + this.employee.surname + '"?',
+      title: 'Confirm deletion',
+      acceptButton: 'DELETE EMPLOYEE',
+    }).afterClosed();
+  }
+
+  deleteEmployee(): void {
+    this.confirmDeletion()
+      .filter(accept => accept)
+      .subscribe(() => {
+        this.store.dispatch({ type: DELETE, payload: {
+          employee: this.employee,
+          activatedRoute: this.route
+        } });
+      });
+  }
+
+  goToOverviewPage(term?: string): void {
+    this.router.navigate(['../../'], { queryParams: { term: term }, relativeTo: this.route });
+  }
+}
diff --git a/src/app/centers/form/create/create.form.component.html b/src/app/centers/form/create/create.form.component.html
new file mode 100644
index 0000000..16a62e0
--- /dev/null
+++ b/src/app/centers/form/create/create.form.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.
+-->
+
+<fims-layout-card-over title="{{'Create new center' | translate}}">
+  <fims-center-form-component #form
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [formData]="centerFormData">
+  </fims-center-form-component>
+</fims-layout-card-over>
diff --git a/src/app/centers/form/create/create.form.component.spec.ts b/src/app/centers/form/create/create.form.component.spec.ts
new file mode 100644
index 0000000..0475674
--- /dev/null
+++ b/src/app/centers/form/create/create.form.component.spec.ts
@@ -0,0 +1,130 @@
+/**
+ * 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 {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {CenterFormComponent, CenterSaveEvent} from '../form.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {CreateCenterFormComponent} from './create.form.component';
+import {mapEmployee, mapUser} from '../form.mapper';
+import {EmployeesStore} from '../../store/index';
+import {CREATE} from '../../store/employee.actions';
+import {Store} from '@ngrx/store';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {MatCardModule, MatInputModule, MatOptionModule, MatSelectModule} from '@angular/material';
+import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {FimsSharedModule} from '../../../common/common.module';
+
+const eventMock: CenterSaveEvent = {
+  detailForm: {
+    identifier: 'test',
+    firstName: 'test',
+    middleName: 'test',
+    lastName: 'test',
+    password: 'test',
+    role: 'test'
+  },
+  contactForm: {
+    email: 'test',
+    mobile: 'test',
+    phone: 'test'
+  },
+  
+  officeForm: {
+    assignedOffice: 'test'
+  }
+};
+
+let router: Router;
+
+describe('Test employee form component', () => {
+
+  let fixture: ComponentFixture<CreateCenterFormComponent>;
+
+  let testComponent: CreateCenterFormComponent;
+
+  beforeEach(() => {
+    router = jasmine.createSpyObj('Router', ['navigate']);
+
+    TestBed.configureTestingModule({
+      declarations: [
+        CenterFormComponent,
+        CreateCenterFormComponent,
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatInputModule,
+        MatCardModule,
+        MatSelectModule,
+        MatOptionModule,
+        CovalentStepsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        { provide: Router, useValue: router},
+        { provide: ActivatedRoute, useValue: {} },
+        {
+          provide: Store, useClass: class {
+          dispatch = jasmine.createSpy('dispatch');
+          select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+        }},
+        {
+          provide: EmployeesStore, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+          }
+        }
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    });
+
+    fixture = TestBed.createComponent(CreateCenterFormComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should test if employee is created', async(inject([EmployeesStore], (store: EmployeesStore) => {
+    fixture.detectChanges();
+
+    testComponent.onSave(eventMock);
+
+    fixture.whenStable().then(() => {
+      const employee = mapEmployee(eventMock);
+      const user = mapUser(eventMock);
+
+      expect(store.dispatch).toHaveBeenCalledWith({ type: CREATE, payload: {
+        employee: employee,
+        user: user,
+        activatedRoute: {}
+      }});
+    });
+  })));
+
+  xit('should test if error is set on 409', async(() => {
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(testComponent.formComponent.detailForm.get('identifier').errors).toBeDefined();
+      expect(testComponent.formComponent.detailForm.get('identifier').errors['unique']).toBeTruthy();
+    });
+  }));
+});
diff --git a/src/app/centers/form/create/create.form.component.ts b/src/app/centers/form/create/create.form.component.ts
new file mode 100644
index 0000000..f5c1d0a
--- /dev/null
+++ b/src/app/centers/form/create/create.form.component.ts
@@ -0,0 +1,84 @@
+/**
+ * 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, OnDestroy, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {CenterFormComponent, CenterFormData, CenterSaveEvent} from '../form.component';
+import {mapEmployee, mapUser} from '../form.mapper';
+import {Center} from '../../../services/office/domain/employee.model';
+import {UserWithPassword} from '../../../services/identity/domain/user-with-password.model';
+import * as fromEmployees from '../../store';
+import {Subscription} from 'rxjs/Subscription';
+import {CREATE, RESET_FORM} from '../../store/employee.actions';
+import {Error} from '../../../services/domain/error.model';
+import {EmployeesStore} from '../../store/index';
+
+@Component({
+  templateUrl: './create.form.component.html'
+})
+export class CreateCenterFormComponent implements OnInit, OnDestroy {
+
+  private formStateSubscription: Subscription;
+
+  @ViewChild('form') formComponent: CenterFormComponent;
+
+  centerFormData: CenterFormData = {
+    user: { identifier: '', role: ''},
+    employee: { identifier: '', givenName: '', surname: '', contactDetails: [] }
+  };
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: EmployeesStore) {}
+
+  ngOnInit(): void {
+    this.formStateSubscription = this.store.select(fromEmployees.getEmployeeFormError)
+      .filter((error: Error) => !!error)
+      .subscribe((error: Error) => {
+        const detailForm = this.formComponent.detailForm;
+        const errors = detailForm.get('identifier').errors || {};
+        errors['unique'] = true;
+        detailForm.get('identifier').setErrors(errors);
+        this.formComponent.step.open();
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.formStateSubscription.unsubscribe();
+
+    this.store.dispatch({ type: RESET_FORM });
+  }
+
+  onSave(event: CenterSaveEvent): void {
+    const employee: Center = mapEmployee(event);
+    const user: UserWithPassword = mapUser(event);
+
+    this.store.dispatch({ type: CREATE, payload: {
+      employee: employee,
+      user: user,
+      activatedRoute: this.route
+    }});
+
+  }
+
+  onCancel(): void {
+    this.navigateAway();
+  }
+
+  navigateAway(): void {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/centers/form/edit/edit.form.component.html b/src/app/centers/form/edit/edit.form.component.html
new file mode 100644
index 0000000..5750425
--- /dev/null
+++ b/src/app/centers/form/edit/edit.form.component.html
@@ -0,0 +1,25 @@
+<!--
+  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="{{'Edit employee' | translate}}">
+  <fims-center-form-component #form
+                                  [editMode]="true"
+                                  (onSave)="onSave($event)"
+                                  (onCancel)="onCancel()"
+                                  [formData]="formData | async">
+  </fims-center-form-component>
+</fims-layout-card-over>
diff --git a/src/app/centers/form/edit/edit.form.component.spec.ts b/src/app/centers/form/edit/edit.form.component.spec.ts
new file mode 100644
index 0000000..c3dd78b
--- /dev/null
+++ b/src/app/centers/form/edit/edit.form.component.spec.ts
@@ -0,0 +1,141 @@
+/**
+ * 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 {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {EditEmployeeFormComponent} from './edit.form.component';
+import {CenterFormComponent} from '../form.component';
+import {ActivatedRoute, Router} from '@angular/router';
+import {User} from '../../../services/identity/domain/user.model';
+import {Center} from '../../../services/office/domain/employee.model';
+import {Observable} from 'rxjs/Observable';
+import {EmployeesStore} from '../../store/index';
+import {Store} from '@ngrx/store';
+import {UPDATE} from '../../store/employee.actions';
+import * as fromEmployees from '../../store';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {MatCardModule, MatInputModule, MatOptionModule, MatSelectModule} from '@angular/material';
+import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {FimsSharedModule} from '../../../common/common.module';
+
+const userMock: User = {
+  identifier: 'test',
+  role: 'test'
+};
+
+const employeeMock: Center = {
+  identifier: 'test',
+  assignedOffice: 'test',
+  givenName: 'test',
+  middleName: 'test',
+  surname: 'test',
+  contactDetails: [
+    {
+      group: 'BUSINESS',
+      type: 'EMAIL',
+      value: 'test',
+      preferenceLevel: 1
+    }
+  ]
+  
+};
+
+const activatedRoute = {
+  data: Observable.of({
+    user: userMock
+  })
+};
+let router: Router;
+
+describe('Test employee form component', () => {
+
+  let fixture: ComponentFixture<EditEmployeeFormComponent>;
+
+  let testComponent: EditEmployeeFormComponent;
+
+  beforeEach(() => {
+    router = jasmine.createSpyObj('Router', ['navigate']);
+
+    TestBed.configureTestingModule({
+      declarations: [
+        CenterFormComponent,
+        EditEmployeeFormComponent,
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatInputModule,
+        MatCardModule,
+        MatSelectModule,
+        MatOptionModule,
+        CovalentStepsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        { provide: Router, useValue: router},
+        { provide: ActivatedRoute, useValue: activatedRoute },
+        {
+          provide: Store, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+          }
+        },
+        {
+          provide: EmployeesStore, useClass: class {
+            dispatch = jasmine.createSpy('dispatch');
+            select = jasmine.createSpy('select').and.callFake(selector => {
+              if (selector === fromEmployees.getSelectedEmployee) {
+                return Observable.of(employeeMock);
+              }
+
+              return Observable.empty();
+            });
+          }
+        }
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    });
+
+    fixture = TestBed.createComponent(EditEmployeeFormComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should test if employee is updated', async(inject([EmployeesStore], (store: EmployeesStore) => {
+    fixture.detectChanges();
+
+    testComponent.formComponent.detailForm.get('password').setValue('newPassword');
+
+    fixture.detectChanges();
+
+    testComponent.formComponent.save();
+
+    fixture.whenStable().then(() => {
+      expect(store.dispatch).toHaveBeenCalledWith({ type: UPDATE, payload: {
+        employee: employeeMock,
+        contactDetails: employeeMock.contactDetails,
+        role: userMock.role,
+        password: 'newPassword',
+        activatedRoute: activatedRoute
+      }});
+    });
+
+  })));
+});
diff --git a/src/app/centers/form/edit/edit.form.component.ts b/src/app/centers/form/edit/edit.form.component.ts
new file mode 100644
index 0000000..f0ab243
--- /dev/null
+++ b/src/app/centers/form/edit/edit.form.component.ts
@@ -0,0 +1,76 @@
+/**
+ * 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, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {CenterFormComponent, CenterFormData, CenterSaveEvent} from '../form.component';
+import {mapContactDetails, mapEmployee} from '../form.mapper';
+import {Center} from '../../../services/office/domain/employee.model';
+import {User} from '../../../services/identity/domain/user.model';
+import {UPDATE} from '../../store/employee.actions';
+import {Observable} from 'rxjs/Observable';
+import {EmployeesStore, getSelectedEmployee} from '../../store/index';
+
+@Component({
+  templateUrl: './edit.form.component.html'
+})
+export class EditEmployeeFormComponent implements OnInit {
+
+  @ViewChild('form') formComponent: CenterFormComponent;
+
+  formData: Observable<CenterFormData>;
+
+  employee: Center;
+
+  user: User;
+
+  constructor(private router: Router, private route: ActivatedRoute, private store: EmployeesStore) {}
+
+  ngOnInit() {
+    this.formData = Observable.combineLatest(
+      this.store.select(getSelectedEmployee),
+      this.route.data,
+      (employee: Center, data: { user: User }) => {
+        return {
+          user: data.user,
+          employee: employee
+        };
+      }
+    );
+  }
+
+  onSave(event: CenterSaveEvent) {
+    const employee: Center = mapEmployee(event);
+
+    this.store.dispatch({ type: UPDATE, payload: {
+      employee: employee,
+      contactDetails: mapContactDetails(event.contactForm),
+      role: event.detailForm.role,
+      password: event.detailForm.password,
+      activatedRoute: this.route
+    }});
+  }
+
+  onCancel() {
+    this.navigateToOffice();
+  }
+
+  private navigateToOffice() {
+    this.router.navigate(['../'], { relativeTo: this.route });
+  }
+}
diff --git a/src/app/centers/form/form.component.html b/src/app/centers/form/form.component.html
new file mode 100644
index 0000000..43ff2d4
--- /dev/null
+++ b/src/app/centers/form/form.component.html
@@ -0,0 +1,88 @@
+<!--
+  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.
+-->
+
+<td-steps mode="'vertical'">
+  <td-step #detailsStep label="{{'Center details' | translate}}" [state]="detailForm.valid ? 'complete' : detailForm.pristine ? 'none' : 'required'">
+    <form [formGroup]="detailForm" layout="column">
+      <fims-id-input [form]="detailForm" placeholder="External id" controlName="identifier" [readonly]="editMode"></fims-id-input>
+      <fims-text-input [form]="detailForm" controlName="firstName" placeholder="{{'Name' | translate}}"></fims-text-input>
+     
+    
+    </form>
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="officeStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #officeStep label="{{'Assign office to center' | translate}}"
+           [state]="officeForm.get('assignedOffice').value ? 'complete' : 'none'">
+
+    <fims-select-list #officeList flex
+                        [data]="offices"
+                        id="identifier"
+                        listIcon="store"
+                        [preSelection]="officeForm.get('assignedOffice').value ? [officeForm.get('assignedOffice').value] : []"
+                        (onSearch)="searchOffice($event)"
+                        (onSelectionChange)="assignOffice($event)"
+                        title="{{'Assigned Office' | translate}}"
+                        noResultsMessage="{{'No office was found.' | translate}}"
+                        noSelectionMessage="{{'No office assigned to employee, yet.' | translate}}">
+    </fims-select-list>
+<br/>
+    <mat-form-field>
+      <mat-select placeholder="Select Staff">
+        <mat-option *ngFor="let staff of staffs" [value]="staff.value">
+          {{staff.viewValue}}
+        </mat-option>
+      </mat-select>
+    </mat-form-field>
+    
+    
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="contactStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step #officeStep label="{{'Assign group to center' | translate}}"
+           [state]="officeForm.get('assignedOffice').value ? 'complete' : 'none'">
+
+    <fims-select-list #officeList flex
+                        
+                        title="{{'Assigned Group' | translate}}"
+                        noResultsMessage="{{'No group was found.' | translate}}"
+                        noSelectionMessage="{{'No group assigned to center, yet.' | translate}}">
+    </fims-select-list>
+
+    
+    <ng-template td-step-actions>
+      <fims-form-continue-action (onContinue)="contactStep.open()"></fims-form-continue-action>
+    </ng-template>
+  </td-step>
+
+  <td-step label="{{'Final step' | translate}}" [state]="'complete'">
+    <ng-template td-step-summary>
+      <fims-form-final-action
+        [resourceName]="'CENTER'"
+        [editMode]="editMode"
+        [disabled]="formsInvalid()"
+        (onCancel)="cancel()"
+        (onSave)="save()">
+      </fims-form-final-action>
+    </ng-template>
+  </td-step>
+
+</td-steps>
diff --git a/src/app/centers/form/form.component.spec.ts b/src/app/centers/form/form.component.spec.ts
new file mode 100644
index 0000000..621eefa
--- /dev/null
+++ b/src/app/centers/form/form.component.spec.ts
@@ -0,0 +1,135 @@
+/**
+ * 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 {Component, EventEmitter, ViewChild} from '@angular/core';
+import {Center} from '../../services/office/domain/employee.model';
+import {CenterFormComponent, CenterFormData, CenterSaveEvent} from './form.component';
+import {User} from '../../services/identity/domain/user.model';
+import {TranslateModule} from '@ngx-translate/core';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CovalentStepsModule} from '@covalent/core';
+import {Observable} from 'rxjs/Observable';
+import {Store} from '@ngrx/store';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {FimsSharedModule} from '../../common/common.module';
+import {MatIconModule, MatInputModule, MatOptionModule, MatSelectModule} from '@angular/material';
+
+const employeeTemplate: Center = {
+  identifier: 'test',
+  givenName: 'test',
+  middleName: 'test',
+  surname: 'test',
+  contactDetails: [{
+    type: 'EMAIL',
+    group: 'BUSINESS',
+    value: 'test@test.de',
+    preferenceLevel: 0
+  }], 
+  assignedOffice: 'test'
+};
+
+const userTemplate: User = {
+  identifier: 'test',
+  role: 'test'
+};
+
+describe('Test employee form component', () => {
+
+  let fixture: ComponentFixture<TestComponent>;
+
+  let testComponent: TestComponent;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        TestComponent,
+        CenterFormComponent
+      ],
+      imports: [
+        TranslateModule.forRoot(),
+        FimsSharedModule,
+        ReactiveFormsModule,
+        MatSelectModule,
+        MatOptionModule,
+        MatIconModule,
+        MatInputModule,
+        CovalentStepsModule,
+        NoopAnimationsModule
+      ],
+      providers: [
+        {
+          provide: Store, useClass: class {
+          dispatch = jasmine.createSpy('dispatch');
+          select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+        }}
+      ]
+    });
+
+    fixture = TestBed.createComponent(TestComponent);
+    testComponent = fixture.componentInstance;
+  });
+
+  it('should test if the form save the original values', () => {
+    fixture.detectChanges();
+
+    testComponent.saveEmitter.subscribe((saveEvent: CenterSaveEvent) => {
+      expect(employeeTemplate.identifier).toEqual(saveEvent.detailForm.identifier);
+      expect(employeeTemplate.givenName).toEqual(saveEvent.detailForm.firstName);
+      expect(employeeTemplate.middleName).toEqual(saveEvent.detailForm.middleName);
+      expect(employeeTemplate.surname).toEqual(saveEvent.detailForm.lastName);
+      expect(saveEvent.detailForm.password).toEqual('');
+
+      expect(employeeTemplate.assignedOffice).toEqual(saveEvent.officeForm.assignedOffice);
+
+      expect(employeeTemplate.contactDetails.length).toEqual(1);
+      expect(employeeTemplate.contactDetails[0].value).toEqual(saveEvent.contactForm.email);
+
+      expect(userTemplate.role).toEqual(saveEvent.detailForm.role);
+    });
+
+    testComponent.triggerSave();
+
+  });
+});
+
+@Component({
+  template: `
+    <fims-employee-form-component #form (onSave)="onSave($event)" (onCancel)="onCancel($event)" [formData]="employeeFormData">
+    </fims-employee-form-component>`
+})
+class TestComponent {
+
+  saveEmitter = new EventEmitter<CenterSaveEvent>();
+
+  @ViewChild('form') formComponent: CenterFormComponent;
+
+  employeeFormData: CenterFormData = {
+    employee: employeeTemplate,
+    user: userTemplate
+  };
+
+  triggerSave(): void {
+    this.formComponent.save();
+  }
+
+  onSave(event: CenterSaveEvent): void {
+    this.saveEmitter.emit(event);
+  }
+
+}
diff --git a/src/app/centers/form/form.component.ts b/src/app/centers/form/form.component.ts
new file mode 100644
index 0000000..6f1e6cc
--- /dev/null
+++ b/src/app/centers/form/form.component.ts
@@ -0,0 +1,189 @@
+/**
+ * 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, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
+import {FormBuilder, FormGroup, ValidatorFn, Validators} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import {TdStepComponent} from '@covalent/core';
+import {Office} from '../../services/office/domain/office.model';
+import {Center} from '../../services/office/domain/employee.model';
+import {BUSINESS, ContactDetail, EMAIL, MOBILE, PHONE} from '../../services/domain/contact/contact-detail.model';
+import {FetchRequest} from '../../services/domain/paging/fetch-request.model';
+import {Role} from '../../services/identity/domain/role.model';
+import {User} from '../../services/identity/domain/user.model';
+import {FimsValidators} from '../../common/validator/validators';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../../store';
+import {SEARCH as SEARCH_OFFICE} from '../../store/office/office.actions';
+import {SEARCH as SEARCH_ROLE} from '../../store/role/role.actions';
+
+export interface CenterFormData {
+  user: User;
+  employee: Center;
+}
+
+export interface CenterSaveEvent {
+  detailForm: {
+    identifier: string;
+    firstName: string;
+    middleName: string;
+    lastName: string;
+    password: string;
+    role: string;
+  };
+  contactForm: {
+    email: string;
+    phone: string;
+    mobile: string;
+  };
+  
+  officeForm: {
+    assignedOffice: string;
+  };
+}
+
+@Component({
+  selector: 'fims-center-form-component',
+  templateUrl: './form.component.html'
+})
+export class CenterFormComponent implements OnInit {
+
+  offices: Observable<Office[]>;
+
+  roles: Observable<Role[]>;
+
+  detailForm: FormGroup;
+  contactForm: FormGroup;
+  officeForm: FormGroup;
+
+  @ViewChild('detailsStep') step: TdStepComponent;
+
+  @Input('editMode') editMode: boolean;
+
+  @Input('formData') set formData(formData: CenterFormData) {
+    this.prepareDetailForm(formData.employee, formData.user);
+    this.prepareOfficeForm(formData.employee);
+    this.prepareContactForm(formData.employee.contactDetails);
+  }
+
+  @Output('onSave') onSave = new EventEmitter<CenterSaveEvent>();
+  @Output('onCancel') onCancel = new EventEmitter<void>();
+
+  constructor(private formBuilder: FormBuilder, private store: Store<fromRoot.State>) {}
+
+  ngOnInit(): void {
+    this.offices = this.store.select(fromRoot.getOfficeSearchResults)
+      .map(officePage => officePage.offices);
+
+    this.roles = this.store.select(fromRoot.getRoleSearchResults)
+      .map(rolesPage => rolesPage.roles);
+
+    this.fetchRoles();
+
+    this.step.open();
+  }
+
+  prepareDetailForm(employee: Center, user: User): void {
+    const passwordValidators: ValidatorFn[] = [Validators.minLength(8)];
+
+    if (!this.editMode) {
+      passwordValidators.push(Validators.required);
+    }
+
+    this.detailForm = this.formBuilder.group({
+      identifier: [employee.identifier, [Validators.required, Validators.minLength(3), Validators.maxLength(32), FimsValidators.urlSafe]],
+      firstName: [employee.givenName, [Validators.required, Validators.maxLength(256)]],
+      middleName: [employee.middleName, Validators.maxLength(256)],
+      lastName: [employee.surname, [Validators.required, Validators.maxLength(256)]],
+      password: ['', passwordValidators],
+      role: [user ? user.role : '', Validators.required]
+    });
+  }
+
+  private prepareOfficeForm(employee: Center) {
+    this.officeForm = this.formBuilder.group({
+      assignedOffice: [employee.assignedOffice]
+    });
+  }
+
+ private prepareContactForm(contactDetails: ContactDetail[]): void {
+    let phone = '';
+    let mobile = '';
+    let email = '';
+
+    const businessContacts: ContactDetail[] = contactDetails.filter(contactDetail => contactDetail.group === BUSINESS);
+
+    if (businessContacts.length) {
+      phone = this.getFirstItemByType(businessContacts, PHONE);
+      mobile = this.getFirstItemByType(businessContacts, MOBILE);
+      email = this.getFirstItemByType(businessContacts, EMAIL);
+    }
+
+    this.contactForm = this.formBuilder.group({
+      email: [email, [Validators.maxLength(256), FimsValidators.email]],
+      phone: [phone, Validators.maxLength(256)],
+      mobile: [mobile, Validators.maxLength(256)]
+    });
+  }
+  
+
+  getFirstItemByType(contactDetails: ContactDetail[], type: string): string {
+    const items = contactDetails.filter(contact => contact.type === type);
+    return items.length ? items[0].value : '';
+  }
+
+  formsInvalid(): boolean {
+    return (!this.officeForm.pristine && this.officeForm.invalid) ||
+    (!this.contactForm.pristine && this.contactForm.invalid)
+      || this.detailForm.invalid;
+  }
+
+  
+
+  save(): void {
+    this.onSave.emit({
+      detailForm: this.detailForm.value,
+      contactForm: this.contactForm.value,
+      officeForm: this.officeForm.value
+    });
+  }
+
+  cancel(): void {
+    this.onCancel.emit();
+  }
+
+  searchOffice(searchTerm: string): void {
+    const fetchRequest: FetchRequest = {
+      searchTerm
+    };
+    this.store.dispatch({ type: SEARCH_OFFICE, payload: fetchRequest });
+  }
+
+  assignOffice(selections: string[]): void {
+    this.setFormValue(this.officeForm, {'assignedOffice': selections && selections.length > 0 ? selections[0] : undefined});
+  }
+
+  fetchRoles(): void {
+    this.store.dispatch({ type: SEARCH_ROLE });
+  }
+
+  private setFormValue(form: FormGroup, value: any) {
+    form.setValue(value);
+    form.markAsDirty();
+  }
+}
diff --git a/src/app/centers/form/form.mapper.ts b/src/app/centers/form/form.mapper.ts
new file mode 100644
index 0000000..c74aab3
--- /dev/null
+++ b/src/app/centers/form/form.mapper.ts
@@ -0,0 +1,78 @@
+/**
+ * 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 {CenterSaveEvent} from './form.component';
+import {ContactDetail, ContactDetailType} from '../../services/domain/contact/contact-detail.model';
+import {Center} from '../../services/office/domain/employee.model';
+import {UserWithPassword} from '../../services/identity/domain/user-with-password.model';
+
+function buildContactDetail(type: ContactDetailType, value: string): ContactDetail {
+  return {
+    group: 'BUSINESS',
+    type: type,
+    value: value,
+    preferenceLevel: 1
+  };
+} 
+
+export function mapContactDetails(contactForm: any): ContactDetail[] {
+  const contactDetails: ContactDetail[] = [];
+
+  if (contactForm.phone) {
+    contactDetails.push(buildContactDetail('PHONE', contactForm.phone));
+  }
+
+  if (contactForm.mobile) {
+    contactDetails.push(buildContactDetail('MOBILE', contactForm.mobile));
+  }
+
+  if (contactForm.email) {
+    contactDetails.push(buildContactDetail('EMAIL', contactForm.email));
+  }
+
+  return contactDetails;
+}
+
+
+export function mapEmployee(event: CenterSaveEvent): Center {
+  const assignedOffice = event.officeForm.assignedOffice;
+
+  const contactDetails: ContactDetail[] = mapContactDetails(event.contactForm);
+
+  const employee: Center = {
+    identifier: event.detailForm.identifier,
+    givenName: event.detailForm.firstName,
+    middleName: event.detailForm.middleName,
+    surname: event.detailForm.lastName,
+    contactDetails: contactDetails,
+    assignedOffice: assignedOffice ? assignedOffice : undefined
+  };
+
+  return employee;
+}
+
+export function mapUser(event: CenterSaveEvent): UserWithPassword {
+  const userWithPassword: UserWithPassword = {
+    identifier: event.detailForm.identifier,
+    password: event.detailForm.password,
+    role: event.detailForm.role
+  };
+
+  return userWithPassword;
+}
diff --git a/src/app/centers/store/effects/notification.effects.ts b/src/app/centers/store/effects/notification.effects.ts
new file mode 100644
index 0000000..3de745e
--- /dev/null
+++ b/src/app/centers/store/effects/notification.effects.ts
@@ -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.
+ */
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as employeeActions from '../employee.actions';
+import {NotificationService, NotificationType} from '../../../services/notification/notification.service';
+
+@Injectable()
+export class EmployeeNotificationEffects {
+
+  @Effect({ dispatch: false })
+  createEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.CREATE_SUCCESS, employeeActions.UPDATE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Employee is going to be saved'
+    }));
+
+  @Effect({ dispatch: false })
+  deleteEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.DELETE_SUCCESS)
+    .do(() => this.notificationService.send({
+      type: NotificationType.MESSAGE,
+      message: 'Employee is going to be deleted'
+    }));
+
+  constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
+
diff --git a/src/app/centers/store/effects/route.effects.ts b/src/app/centers/store/effects/route.effects.ts
new file mode 100644
index 0000000..3e35dd1
--- /dev/null
+++ b/src/app/centers/store/effects/route.effects.ts
@@ -0,0 +1,42 @@
+/**
+ * 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 {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as employeeActions from '../employee.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class EmployeeRouteEffects {
+
+  @Effect({ dispatch: false })
+  createEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.CREATE_SUCCESS, employeeActions.UPDATE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../'], { relativeTo: payload.activatedRoute} ));
+
+  @Effect({ dispatch: false })
+  deleteEmployeeSuccess$: Observable<Action> = this.actions$
+    .ofType(employeeActions.DELETE_SUCCESS)
+    .map(action => action.payload)
+    .do(payload => this.router.navigate(['../../'], { relativeTo: payload.activatedRoute }));
+
+  constructor(private actions$: Actions, private router: Router) { }
+}
diff --git a/src/app/centers/store/effects/service.effects.spec.ts b/src/app/centers/store/effects/service.effects.spec.ts
new file mode 100644
index 0000000..5afcd87
--- /dev/null
+++ b/src/app/centers/store/effects/service.effects.spec.ts
@@ -0,0 +1,201 @@
+/**
+ * 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 {fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
+import {EmployeeApiEffects} from './service.effects';
+import {OfficeService} from '../../../services/office/office.service';
+import {IdentityService} from '../../../services/identity/identity.service';
+import {Observable} from 'rxjs/Observable';
+import {UpdateEmployeeAction, UpdateEmployeeSuccessAction} from '../employee.actions';
+import {Employee} from '../../../services/office/domain/employee.model';
+
+describe('Account Search Api Effects', () => {
+  beforeEach(() => {
+
+    TestBed.configureTestingModule({
+      imports: [
+        EffectsTestingModule
+      ],
+      providers: [
+        EmployeeApiEffects,
+        {
+          provide: OfficeService,
+          useValue: jasmine.createSpyObj('officeService', ['createEmployee', 'updateEmployee', 'setContactDetails'])
+        },
+        {
+          provide: IdentityService,
+          useValue: jasmine.createSpyObj('identityService', ['createUser', 'changeUserRole', 'changePassword'])
+        }
+      ]
+    });
+
+  });
+
+  describe('updateEmployee$', () => {
+
+    function setup() {
+      const officeService = TestBed.get(OfficeService);
+
+      officeService.updateEmployee.and.returnValue(Observable.of({}));
+
+      return {
+        runner: TestBed.get(EffectsRunner),
+        officeService: officeService,
+        identityService: TestBed.get(IdentityService),
+        employeeEffects: TestBed.get(EmployeeApiEffects)
+      };
+    }
+
+    it('should update employee', fakeAsync(() => {
+      const { runner, officeService, employeeEffects } = setup();
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(officeService.updateEmployee).toHaveBeenCalled();
+    }));
+
+    it('should update contact details', fakeAsync(() => {
+      const { runner, officeService, employeeEffects } = setup();
+
+      officeService.setContactDetails.and.returnValue(Observable.of({}));
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        contactDetails: [
+          {
+            type: 'EMAIL',
+            group: 'BUSINESS',
+            value: 'dont@call.me',
+            preferenceLevel: 0
+          }
+        ],
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(officeService.setContactDetails).toHaveBeenCalled();
+    }));
+
+    it('should update password', fakeAsync(() => {
+      const { runner, identityService, employeeEffects } = setup();
+
+      identityService.changePassword.and.returnValue(Observable.of({}));
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        password: 'test',
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(identityService.changePassword).toHaveBeenCalled();
+    }));
+
+    it('should update role', fakeAsync(() => {
+      const { runner, identityService, employeeEffects } = setup();
+
+      identityService.changeUserRole.and.returnValue(Observable.of({}));
+
+      const employee: Employee = {
+        identifier: '',
+        givenName: '',
+        surname: '',
+        contactDetails: []
+      };
+
+      const expectedResult = new UpdateEmployeeSuccessAction({
+        resource: employee,
+        activatedRoute: null
+      });
+
+      runner.queue(new UpdateEmployeeAction({
+        employee: employee,
+        role: 'test',
+        activatedRoute: null
+      }));
+
+      let result = null;
+      employeeEffects.updateEmployee$.subscribe(_result => result = _result);
+
+      tick();
+
+      expect(result).toEqual(expectedResult);
+
+      expect(identityService.changeUserRole).toHaveBeenCalled();
+    }));
+  });
+});
diff --git a/src/app/centers/store/effects/service.effects.ts b/src/app/centers/store/effects/service.effects.ts
new file mode 100644
index 0000000..0a17834
--- /dev/null
+++ b/src/app/centers/store/effects/service.effects.ts
@@ -0,0 +1,95 @@
+/**
+ * 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 {Injectable} from '@angular/core';
+import {OfficeService} from '../../../services/office/office.service';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as employeeActions from '../employee.actions';
+import {IdentityService} from '../../../services/identity/identity.service';
+import {RoleIdentifier} from '../../../services/identity/domain/role-identifier.model';
+import {Password} from '../../../services/identity/domain/password.model';
+
+@Injectable()
+export class EmployeeApiEffects {
+
+  @Effect()
+  createEmployee$: Observable<Action> = this.actions$
+    .ofType(employeeActions.CREATE)
+    .map((action: employeeActions.CreateEmployeeAction) => action.payload)
+    .mergeMap(payload =>
+      this.identityService.createUser(payload.user)
+        .mergeMap(() => this.officeService.createEmployee(payload.employee) )
+        .map(() => new employeeActions.CreateEmployeeSuccessAction({
+          resource: payload.employee,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new employeeActions.CreateEmployeeFailAction(error)))
+    );
+
+  @Effect()
+  updateEmployee$: Observable<Action> = this.actions$
+    .ofType(employeeActions.UPDATE)
+    .map((action: employeeActions.UpdateEmployeeAction) => action.payload)
+    .mergeMap(payload => {
+        const employee = payload.employee;
+        const httpCalls: Observable<any>[] = [];
+
+        httpCalls.push(this.officeService.updateEmployee(employee));
+
+        if (payload.contactDetails) {
+          httpCalls.push(this.officeService.setContactDetails(employee.identifier, payload.contactDetails));
+        }
+
+        if (payload.role) {
+          httpCalls.push(this.identityService.changeUserRole(employee.identifier, new RoleIdentifier(payload.role)));
+        }
+
+        if (payload.password) {
+          httpCalls.push(this.identityService.changePassword(employee.identifier, new Password(payload.password)));
+        }
+
+        return Observable.forkJoin(httpCalls)
+          .map(() => new employeeActions.UpdateEmployeeSuccessAction({
+            resource: payload.employee,
+            activatedRoute: payload.activatedRoute
+          }))
+          .catch((error) => of(new employeeActions.UpdateEmployeeFailAction(error)));
+    });
+
+  @Effect()
+  deleteEmployee$: Observable<Action> = this.actions$
+    .ofType(employeeActions.DELETE)
+    .map((action: employeeActions.DeleteEmployeeAction) => action.payload)
+    .mergeMap(payload => {
+      return Observable.forkJoin(
+        this.officeService.deleteEmployee(payload.employee.identifier),
+        this.identityService.changeUserRole(payload.employee.identifier, new RoleIdentifier('deactivated'))
+      )
+        .map(() => new employeeActions.DeleteEmployeeSuccessAction({
+          resource: payload.employee,
+          activatedRoute: payload.activatedRoute
+        }))
+        .catch((error) => of(new employeeActions.DeleteEmployeeFailAction(error)));
+      }
+    );
+
+  constructor(private actions$: Actions, private officeService: OfficeService, private identityService: IdentityService) { }
+}
diff --git a/src/app/centers/store/employee.actions.ts b/src/app/centers/store/employee.actions.ts
new file mode 100644
index 0000000..75c3a99
--- /dev/null
+++ b/src/app/centers/store/employee.actions.ts
@@ -0,0 +1,149 @@
+/**
+ * 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 {Action} from '@ngrx/store';
+import {type} from '../../store/util';
+import {Employee} from '../../services/office/domain/employee.model';
+import {Error} from '../../services/domain/error.model';
+import {ContactDetail} from '../../services/domain/contact/contact-detail.model';
+import {UserWithPassword} from '../../services/identity/domain/user-with-password.model';
+import {RoutePayload} from '../../common/store/route-payload';
+import {
+  CreateResourceSuccessPayload,
+  DeleteResourceSuccessPayload,
+  LoadResourcePayload,
+  SelectResourcePayload,
+  UpdateResourceSuccessPayload
+} from '../../common/store/resource.reducer';
+
+export const LOAD = type('[Center] Load');
+export const SELECT = type('[Center] Select');
+
+export const CREATE = type('[Center] Create');
+export const CREATE_SUCCESS = type('[Center] Create Success');
+export const CREATE_FAIL = type('[Center] Create Fail');
+
+export const UPDATE = type('[Center] Update');
+export const UPDATE_SUCCESS = type('[Center] Update Success');
+export const UPDATE_FAIL = type('[Center] Update Fail');
+
+export const DELETE = type('[Center] Delete');
+export const DELETE_SUCCESS = type('[Center] Delete Success');
+export const DELETE_FAIL = type('[Center] Delete Fail');
+
+export const RESET_FORM = type('[Center] Reset Form');
+
+export interface EmployeeRoutePayload extends RoutePayload {
+  employee: Employee;
+}
+
+export interface CreateEmployeePayload extends EmployeeRoutePayload {
+  user: UserWithPassword;
+}
+
+export interface UpdateEmployeePayload extends EmployeeRoutePayload {
+  contactDetails?: ContactDetail[];
+  password?: string;
+  role?: string;
+}
+
+export class LoadAction implements Action {
+  readonly type = LOAD;
+
+  constructor(public payload: LoadResourcePayload) { }
+}
+
+export class SelectAction implements Action {
+  readonly type = SELECT;
+
+  constructor(public payload: SelectResourcePayload) { }
+}
+
+export class CreateEmployeeAction implements Action {
+  readonly type = CREATE;
+
+  constructor(public payload: CreateEmployeePayload) { }
+}
+
+export class CreateEmployeeSuccessAction implements Action {
+  readonly type = CREATE_SUCCESS;
+
+  constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateEmployeeFailAction implements Action {
+  readonly type = CREATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class UpdateEmployeeAction implements Action {
+  readonly type = UPDATE;
+
+  constructor(public payload: UpdateEmployeePayload) { }
+}
+
+export class UpdateEmployeeSuccessAction implements Action {
+  readonly type = UPDATE_SUCCESS;
+
+  constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateEmployeeFailAction implements Action {
+  readonly type = UPDATE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class DeleteEmployeeAction implements Action {
+  readonly type = DELETE;
+
+  constructor(public payload: EmployeeRoutePayload) { }
+}
+
+export class DeleteEmployeeSuccessAction implements Action {
+  readonly type = DELETE_SUCCESS;
+
+  constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteEmployeeFailAction implements Action {
+  readonly type = DELETE_FAIL;
+
+  constructor(public payload: Error) { }
+}
+
+export class ResetEmployeeFormAction implements Action {
+  readonly type = RESET_FORM;
+
+  constructor() { }
+}
+
+export type Actions
+  = LoadAction
+  | SelectAction
+  | CreateEmployeeAction
+  | CreateEmployeeSuccessAction
+  | CreateEmployeeFailAction
+  | UpdateEmployeeAction
+  | UpdateEmployeeSuccessAction
+  | UpdateEmployeeFailAction
+  | DeleteEmployeeAction
+  | DeleteEmployeeSuccessAction
+  | DeleteEmployeeFailAction
+  | ResetEmployeeFormAction;
diff --git a/src/app/centers/store/index.ts b/src/app/centers/store/index.ts
new file mode 100644
index 0000000..6b4ea4e
--- /dev/null
+++ b/src/app/centers/store/index.ts
@@ -0,0 +1,51 @@
+/**
+ * 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 * as fromRoot from '../../store';
+import {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../store/index';
+import {createSelector} from 'reselect';
+import {createResourceReducer, getResourceLoadedAt, getResourceSelected, ResourceState} from '../../common/store/resource.reducer';
+import {createFormReducer, FormState, getFormError} from '../../common/store/form.reducer';
+
+export interface State extends fromRoot.State {
+  employees: ResourceState;
+  employeeForm: FormState;
+}
+
+const reducers = {
+  employees: createResourceReducer('Employee'),
+  employeeForm: createFormReducer('Employee')
+};
+
+export const employeeModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export const getEmployeesState = (state: State) => state.employees;
+
+export const getEmployeeFormState = (state: State) => state.employeeForm;
+export const getEmployeeFormError = createSelector(getEmployeeFormState, getFormError);
+
+export const getEmployeesLoadedAt = createSelector(getEmployeesState, getResourceLoadedAt);
+export const getSelectedEmployee = createSelector(getEmployeesState, getResourceSelected);
+
+export class EmployeesStore extends Store<State> {}
+
+export function employeeStoreFactory(appStore: Store<fromRoot.State>) {
+  appStore.replaceReducer(employeeModuleReducer);
+  return appStore;
+}
diff --git a/src/app/centers/user.resolver.ts b/src/app/centers/user.resolver.ts
new file mode 100644
index 0000000..34f29cd
--- /dev/null
+++ b/src/app/centers/user.resolver.ts
@@ -0,0 +1,33 @@
+/**
+ * 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 {Injectable} from '@angular/core';
+import {ActivatedRouteSnapshot, Resolve} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {IdentityService} from '../services/identity/identity.service';
+import {User} from '../services/identity/domain/user.model';
+
+@Injectable()
+export class UserResolver implements Resolve<User> {
+
+  constructor(private identityService: IdentityService) {}
+
+  resolve(route: ActivatedRouteSnapshot): Observable<User> {
+    return this.identityService.getUser(route.params['id']);
+  }
+}
diff --git a/src/app/main/main.component.html b/src/app/main/main.component.html
index ed0ab27..758205a 100644
--- a/src/app/main/main.component.html
+++ b/src/app/main/main.component.html
@@ -38,7 +38,7 @@
         </button>
         <mat-menu #client="matMenu">
           <a mat-menu-item [routerLink]="['/customers']">Client</a>
-          <a mat-menu-item [routerLink]="['/']">Group</a>
+          <a mat-menu-item [routerLink]="['/centers']">Group</a>
           <a mat-menu-item [routerLink]="['/']">Center</a>
         </mat-menu>
       </li>
diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts
index cbe9a42..9e74fa9 100644
--- a/src/app/main/main.component.ts
+++ b/src/app/main/main.component.ts
@@ -122,6 +122,15 @@
       permission: {id: 'accounting_ledgers', accessLevel: 'READ'}
 
     },
+    {
+      title: 'Centers',
+      description: 'View centers',
+      icon: 'event_note',
+      routerLink: '/centers',
+      permission: {id: 'office_employees', accessLevel: 'READ'}
+
+    },
+   
   ];
 
   isLoading$: Observable<boolean>;
diff --git a/src/app/main/main.routing.ts b/src/app/main/main.routing.ts
index 1f81b96..d94d87b 100644
--- a/src/app/main/main.routing.ts
+++ b/src/app/main/main.routing.ts
@@ -38,7 +38,9 @@
       { path: 'deposits', loadChildren: './../depositAccount/deposit-account.module#DepositAccountModule' },
       { path: 'teller', loadChildren: './../teller/teller.module#TellerModule' },
       { path: 'reports', loadChildren: './../reporting/reporting.module#ReportingModule' },
-      { path: 'denied', component: AccessDeniedComponent, data: { title: 'Not allowed' } }
+      { path: 'denied', component: AccessDeniedComponent, data: { title: 'Not allowed' }},
+      { path: 'centers', loadChildren: './../centers/center.module#CenterModule' }
+    
     ]
   },
   {
diff --git a/src/app/services/office/domain/employee.model.ts b/src/app/services/office/domain/employee.model.ts
index 485e515..8350439 100644
--- a/src/app/services/office/domain/employee.model.ts
+++ b/src/app/services/office/domain/employee.model.ts
@@ -26,3 +26,14 @@
   assignedOffice?: string;
   contactDetails: ContactDetail[];
 }
+
+export interface Center {
+  identifier: string;
+  givenName: string;
+  middleName?: string;
+  surname: string;
+  assignedOffice?: string;
+  contactDetails: ContactDetail[];
+}
+
+
diff --git a/src/app/services/security/authz/permission-id.type.ts b/src/app/services/security/authz/permission-id.type.ts
index efbe082..dde53b3 100644
--- a/src/app/services/security/authz/permission-id.type.ts
+++ b/src/app/services/security/authz/permission-id.type.ts
@@ -30,3 +30,4 @@
   'reporting_management' |
   'cheque_management' | 'cheque_transaction' |
   'payroll_configuration' | 'payroll_distribution';
+ 
diff --git a/src/app/services/security/authz/permittable-group-id-mapper.ts b/src/app/services/security/authz/permittable-group-id-mapper.ts
index c81b30e..ac127e5 100644
--- a/src/app/services/security/authz/permittable-group-id-mapper.ts
+++ b/src/app/services/security/authz/permittable-group-id-mapper.ts
@@ -51,6 +51,7 @@
       label: 'User created resources(Offices & Employees)'
     };
 
+   
     this._permittableGroupMap[IdentityPermittableGroupIds.IDENTITY_MANAGEMENT] = {id: 'identity_identities', label: 'Identities'};
     this._permittableGroupMap[IdentityPermittableGroupIds.ROLE_MANAGEMENT] = {id: 'identity_roles', label: 'Roles'};
     this._permittableGroupMap[IdentityPermittableGroupIds.SELF_MANAGEMENT] = {