Adds functionality for user to change their password when they are logged into the site.
parent
89c5917ed7
commit
b8de3f2d0b
|
@ -26,10 +26,12 @@ import { Account } from '@account/models/account.model';
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { MembershipType } from '@account/models/membership.model';
|
import { MembershipType } from '@account/models/membership.model';
|
||||||
import { handleError } from '@account/app/app.service';
|
import { handleError } from '@account/app/app.service';
|
||||||
|
import { AbstractControl } from '@angular/forms';
|
||||||
|
|
||||||
|
|
||||||
// URLs for the http requests
|
// URLs for the http requests
|
||||||
const ACCOUNT_URL = '/api/account';
|
const ACCOUNT_URL = '/api/account';
|
||||||
|
const CHANGE_PASSWORD_URL = '/api/password-change';
|
||||||
const MEMBERSHIP_URL = '/api/memberships';
|
const MEMBERSHIP_URL = '/api/memberships';
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,4 +82,9 @@ export class ProfileService {
|
||||||
deleteAccount() {
|
deleteAccount() {
|
||||||
return this.http.delete(ACCOUNT_URL);
|
return this.http.delete(ACCOUNT_URL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changePassword(passwordControl: AbstractControl) {
|
||||||
|
const codedPassword = btoa(passwordControl.value);
|
||||||
|
return this.http.put(CHANGE_PASSWORD_URL, {password: codedPassword});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,6 @@
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<a mat-button>CHANGE EMAIL ADDRESS</a>
|
<a mat-button>CHANGE EMAIL ADDRESS</a>
|
||||||
<a mat-button>CHANGE PASSWORD</a>
|
<a mat-button [href]="changePasswordUrl">CHANGE PASSWORD</a>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { Component, Input } from '@angular/core';
|
||||||
import { faAddressCard, IconDefinition } from '@fortawesome/free-solid-svg-icons';
|
import { faAddressCard, IconDefinition } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
import { Account } from '@account/models/account.model';
|
import { Account } from '@account/models/account.model';
|
||||||
|
import { environment } from '@account/environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'account-login-edit',
|
selector: 'account-login-edit',
|
||||||
|
@ -30,6 +31,7 @@ import { Account } from '@account/models/account.model';
|
||||||
export class LoginComponent {
|
export class LoginComponent {
|
||||||
@Input() account: Account;
|
@Input() account: Account;
|
||||||
public loginIcon: IconDefinition = faAddressCard;
|
public loginIcon: IconDefinition = faAddressCard;
|
||||||
|
public changePasswordUrl = environment.mycroftUrls.account + '/password-change';
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<mat-card>
|
||||||
|
<mat-card-header>
|
||||||
|
<fa-icon mat-card-avatar [icon]="changePasswordIcon"></fa-icon>
|
||||||
|
<mat-card-title>Change your password</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>New Password</mat-label>
|
||||||
|
<input matInput required [type]="showPassword ? 'text' : 'password'" [formControl]="passwordControl">
|
||||||
|
<mat-error *ngIf="passwordControl.hasError('required')">
|
||||||
|
Password is required
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-icon-button (click)="showHidePassword()">
|
||||||
|
<fa-icon [icon]="showPassword ? showIcon : hideIcon"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
id="submit-button"
|
||||||
|
[disabled]="passwordControl.invalid"
|
||||||
|
(click)="onChangePassword()"
|
||||||
|
>
|
||||||
|
SUBMIT
|
||||||
|
</button>
|
||||||
|
<button mat-button id="cancel-button" (click)="onCancel()">CANCEL</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
|
@ -0,0 +1,53 @@
|
||||||
|
// *****************************************************************************
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Copyright (c) Mycroft AI Inc. All rights reserved.
|
||||||
|
// Licensed 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
|
||||||
|
//
|
||||||
|
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||||
|
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||||
|
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||||
|
//
|
||||||
|
// See the Apache Version 2.0 License for specific language governing permissions
|
||||||
|
// and limitations under the License.
|
||||||
|
// *****************************************************************************
|
||||||
|
|
||||||
|
@use "@angular/material" as mat;
|
||||||
|
@use "components/buttons" as buttons;
|
||||||
|
@use "components/cards" as cards;
|
||||||
|
@use 'mycroft-theme' as theme;
|
||||||
|
|
||||||
|
mat-card {
|
||||||
|
@include cards.selene-card;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-top: 48px;
|
||||||
|
|
||||||
|
mat-card-content {
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-top: -8px;
|
||||||
|
fa-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
color: mat.get-color-from-palette(theme.$mycroft-primary, 500);
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
width: 240px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-card-actions {
|
||||||
|
#cancel-button {
|
||||||
|
background-color: white;
|
||||||
|
color: mat.get-color-from-palette(theme.$mycroft-accent, 800);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*! *****************************************************************************
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
|
||||||
|
Copyright (c) Mycroft AI Inc. All rights reserved.
|
||||||
|
Licensed 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
|
||||||
|
|
||||||
|
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||||
|
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||||
|
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||||
|
|
||||||
|
See the Apache Version 2.0 License for specific language governing permissions
|
||||||
|
and limitations under the License.
|
||||||
|
***************************************************************************** */
|
||||||
|
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { UntypedFormControl, Validators } from '@angular/forms';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';
|
||||||
|
|
||||||
|
import {faEye, faEyeSlash, faUserSecret, IconDefinition } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
|
import { ProfileService } from '@account/http/profile.service';
|
||||||
|
import { SnackbarComponent } from 'shared';
|
||||||
|
|
||||||
|
const fiveSeconds = 5000;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'account-change-password',
|
||||||
|
templateUrl: './change-password.component.html',
|
||||||
|
styleUrls: ['./change-password.component.scss']
|
||||||
|
})
|
||||||
|
export class ChangePasswordComponent implements OnInit {
|
||||||
|
public changePasswordIcon: IconDefinition = faUserSecret;
|
||||||
|
public hideIcon: IconDefinition = faEyeSlash;
|
||||||
|
public passwordControl = new UntypedFormControl(null, [Validators.required]);
|
||||||
|
public showIcon: IconDefinition = faEye;
|
||||||
|
public showPassword = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private profileService: ProfileService,
|
||||||
|
private snackbar: MatSnackBar,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangePassword() {
|
||||||
|
this.profileService.changePassword(this.passwordControl).subscribe({
|
||||||
|
next: () => { this.openSuccessSnackbar(); },
|
||||||
|
error: () => { this.openErrorSnackbar(); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openErrorSnackbar() {
|
||||||
|
const config = new MatSnackBarConfig();
|
||||||
|
config.data = {type: 'error', message: 'An error occurred changing the password'};
|
||||||
|
this.snackbar.openFromComponent(SnackbarComponent, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
openSuccessSnackbar() {
|
||||||
|
const config = new MatSnackBarConfig();
|
||||||
|
config.duration = fiveSeconds;
|
||||||
|
config.data = {type: 'success', message: 'Password successfully changed'};
|
||||||
|
const successSnackbar = this.snackbar.openFromComponent(SnackbarComponent, config);
|
||||||
|
successSnackbar.afterDismissed().subscribe(
|
||||||
|
() => { this.router.navigate(['/profile']); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
this.router.navigate(['/profile']);
|
||||||
|
}
|
||||||
|
|
||||||
|
showHidePassword() {
|
||||||
|
this.showPassword = !this.showPassword;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,10 +19,11 @@ and limitations under the License.
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
import { AccountResolverService } from '../../core/guards/account-resolver.service';
|
import { AccountResolverService } from '@account/app/core/guards/account-resolver.service';
|
||||||
import { MembershipResolverService } from '../../core/guards/membership-resolver.service';
|
import { MembershipResolverService } from '@account/app/core/guards/membership-resolver.service';
|
||||||
import { EditComponent } from './pages/edit/edit.component';
|
import { EditComponent } from './pages/edit/edit.component';
|
||||||
import { NewComponent } from './pages/new/new.component';
|
import { NewComponent } from './pages/new/new.component';
|
||||||
|
import { ChangePasswordComponent } from '@account/app/modules/profile/pages/change-password/change-password.component';
|
||||||
|
|
||||||
const profileRoutes: Routes = [
|
const profileRoutes: Routes = [
|
||||||
{
|
{
|
||||||
|
@ -39,6 +40,10 @@ const profileRoutes: Routes = [
|
||||||
account: AccountResolverService,
|
account: AccountResolverService,
|
||||||
membershipTypes: MembershipResolverService
|
membershipTypes: MembershipResolverService
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'password-change',
|
||||||
|
component: ChangePasswordComponent
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||||
import { NgxStripeModule } from 'ngx-stripe';
|
import { NgxStripeModule } from 'ngx-stripe';
|
||||||
|
|
||||||
import { AgreementsComponent } from './components/cards/agreements/agreements.component';
|
import { AgreementsComponent } from './components/cards/agreements/agreements.component';
|
||||||
|
import { ChangePasswordComponent } from './pages/change-password/change-password.component';
|
||||||
import { DeleteComponent } from './components/cards/delete/delete.component';
|
import { DeleteComponent } from './components/cards/delete/delete.component';
|
||||||
import { EditComponent } from './pages/edit/edit.component';
|
import { EditComponent } from './pages/edit/edit.component';
|
||||||
import { environment} from '../../../environments/environment';
|
import { environment} from '../../../environments/environment';
|
||||||
|
@ -56,6 +57,7 @@ import { MembershipStepComponent } from './components/views/membership-step/memb
|
||||||
declarations: [
|
declarations: [
|
||||||
// Profile view and edit
|
// Profile view and edit
|
||||||
AgreementsComponent,
|
AgreementsComponent,
|
||||||
|
ChangePasswordComponent,
|
||||||
DeleteComponent,
|
DeleteComponent,
|
||||||
EditComponent,
|
EditComponent,
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
|
|
Loading…
Reference in New Issue