Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/user info edit #69

Merged
merged 11 commits into from
Nov 26, 2017
4 changes: 4 additions & 0 deletions src/app/_domains/change-password-form.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface ChangePasswordForm {
password: string;
new_password: string;
}
7 changes: 7 additions & 0 deletions src/app/_domains/edit-user-info-form.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {NotificationSettings} from './notification-settings';

export interface EditUserInfoForm {
phone: string;
notification: NotificationSettings;
password: string;
}
29 changes: 29 additions & 0 deletions src/app/_effect-actions/password-change.actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {RequestError} from '../_domains/request-error';
import {Action} from '@ngrx/store';
import {ChangePasswordForm} from '../_domains/change-password-form';
import {AuthResponse} from '../_domains/auth-response';

export const REQUEST = 'PasswordChangeActions.REQUEST';
export const SUCCESS = 'PasswordChangeActions.SUCCESS';
export const ERROR = 'PasswordChangeActions.ERROR';

export class Request implements Action {
readonly type = REQUEST;

constructor(public payload: ChangePasswordForm, public userId) {
}
}

export class Success implements Action {
readonly type = SUCCESS;

constructor(public payload: AuthResponse) {
}
}

export class Error implements Action {
readonly type = ERROR;

constructor(public payload: RequestError) {
}
}
25 changes: 25 additions & 0 deletions src/app/_effect-actions/user-info-update.actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {RequestError} from '../_domains/request-error';
import {Action} from '@ngrx/store';
import {EditUserInfoForm} from '../_domains/edit-user-info-form';

export const REQUEST = 'UserInfoUpdateActions.REQUEST';
export const SUCCESS = 'UserInfoUpdateActions.SUCCESS';
export const ERROR = 'UserInfoUpdateActions.ERROR';

export class Request implements Action {
readonly type = REQUEST;

constructor(public payload: EditUserInfoForm, public userId) {
}
}

export class Success implements Action {
readonly type = SUCCESS;
}

export class Error implements Action {
readonly type = ERROR;

constructor(public payload: RequestError) {
}
}
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {EffectsModule} from '@ngrx/effects';
import {UserModule} from './user/user.module';
import {PropertiesModule} from './properties/properties.module';
import {AccessPurchaseModule} from './access-purchase/access-purchase.module';
import {PasswordResetModule} from './password-reset/password-reset.module';

@NgModule({
declarations: [
Expand Down Expand Up @@ -57,6 +58,7 @@ import {AccessPurchaseModule} from './access-purchase/access-purchase.module';
UserModule,
PropertiesModule,
AccessPurchaseModule,
PasswordResetModule,
ClarityModule.forRoot(),
],
bootstrap: [AppComponent]
Expand Down
10 changes: 9 additions & 1 deletion src/app/app.routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import {AuthGuard} from './auth/auth.guard';
import {VisitorGuard} from './auth/visitor.guard';
import {PropertyListPageComponent} from './properties/property-list-page/property-list-page.component';
import {PropertyPageComponent} from './properties/property-page/property-page.component';
import {PasswordResetRequestPageComponent} from './password-reset/password-reset-request-page/password-reset-request-page.component';
import {PasswordResetPageComponent} from './password-reset/password-reset-page/password-reset-page.component';
import {UserInfoEditPageComponent} from './user/user-info-edit-page/user-info-edit-page.component';
import {PasswordChangePageComponent} from './user/password-change-page/password-change-page.component';

const routes: Routes = [
{path: '', pathMatch: 'full', redirectTo: 'welcome'},
Expand All @@ -26,11 +30,15 @@ const routes: Routes = [
{path: 'account/reactivate', component: ActivationLinkRequestPageComponent, canActivate: [VisitorGuard]},
{path: 'account/deactivate', component: DeactivateAccountComponent, canActivate: [AuthGuard]},
{path: 'account/info', component: AccountInfoPageComponent, canActivate: [AuthGuard]},
{path: 'account/info/edit', component: UserInfoEditPageComponent, canActivate: [AuthGuard]},
{path: 'account/info/change-password', component: PasswordChangePageComponent, canActivate: [AuthGuard]},
{path: 'access/:type', component: AccessPurchasePageComponent, canActivate: [AuthGuard]},
{path: 'property/create', component: CreateListingPageComponent, canActivate: [AuthGuard]},
{path: 'property/:id/remove', component: RemoveListingPageComponent, canActivate: [AuthGuard]},
{path: 'properties', component: PropertyListPageComponent},
{path: 'properties/:id', component: PropertyPageComponent}
{path: 'properties/:id', component: PropertyPageComponent},
{path: 'reset-password/request-link', component: PasswordResetRequestPageComponent, canActivate: [VisitorGuard]},
{path: 'reset-password/form', component: PasswordResetPageComponent, canActivate: [VisitorGuard]},
];

@NgModule({
Expand Down
2 changes: 1 addition & 1 deletion src/app/core/api-endpoints.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const API = {
method: 'POST',
url: '/users/${userId}/check-password'
},
UPDATE_PASSWORD: {
CHANGE_PASSWORD: {
method: 'PUT',
url: '/users/${userId}/password'
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<div class="sidenav" style="margin-left:20px">
<p><strong>Step 1:</strong> Change Password</p>
</div>

<!-- MAIN CONTENT AREA-->
<div class="content-area">
<h2>Enter Your New Password</h2>
<p>Please enter your old/new passwords</p>
<form novalidate (ngSubmit)="onSubmit(passwordChangeForm)" [formGroup]="passwordChangeForm">
<section class="form-block">
<div class="form-group row">
<div class="col-lg-2 col-md-12 col-sm-12 col-xs-12">
<label for="password-input">Old Password</label>
</div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<label for="password-input"
aria-haspopup="true"
role="tooltip"
class="tooltip tooltip-validation tooltip-sm"
[class.invalid]="passwordChangeForm.get('password').invalid && passwordChangeForm.get('password').touched">
<input class="form-control" type="password" id="password-input" placeholder="Enter your old password"
size="45"
formControlName="password">
<span class="tooltip-content">
Please enter your old password
</span>
</label>
</div>
</div>
</section>
<section class="form-block">
<div class="form-group row">
<div class="col-lg-2 col-md-12 col-sm-12 col-xs-12">
<label for="new-password-input">New Password</label>
</div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<label for="new-password-input"
aria-haspopup="true"
role="tooltip"
class="tooltip tooltip-validation tooltip-sm"
[class.invalid]="passwordChangeForm.get('new_password').invalid && passwordChangeForm.get('new_password').touched">
<input class="form-control" type="password" id="new-password-input" placeholder="Enter your new password" size="45"
formControlName="new_password">
<span class="tooltip-content">
Password must be of length > 8 and include at least one alphabet and at least one digit
</span>
</label>

</div>
</div>
</section>
<section class="form-block">
<div class="row">
<div class="col-lg-2">
</div>
<div class="col-lg-6">
<button type="submit" class="btn btn-danger">Change My Password</button>
<button type="button" class="btn btn-link" [routerLink]="['/account/info']">Take me back to my account
</button>
</div>
<div class="col-lg-4">
</div>
</div>
</section>
</form>
</div>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';

import {PasswordChangePageComponent} from './password-change-page.component';
import {ReactiveFormsModule} from '@angular/forms';
import {RouterTestingModule} from '@angular/router/testing';
import {UserInfoService} from '../user-info.service';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/of';

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

const mockService = {
userInfo$: Observable.of({
phone: '12312341234',
notification: true
}),
update: jasmine.createSpy('update'),
changePassword: jasmine.createSpy('changePassword')
};

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
ReactiveFormsModule
],
providers: [
{provide: UserInfoService, useValue: mockService}
],
declarations: [PasswordChangePageComponent]
})
.compileComponents();
}));

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

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {Component, HostBinding, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {UserInfoService} from '../user-info.service';

@Component({
selector: 'app-password-change-page',
templateUrl: './password-change-page.component.html',
styleUrls: ['./password-change-page.component.scss']
})
export class PasswordChangePageComponent implements OnInit {
@HostBinding('class') cssClass = 'content-container';

passwordChangeForm: FormGroup;

constructor(private _service: UserInfoService, private _fb: FormBuilder) {
this.passwordChangeForm = this._fb.group({
password: ['', Validators.compose([
Validators.required
])],
new_password: ['', Validators.compose([
Validators.required,
Validators.pattern(/[a-zA-Z0-9!@#\$%\^&.]{8,32}/),
Validators.pattern(/.*[A-Za-z]+.*/),
Validators.pattern(/.*[0-9]+.*/)
])]
});
}

ngOnInit() {
}

onSubmit({value, valid}) {
if (valid) {
this._service.changePassword(value);
}
}
}
53 changes: 53 additions & 0 deletions src/app/user/password-change.effects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Actions, Effect} from '@ngrx/effects';
import {Action} from '@ngrx/store';
import {RestApiService} from '../core/rest-api.service';
import {AlertActions} from '../_actions/alert.actions';
import {RequestError} from '../_domains/request-error';
import {API} from '../core/api-endpoints.constant';
import {RestApiRequest} from '../core/rest-api-request';
import * as PasswordChangeActions from '../_effect-actions/password-change.actions';
import * as AuthActions from '../_actions/auth.actions';
import * as WishlistActions from '../_actions/wishlist.actions';
import {AuthResponse} from '../_domains/auth-response';
import {Wishlist} from '../_domains/wishlist';
import {Auth} from '../_domains/auth';

@Injectable()
export class PasswordChangeEffects {
@Effect() onRequest$: Observable<Action> = this.actions$
.ofType(PasswordChangeActions.REQUEST)
.switchMap((action: PasswordChangeActions.Request) => {
const request = new RestApiRequest(API.USER.CHANGE_PASSWORD);
request.setPathParams({userId: action.userId});
request.setBody(action.payload);

return this._api.request(request)
.map(response => new PasswordChangeActions.Success(response))
.catch(error => Observable.of(new PasswordChangeActions.Error(error)));
});

@Effect() onSuccess$: Observable<Action> = this.actions$
.ofType(PasswordChangeActions.SUCCESS)
.map((action: PasswordChangeActions.Success) => action.payload)
.mergeMap((payload: AuthResponse) => [
new AuthActions.Set(new Auth(payload.token, payload.user.email, payload.user.id)),
new WishlistActions.Set(new Wishlist(payload.user.wishlist)),
new AlertActions.SetInfo('Password Changed')
]);

@Effect() onError$: Observable<Action> = this.actions$
.ofType(PasswordChangeActions.ERROR)
.map((action: PasswordChangeActions.Error) => action.payload)
.map((error: RequestError) => {
switch (error.status) {

default:
return new AlertActions.SetError('Unknown Error');
}
});

constructor(private _api: RestApiService, private actions$: Actions) {
}
}
2 changes: 2 additions & 0 deletions src/app/user/personal-info/personal-info.component.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<div>E-mail: {{email}}</div>
<div>Phone: {{phone}}</div>
<clr-checkbox [clrDisabled]="true" [clrChecked]="notification">Receive E-mail Notifications</clr-checkbox>
<button class="btn btn-block" routerLink="change-password">Change Password</button>
<button class="btn btn-block" routerLink="edit">Edit User Info</button>
Loading