Skip to content

Commit

Permalink
Merge pull request #21 from mathieuher/preview/resetPassword
Browse files Browse the repository at this point in the history
Preview/reset password
  • Loading branch information
mathieuher authored Jan 4, 2025
2 parents 0a8d456 + 58714af commit 8b76acd
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 3 deletions.
4 changes: 3 additions & 1 deletion roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
- Improve skier sprites when turning
- Define new default tracks, records and ghosts
- Improve online data list to match big items list
- Use mailing server
- Define premium url & link
- Add verification step
- ✔️ ~~Add password management step~~
- ✔️ ~~Use mailing server~~
- ✔️ ~~Implement async multiplayer~~
- ✔️ ~~Improve spectators dynamic and animation~~
- ✔️ ~~Rework skier dynamic~~
Expand Down
12 changes: 12 additions & 0 deletions src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,17 @@ export const routes: Routes = [
data: { type: 'online' },
canActivate: [AuthGuard, AvailableGuard]
},
{
path: 'reset-password',
loadComponent: () =>
import('./pages/reset-password/reset-password.component').then(m => m.ResetPasswordComponent),
canActivate: [AvailableGuard]
},
{
path: 'reset-password/:token',
loadComponent: () =>
import('./pages/reset-password/reset-password.component').then(m => m.ResetPasswordComponent),
canActivate: [AvailableGuard]
},
{ path: '**', pathMatch: 'full', redirectTo: '' }
];
30 changes: 30 additions & 0 deletions src/app/common/scss/components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
border: 1px solid var(--color-primary);
color: var(--color-primary);

&.placeholder {
margin-top: 1rem;
}

&.tertiary {
border: 1px solid var(--color-tertiary);
color: var(--color-tertiary);
Expand Down Expand Up @@ -73,6 +77,11 @@
position: sticky;
top: 0;
background-color: var(--color-surface);

&.placeholder {
color: var(--color-tertiary);
text-align: center;
}
}

.retro-subtitle {
Expand All @@ -92,6 +101,10 @@
color: var(--color-primary);
font-weight: 400;

&.placeholder {
text-align: center;
}

&.tertiary {
color: var(--color-tertiary);
}
Expand All @@ -101,11 +114,28 @@
}
}

.retro-hint {
font-size: var(--text-small);
color: var(--color-primary-light);
text-align: right;
}

.retro-link {
color: var(--color-tertiary);
font-weight: 500;
}

.retro-placeholder-image {
display: flex;
padding: 2rem;
justify-content: center;
align-items: center;
img {
fill: var(--color-tertiary);
color: var(--color-tertiary);
}
}

.retro-placeholder {
font-size: var(--text-small);
padding: 1rem 0.5rem;
Expand Down
8 changes: 8 additions & 0 deletions src/app/common/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,12 @@ export class AuthService {
? User.buildFromRecord(environment.pb.authStore.record as RecordModel)
: null;
}

public sendResetPasswordMail(email: string): Promise<boolean> {
return environment.pb.collection('users').requestPasswordReset(email);
}

public changePassword(password: string, passwordConfirm: string, token: string): Promise<boolean> {
return environment.pb.collection('users').confirmPasswordReset(token, password, passwordConfirm);
}
}
9 changes: 8 additions & 1 deletion src/app/pages/login/login.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
</app-toolbar>
<div class="retro-content">
@if(!serverAvailable()) {
<div class="retro-placeholder fill">Unfortunately online services are not available, please try again later.</div>
<div class="retro-placeholder fill">
Unfortunately online services are not available, please try again later.
</div>
} @else {
<div class="retro-title">Ride online</div>
<div class="retro-text">
Expand Down Expand Up @@ -33,6 +35,11 @@
placeholder="6 to 16 char."
/>
</div>
<div class="retro-hint">
<a class="retro-link" routerLink="/reset-password">
Forgot password ?
</a>
</div>
@if(loginError()) {
<div class="retro-text error">{{ loginError() }}</div>
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/pages/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface RegisterForm extends LoginForm {
name: FormControl<string | null>;
}

const emailValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
export const emailValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
let email: string = control?.value;
if (email?.length) {
const regex = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
Expand Down
101 changes: 101 additions & 0 deletions src/app/pages/reset-password/reset-password.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<app-toolbar>
<ng-container ngProjectAs="left-actions">
<app-button-icon
icon="arrow_back"
routerLink="/login"
></app-button-icon>
</ng-container>
</app-toolbar>
<div class="retro-content">
@if(token) { @if(passwordChanged && passwordChanged()) {
<div class="retro-placeholder-image">
<img src="assets/icons/password.svg" />
</div>
<div class="retro-title placeholder">Password updated</div>
<div class="retro-text placeholder">
Password updated successfully! You're all set to get back on the
mountain and enjoy the ride.
</div>
<button class="retro-button placeholder" routerLink="/login">
Go to login
</button>
} @else {
<div class="retro-title">Set your new password</div>
<div class="form-container">
<div class="retro-form-line">
<div class="label">Password</div>
<input
class="retro-input"
type="password"
[formControl]="password"
placeholder="6 to 16 char."
/>
</div>
<div class="retro-form-line">
<div class="label">Confirm password</div>
<input
class="retro-input"
type="password"
[formControl]="passwordConfirm"
placeholder="6 to 16 char."
/>
</div>
<button
class="retro-button"
[disabled]="
password.invalid ||
passwordConfirm.invalid ||
password.value !== passwordConfirm.value
"
(click)="changePassword()"
>
Save your new password
</button>
@if(password.valid && passwordConfirm.valid && password.value !==
passwordConfirm.value ) {
<div class="retro-text error">Passwords don’t match. Try again.</div>
} @if(passwordChanged && !passwordChanged()) {
<div class="retro-text error">
Something went wrong while updating your password. Please try again.
</div>
}
</div>
} } @else { @if(mailSent()) {
<div class="retro-placeholder-image">
<img src="assets/icons/mailSent.svg" />
</div>
<div class="retro-title placeholder">Mail sent</div>
<div class="retro-text placeholder">
Check your inbox for a message from us. Follow the instructions in the
email to reset your password and get back on track!
</div>
} @else {
<div class="retro-title">Reset your password</div>
<div class="retro-text">
Forgot your password? No worries, we’ve got you covered!
</div>
<div class="retro-text">
Simply enter your email below and click the button. You’ll receive an
email shortly with instructions to reset your password and create a new
one.
</div>
<div class="form-container">
<div class="retro-form-line">
<div class="label">Email</div>
<input
class="retro-input"
type="email"
[formControl]="email"
placeholder="Profile email"
/>
</div>
<button
class="retro-button"
[disabled]="email.invalid"
(click)="sendMail()"
>
Send password reset mail
</button>
</div>
} }
</div>
13 changes: 13 additions & 0 deletions src/app/pages/reset-password/reset-password.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
:host {
display: flex;
flex: 1 1 auto;
flex-direction: column;
padding: 1rem;

.form-container {
padding: 1rem 0;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
}
56 changes: 56 additions & 0 deletions src/app/pages/reset-password/reset-password.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ChangeDetectionStrategy, Component, inject, signal, type Signal } from '@angular/core';
import { ToolbarComponent } from '../../common/components/toolbar/toolbar.component';
import { ButtonIconComponent } from '../../common/components/button-icon/button-icon.component';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { emailValidator } from '../login/login.component';
import { AuthService } from '../../common/services/auth.service';

@Component({
selector: 'app-reset-password',
imports: [ButtonIconComponent, ReactiveFormsModule, RouterLink, ToolbarComponent],
templateUrl: './reset-password.component.html',
styleUrl: './reset-password.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ResetPasswordComponent {
private authService = inject(AuthService);
private route = inject(ActivatedRoute);

protected mailSent = signal(false);
protected passwordChanged?: Signal<boolean>;
protected email = new FormControl('', [Validators.required, emailValidator]);
protected password = new FormControl('', [Validators.required, Validators.minLength(6), Validators.maxLength(16)]);
protected passwordConfirm = new FormControl('', [
Validators.required,
Validators.minLength(6),
Validators.maxLength(16)
]);
protected readonly token = (this.route.snapshot.params as { token: string }).token;

protected sendMail(): void {
if (this.email.valid) {
this.authService.sendResetPasswordMail(this.email.value!).then(success => {
if (success) {
this.mailSent.set(true);
this.email.reset();
}
});
}
}

protected changePassword(): void {
if (
this.token &&
this.password.valid &&
this.passwordConfirm.valid &&
this.password.value === this.passwordConfirm.value
) {
this.authService
.changePassword(this.password.value!, this.passwordConfirm.value!, this.token)
.then(result => {
this.passwordChanged = signal(result);
});
}
}
}
1 change: 1 addition & 0 deletions src/assets/icons/mailSent.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/password.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 8b76acd

Please sign in to comment.