Add init link.
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { InputsModule } from '@progress/kendo-angular-inputs';
|
||||
import { LabelModule } from '@progress/kendo-angular-label';
|
||||
import { ButtonsModule } from '@progress/kendo-angular-buttons';
|
||||
import { IndicatorsModule } from '@progress/kendo-angular-indicators';
|
||||
import { AuthService } from '../../shared/services/auth.service';
|
||||
import {
|
||||
passwordStrengthValidator,
|
||||
passwordMatchValidator,
|
||||
} from '../account/validators/password.validators';
|
||||
|
||||
type Step = 'loading' | 'invalid' | 'form';
|
||||
|
||||
@Component({
|
||||
selector: 'app-accept-invitation',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule, ReactiveFormsModule,
|
||||
InputsModule, LabelModule, ButtonsModule, IndicatorsModule,
|
||||
],
|
||||
template: `
|
||||
<div class="min-h-screen flex items-center justify-center p-4">
|
||||
<div class="w-full max-w-md rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
|
||||
|
||||
<h1 class="text-xl font-semibold mb-1">River Of Life Christian Church</h1>
|
||||
|
||||
<!-- Validating the link -->
|
||||
<ng-container *ngIf="step === 'loading'">
|
||||
<div class="text-center py-6">
|
||||
<kendo-loader></kendo-loader>
|
||||
<p class="mt-2 text-gray-600">Checking your invitation…</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- Invalid / expired link -->
|
||||
<ng-container *ngIf="step === 'invalid'">
|
||||
<p class="text-base font-medium mb-2">This invitation can't be used</p>
|
||||
<p class="text-gray-600 mb-4">{{ invalidMessage }}</p>
|
||||
<button kendoButton themeColor="primary" (click)="goToLogin()">Go to sign in</button>
|
||||
</ng-container>
|
||||
|
||||
<!-- Set password form -->
|
||||
<ng-container *ngIf="step === 'form'">
|
||||
<p class="text-gray-600 mb-4">
|
||||
Welcome<span *ngIf="memberName">, <strong>{{ memberName }}</strong></span>. Set a password to
|
||||
finish creating your account and sign in.
|
||||
</p>
|
||||
|
||||
<form [formGroup]="form" class="k-form k-form-vertical" (ngSubmit)="onSubmit()">
|
||||
<div class="grid grid-cols-1 gap-y-3">
|
||||
|
||||
<kendo-formfield>
|
||||
<kendo-label text="New Password *"></kendo-label>
|
||||
<kendo-textbox formControlName="newPassword" type="password" [clearButton]="false"></kendo-textbox>
|
||||
<kendo-formerror *ngIf="form.get('newPassword')?.errors?.['required']">Required.</kendo-formerror>
|
||||
<kendo-formerror *ngIf="form.get('newPassword')?.errors?.['passwordStrength']">
|
||||
Must be at least 8 characters with an uppercase letter, a lowercase letter,
|
||||
a digit, and a special character.
|
||||
</kendo-formerror>
|
||||
</kendo-formfield>
|
||||
|
||||
<kendo-formfield>
|
||||
<kendo-label text="Confirm Password *"></kendo-label>
|
||||
<kendo-textbox formControlName="confirmPassword" type="password" [clearButton]="false"></kendo-textbox>
|
||||
<kendo-formerror *ngIf="form.get('confirmPassword')?.errors?.['required']">Required.</kendo-formerror>
|
||||
<kendo-formerror *ngIf="form.errors?.['mismatch'] && form.get('confirmPassword')?.touched">
|
||||
Passwords do not match.
|
||||
</kendo-formerror>
|
||||
</kendo-formfield>
|
||||
|
||||
<p *ngIf="errorMessage" class="k-color-error">{{ errorMessage }}</p>
|
||||
|
||||
<div class="mt-2">
|
||||
<button kendoButton themeColor="primary" type="submit" [disabled]="form.invalid || submitting">
|
||||
<span *ngIf="submitting">…</span>
|
||||
Set password & sign in
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class AcceptInvitationComponent implements OnInit {
|
||||
step: Step = 'loading';
|
||||
form: FormGroup;
|
||||
submitting = false;
|
||||
memberName: string | null = null;
|
||||
invalidMessage = 'This invitation link is invalid or has already been used.';
|
||||
errorMessage = '';
|
||||
|
||||
private token = '';
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private auth: AuthService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
) {
|
||||
this.form = this.fb.group(
|
||||
{
|
||||
newPassword: ['', [Validators.required, passwordStrengthValidator()]],
|
||||
confirmPassword: ['', [Validators.required]],
|
||||
},
|
||||
{ validators: passwordMatchValidator() },
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.token = this.route.snapshot.queryParamMap.get('token') ?? '';
|
||||
if (!this.token) {
|
||||
this.step = 'invalid';
|
||||
return;
|
||||
}
|
||||
|
||||
this.auth.validateInvitation(this.token).subscribe({
|
||||
next: (result) => {
|
||||
if (result.valid) {
|
||||
this.memberName = result.memberName ?? null;
|
||||
this.step = 'form';
|
||||
} else {
|
||||
this.invalidMessage = result.expired
|
||||
? 'This invitation link has expired. Please ask for a new one.'
|
||||
: 'This invitation link is invalid or has already been used.';
|
||||
this.step = 'invalid';
|
||||
}
|
||||
},
|
||||
error: () => { this.step = 'invalid'; },
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
if (this.form.invalid) { this.form.markAllAsTouched(); return; }
|
||||
this.submitting = true;
|
||||
this.errorMessage = '';
|
||||
|
||||
this.auth.acceptInvitation(this.token, this.form.value.newPassword).subscribe({
|
||||
next: () => {
|
||||
this.router.navigate(['/user-portal/dashboard']);
|
||||
},
|
||||
error: (err) => {
|
||||
this.errorMessage = err.error?.message ?? 'Could not set your password. The link may have expired.';
|
||||
this.submitting = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
goToLogin(): void {
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user