diff --git a/package.json b/package.json index 12cba8f..857e607 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@ngrx/effects": "^2.0.0", "@ngrx/store": "^2.2.1", "@types/lodash": "4.14.50", + "angular2-image-upload": "^0.5.1", "core-js": "^2.4.1", "lodash": "^4.17.4", "ng2-validation": "^3.9.1", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 14b7f2c..d4a3c8a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -19,6 +19,7 @@ export class AppComponent implements OnInit { ngOnInit() { this.treeElements = [ { title: 'Home', targetUrl: '/home', imageCssClass: 'glyphicon-globe' }, + { title: 'Register', targetUrl: '/register', imageCssClass: 'glyphicon-user' }, { title: 'Search', targetUrl: '/search', imageCssClass: 'glyphicon-search' }, { title: 'Car', targetUrl: '/car', imageCssClass: 'glyphicon-road' }, { title: 'Housing', targetUrl: '/housing', imageCssClass: 'glyphicon-home' } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d44215e..d122a42 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,5 +1,6 @@ +import { registration } from './registration/reducers/registration.reducer'; import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { AppComponent } from './app.component'; @@ -9,23 +10,28 @@ import { WidgitModule } from './widgit/widgit.module'; import { CarRouteModule } from './car/car.route'; import { HomeComponent } from './home/home.component'; import { PageNotFoundComponent } from './404/pageNotFound.component'; -import {StoreModule, ActionReducer, combineReducers} from '@ngrx/store'; -import {cars} from './car/reducers/car.reducer'; -import {EffectsModule} from '@ngrx/effects'; -import {CarEffects} from './car/effects/cars.'; -import {HousingRouteModule} from './housing/housing.route'; -import {houses} from './housing/reducers/houses.reducer'; -import {HousingEffects} from 'app/housing/effects/housing'; - +import { StoreModule, ActionReducer, combineReducers } from '@ngrx/store'; +import { cars } from './car/reducers/car.reducer'; +import { EffectsModule } from '@ngrx/effects'; +import { CarEffects } from './car/effects/cars.'; +import { HousingRouteModule } from './housing/housing.route'; +import { houses } from './housing/reducers/houses.reducer'; +import { HousingEffects } from 'app/housing/effects/housing'; +import { RegistrationModule } from './registration/registration.module'; +import { RegistrationEffects } from './registration/effects/registration'; +import { RegistrationRouteModule } from './registration/registration.route'; +import { ImageUploadModule } from 'angular2-image-upload'; const routes: Routes = [ { path: '', pathMatch: 'full', redirectTo: 'home' }, { path: 'home', component: HomeComponent }, + { path: 'register', loadChildren: './registration/registration.route#RegistrationRouteModule' }, { path: 'car', loadChildren: './car/car.route#CarRouteModule' }, { path: 'housing', loadChildren: './housing/housing.route#HousingRouteModule' }, { path: '**', component: PageNotFoundComponent } ]; @NgModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], declarations: [ AppComponent, HomeComponent, @@ -35,16 +41,19 @@ const routes: Routes = [ RouterModule.forRoot(routes), BrowserModule, FormsModule, + RegistrationModule, ReactiveFormsModule, HttpModule, RouterModule, CarRouteModule, HousingRouteModule, + RegistrationRouteModule, WidgitModule, NgbModule.forRoot(), - StoreModule.provideStore({cars, houses}), + StoreModule.provideStore({ cars, houses, registration }), EffectsModule.run(CarEffects), - EffectsModule.run(HousingEffects) + EffectsModule.run(HousingEffects), + EffectsModule.run(RegistrationEffects) ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index deb8367..eae1217 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -1,3 +1,3 @@

Welcome to the Angular 2 playground! -

+

\ No newline at end of file diff --git a/src/app/registration/actions/registring.ts b/src/app/registration/actions/registring.ts new file mode 100644 index 0000000..4881575 --- /dev/null +++ b/src/app/registration/actions/registring.ts @@ -0,0 +1,54 @@ +import { Registration } from './../domain/registration'; +import { type } from '../../utilities/type'; +import { Action } from '@ngrx/store'; +import { of } from 'rxjs/observable/of'; + +export const RegistrationAction = { + START_REGISTRATION: type('Registration - start registration'), + CREATE_REGISTRATION: type('Registration - Add registration'), + ABORT_REGISTRATIONS: type('Registration - abort registrations'), +}; + + +export class ActionFactory { + + + static startRegistration(registration: Registration): Action { + return new StartRegistrationAction(registration); + } + + static createRegistration(registration: Registration): Action { + return new CreateRegistrationAction(registration); + } + + static abortRegistration(registration: Registration): Action { + return new AbortRegistrationsAction(registration); + } + + static getRegistration(registration) { + return registration; + } + + static empty() { + return new Registration(); + } +} + +export class StartRegistrationAction implements Action { + type = RegistrationAction.START_REGISTRATION; + + constructor(public payload: Registration) { } +} + + +export class CreateRegistrationAction implements Action { + type = RegistrationAction.CREATE_REGISTRATION; + + constructor(public payload: Registration) { } +} + +export class AbortRegistrationsAction implements Action { + type = RegistrationAction.ABORT_REGISTRATIONS; + + constructor(public payload: Registration) { } +} diff --git a/src/app/registration/components/avatar/registration-avatar.component.html b/src/app/registration/components/avatar/registration-avatar.component.html new file mode 100644 index 0000000..6a5420c --- /dev/null +++ b/src/app/registration/components/avatar/registration-avatar.component.html @@ -0,0 +1,18 @@ +
+ +
+
+ +
+
+ +
+
+ + + +
+
+
+ diff --git a/src/app/registration/components/avatar/registration-avatar.component.ts b/src/app/registration/components/avatar/registration-avatar.component.ts new file mode 100644 index 0000000..0e2e09b --- /dev/null +++ b/src/app/registration/components/avatar/registration-avatar.component.ts @@ -0,0 +1,41 @@ +import { ActionFactory } from './../../actions/registring'; +import { RegistrationsState, User } from './../../domain/registration'; +import { Router } from '@angular/router'; +import { Component, OnInit, Input, Output, EventEmitter, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { CustomValidators } from 'ng2-validation'; +import { Registration } from '../../domain/registration'; +import { Store } from '@ngrx/store'; + +@Component({ + selector: 'app-registration-avatar', + templateUrl: 'registration-avatar.component.html', + +}) +export class RegistrationAvatarComponent implements OnInit { + + + public registration: Registration; + + public constructor(private router: Router, private registringStore: Store) { + } + + ngOnInit() { + this.registringStore.select(state => state.registration).subscribe(registration => this.registration = registration); + } + + upload(data: any) { + this.registration.user.avatar = data.src; + } + + public register() { + this.registringStore.dispatch(ActionFactory.createRegistration(this.registration)); + this.router.navigate(['/profile']); + } + + public abort() { + this.registringStore.dispatch(ActionFactory.abortRegistration(this.registration)); + this.router.navigate(['/home']); + } + +} \ No newline at end of file diff --git a/src/app/registration/components/contact/registration-info.component.html b/src/app/registration/components/contact/registration-info.component.html new file mode 100644 index 0000000..e5d5d44 --- /dev/null +++ b/src/app/registration/components/contact/registration-info.component.html @@ -0,0 +1,48 @@ +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ + +
+
+ +
+
\ No newline at end of file diff --git a/src/app/registration/components/contact/registration-info.component.ts b/src/app/registration/components/contact/registration-info.component.ts new file mode 100644 index 0000000..f6a6f55 --- /dev/null +++ b/src/app/registration/components/contact/registration-info.component.ts @@ -0,0 +1,53 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { CustomValidators } from 'ng2-validation'; +import { Registration, User, RegistrationsState } from '../../domain/registration'; +import { Router } from '@angular/router'; +import { Store } from '@ngrx/store'; + +@Component({ + selector: 'app-registration-info', + templateUrl: 'registration-info.component.html', +}) +export class RegistrationInfoComponent implements OnInit { + + @Input() + public registration: Registration; + + public user: User = new User(); + + @Output() + public registrationStarted: EventEmitter; + + @Output() + public registrationAborted: EventEmitter; + + public registrationForm: FormGroup; + public constructor(private formBuilder: FormBuilder) { + this.registrationStarted = new EventEmitter(); + this.registrationAborted = new EventEmitter(); + + this.registrationForm = this.formBuilder.group({ + firstName: [this.user.firstName, Validators.required], + lastName: [this.user.lastName, Validators.required], + username: [this.user.username, Validators.required], + email: [this.user.email, Validators.required], + password: [this.user.password, Validators.required] + }); + } + + ngOnInit() { + } + + public startRegistration() { + /* if (!this.registrationForm.valid) { + return; + }*/ + this.registration.user = this.user; + this.registrationStarted.emit(this.registration); + } + public abort() { + this.registration = new Registration(); + this.registrationAborted.emit(this.registration); + } +} \ No newline at end of file diff --git a/src/app/registration/components/index.ts b/src/app/registration/components/index.ts new file mode 100644 index 0000000..ed1ca2f --- /dev/null +++ b/src/app/registration/components/index.ts @@ -0,0 +1,4 @@ +export * from './avatar/registration-avatar.component' +export * from './contact/registration-info.component' +export * from './profile/profile.component' + diff --git a/src/app/registration/components/profile/profile.component.html b/src/app/registration/components/profile/profile.component.html new file mode 100644 index 0000000..8fb90c8 --- /dev/null +++ b/src/app/registration/components/profile/profile.component.html @@ -0,0 +1,13 @@ +
+

Hi {{registration.user.firstName}}!

+

your profile page

+ {{registration.user.email}} +
+
+
+ +
+
+
+ Logout +
\ No newline at end of file diff --git a/src/app/registration/components/profile/profile.component.ts b/src/app/registration/components/profile/profile.component.ts new file mode 100644 index 0000000..b098397 --- /dev/null +++ b/src/app/registration/components/profile/profile.component.ts @@ -0,0 +1,24 @@ +import { RegistrationsState } from './../../domain/registration'; +import { Store } from '@ngrx/store'; +import { Component, OnInit } from '@angular/core'; +import { User, Registration } from '../../domain/registration'; +import { Router } from '@angular/router'; + +@Component({ + moduleId: module.id, + templateUrl: 'profile.component.html' + +}) +export class ProfileComponent implements OnInit { + public registration: Registration; + + public constructor(private router: Router, private registringStore: Store) { + } + + ngOnInit() { + this.registringStore.select(state => state.registration).subscribe(registration => this.registration = registration); + } + + + +} \ No newline at end of file diff --git a/src/app/registration/containers/registration/registration.component.html b/src/app/registration/containers/registration/registration.component.html new file mode 100644 index 0000000..6b09534 --- /dev/null +++ b/src/app/registration/containers/registration/registration.component.html @@ -0,0 +1,4 @@ +
+

Register

+ +
\ No newline at end of file diff --git a/src/app/registration/containers/registration/registration.component.ts b/src/app/registration/containers/registration/registration.component.ts new file mode 100644 index 0000000..62f6568 --- /dev/null +++ b/src/app/registration/containers/registration/registration.component.ts @@ -0,0 +1,33 @@ +import { Router } from '@angular/router'; +import { Component, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { ActionFactory } from '../../actions/registring'; +import { SearchOptions } from '../../../widgit/search-form/search-options'; +import { RegistrationsState, Registration } from '../../domain/registration'; + +@Component({ + selector: 'app-registration', + templateUrl: 'registration.component.html', +}) +export class RegistrationComponent implements OnInit { + + registration: Registration; + constructor(private router: Router, private registringStore: Store) { + } + + ngOnInit() { + this.registringStore.select(state => state.registration).subscribe(registration => this.registration = registration); + } + + public registrationStarted() { + this.registringStore.dispatch(ActionFactory.startRegistration(this.registration)); + this.router.navigate(['/avatar']); + } + + public registrationAborted() { + this.registringStore.dispatch(ActionFactory.abortRegistration(this.registration)); + this.router.navigate(['/home']); + } + + +} diff --git a/src/app/registration/domain/registration.ts b/src/app/registration/domain/registration.ts new file mode 100644 index 0000000..3cf05e8 --- /dev/null +++ b/src/app/registration/domain/registration.ts @@ -0,0 +1,20 @@ + +export class User { + id: number; + username: string; + email: string; + password: string; + firstName: string; + lastName: string; + avatar: string; + +} + +export class Registration { + status: string; + user: User; +} + +export interface RegistrationsState { + registration: Registration; +} diff --git a/src/app/registration/effects/registration.ts b/src/app/registration/effects/registration.ts new file mode 100644 index 0000000..37e6c8b --- /dev/null +++ b/src/app/registration/effects/registration.ts @@ -0,0 +1,54 @@ +import { RegistrationAction, ActionFactory } from '../actions/registring'; +import { Injectable } from '@angular/core'; +import { Effect, Actions, toPayload } from '@ngrx/effects'; +import { Action } from '@ngrx/store'; +import { Observable } from 'rxjs/Observable'; +import { of } from 'rxjs/observable/of'; +import { empty } from 'rxjs/observable/empty'; +import 'rxjs/add/operator/skip'; +import 'rxjs/add/operator/takeUntil'; +import { StartRegistrationAction } from '../actions/registring'; +import { RegistrationService } from '../service/registration.service'; + +@Injectable() +export class RegistrationEffects { + + @Effect() + startRegistration$: Observable = this.actions$ + .ofType(RegistrationAction.START_REGISTRATION) + .map(toPayload) + .switchMap(newRegistration => { + return this.registrationService.startRegistration(newRegistration) + .catch(error => { + return of(null); + }); + }); + + @Effect() + createRegistration$: Observable = this.actions$ + .ofType(RegistrationAction.CREATE_REGISTRATION) + .map(toPayload) + .switchMap(registration => { + return this.registrationService.createRegistration(registration) + .catch(error => { + return of(null); + }); + }); + + @Effect() + abortRegistration$: Observable = this.actions$ + .ofType(RegistrationAction.ABORT_REGISTRATIONS) + .map(toPayload) + .switchMap(registration => { + return this.registrationService.abortRegistration(registration) + .catch(error => { + return of(null); + }); + }); + + + constructor(private actions$: Actions, private registrationService: RegistrationService) { + } + + +} diff --git a/src/app/registration/guards/registration.guard.ts b/src/app/registration/guards/registration.guard.ts new file mode 100644 index 0000000..77092a5 --- /dev/null +++ b/src/app/registration/guards/registration.guard.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, Router, ActivatedRouteSnapshot } from '@angular/router'; +import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/take'; +import 'rxjs/add/operator/filter'; +import 'rxjs/add/operator/do'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/switchMap'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/let'; +import { RegistrationService } from '../service/registration.service'; +import { ActionFactory } from '../actions/registring'; +import { RegistrationsState } from '../domain/registration'; + +@Injectable() +export class RegistrationGuard implements CanActivate { + + constructor(private store: Store, + private registrationService: RegistrationService) { + } + + getRegistration(): Observable { + return this.registrationService.getLatestRegistration() + .do(registration => { + this.store.dispatch(ActionFactory.getRegistration(registration)) + }) + .map(registration => + ['started','created'].indexOf(registration.status) !== -1) + } + + canActivate(route: ActivatedRouteSnapshot): Observable { + return this.getRegistration(); + } +} diff --git a/src/app/registration/reducers/registration.reducer.ts b/src/app/registration/reducers/registration.reducer.ts new file mode 100644 index 0000000..14f226e --- /dev/null +++ b/src/app/registration/reducers/registration.reducer.ts @@ -0,0 +1,16 @@ +import { Action } from '@ngrx/store'; +import { Registration } from '../domain/registration'; +import { RegistrationAction } from '../actions/registring'; + +export const registration = (state: any = new Registration(), action: Action) => { + switch (action.type) { + case RegistrationAction.START_REGISTRATION: + return action.payload; + case RegistrationAction.CREATE_REGISTRATION: + return action.payload; + case RegistrationAction.ABORT_REGISTRATIONS: + return action.payload; + default: + return state; + } +}; diff --git a/src/app/registration/registration.module.ts b/src/app/registration/registration.module.ts new file mode 100644 index 0000000..fc34876 --- /dev/null +++ b/src/app/registration/registration.module.ts @@ -0,0 +1,43 @@ +import { RouterModule } from '@angular/router'; +import { ImageUploadModule } from 'angular2-image-upload'; +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { WidgitModule } from '../widgit/widgit.module'; +import { UtilitiesModule } from '../utilities/utilities.module'; +import { RegistrationComponent } from './containers/registration/registration.component'; +import { ProfileComponent, RegistrationInfoComponent, RegistrationAvatarComponent } from './components/index'; +import { RegistrationService } from './service/registration.service'; +import { RegistrationGuard } from './guards/registration.guard'; + +@NgModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + imports: [ + CommonModule, + WidgitModule, + UtilitiesModule, + NgbModule, + FormsModule, + RouterModule, + ImageUploadModule.forRoot(), + ReactiveFormsModule + ], + exports: [ + ProfileComponent, + RegistrationAvatarComponent, + RegistrationInfoComponent, + RegistrationComponent, + ], + declarations: [ + ProfileComponent, + RegistrationAvatarComponent, + RegistrationInfoComponent, + RegistrationComponent, + ], + providers: [ + RegistrationService, + RegistrationGuard + ] +}) +export class RegistrationModule { } diff --git a/src/app/registration/registration.route.ts b/src/app/registration/registration.route.ts new file mode 100644 index 0000000..87a39c3 --- /dev/null +++ b/src/app/registration/registration.route.ts @@ -0,0 +1,48 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { RegistrationInfoComponent } from './components/contact/registration-info.component'; +import { RegistrationAvatarComponent } from './components/avatar/registration-avatar.component'; +import { ProfileComponent } from './components/profile/profile.component'; +import { RegistrationGuard } from './guards/registration.guard'; +import { RegistrationModule } from './registration.module'; +import { RegistrationComponent } from './containers/registration/registration.component'; + + +const routes: Routes = [ + { + path: 'register', + redirectTo: '/register/init', + pathMatch: 'full' + }, + { + path: 'init', + component: RegistrationComponent + }, + { + path: 'contact', + component: RegistrationInfoComponent + }, + { + path: 'avatar', + canActivate: [RegistrationGuard], + component: RegistrationAvatarComponent + }, + { + path: 'profile', + canActivate: [RegistrationGuard], + component: ProfileComponent + } + +]; + +@NgModule({ + exports: [ + ], + imports: [ + RouterModule.forChild(routes), + RegistrationModule, + CommonModule + ], declarations: [] +}) +export class RegistrationRouteModule { } diff --git a/src/app/registration/service/registration.service.ts b/src/app/registration/service/registration.service.ts new file mode 100644 index 0000000..dca11eb --- /dev/null +++ b/src/app/registration/service/registration.service.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@angular/core'; +import { Response, Http } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; +import { of } from 'rxjs/observable/of'; +import 'rxjs/add/operator/map'; +import { Registration } from '../domain/registration'; + +@Injectable() +export class RegistrationService { + + private record: Registration; + + constructor(private http: Http) { + this.record = new Registration(); + } + + startRegistration(newRegistration: Registration): Observable { + this.record.status = 'started'; + this.record.user = newRegistration.user; + return of(this.record); + } + + createRegistration(registration: Registration): Observable { + this.record.status = 'created'; + this.record.user = registration.user; + return of(this.record); + } + + abortRegistration(registration: Registration): Observable { + this.record.status = 'aborted'; + this.record.user = registration.user; + return of(registration); + } + + getLatestRegistration() { + return of(this.record); + } + +} diff --git a/src/app/widgit/widgit.module.ts b/src/app/widgit/widgit.module.ts index ba40f06..b1565f5 100644 --- a/src/app/widgit/widgit.module.ts +++ b/src/app/widgit/widgit.module.ts @@ -2,22 +2,24 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { CommonModule } from '@angular/common'; import { ReactiveFormsModule } from '@angular/forms'; -import {UtilitiesModule} from '../utilities/utilities.module'; -import {SearchFormComponent} from './search-form/search-form.component'; +import { UtilitiesModule } from '../utilities/utilities.module'; +import { SearchFormComponent } from './search-form/search-form.component'; import { NavigationComponent } from './navigation/navigation.component'; import { NavigationItemComponent } from './navigation/navigation-item.component'; + @NgModule({ imports: [ CommonModule, RouterModule, UtilitiesModule, - ReactiveFormsModule + ReactiveFormsModule, + ], exports: [ SearchFormComponent, NavigationComponent, - NavigationItemComponent + NavigationItemComponent, ], declarations: [ SearchFormComponent,