feature/init-tailwind-daisyui #13

Merged
igorpropisnov merged 10 commits from feature/init-tailwind-daisyui into main 2024-06-26 19:22:33 +02:00
6 changed files with 114 additions and 102 deletions
Showing only changes of commit 67b7ca3922 - Show all commits

View File

@ -1,20 +1,39 @@
<div id="background"> <div class="bg-primary w-screen h-screen">
<div class="wrapper"> <div class="modal modal-open">
<div class="content"> <div
@if (verifyStatus() === true) { [ngStyle]="backgroundStyle"
@if (showRedirectMessage()) { class="modal-box w-11/12 h-2/6 max-w-5xl flex">
<h1>Es geht gleich los!</h1> <div class="w-full flex flex-col justify-center items-center">
<h2> @if (verifyStatus() === true) {
Danke für das bestätigen der E-Mail - Wir leiten dich zum Login @if (showRedirectMessage()) {
weiter! <div class="text-center">
</h2> <h1 class="font-bold text-3xl pt-5">Your email is verified!</h1>
<p class="pt-3 pb-6">
Your email {{ email() }} has been successfully verified. will
<br />
You be automatically redirected in to the login page to access
the application shortly.
</p>
<button
(click)="navigateToSignup()"
class="btn btn-primary no-animation">
Go to the App
</button>
</div>
}
} @else if (verifyStatus() === false) {
<div class="text-center">
<h1 class="font-bold text-3xl pt-5">
Oops, something went wrong! :(
</h1>
<p class="pt-3">We couldn't verify your email.</p>
</div>
} @else {
<div class="text-center">
<span class="loading loading-dots loading-lg"></span>
</div>
} }
} @else if (verifyStatus() === false) { </div>
<h1>Oops, da ist etwas schief gelaufen!</h1>
<h2>Der Link ist nicht mehr gültig</h2>
} @else {
<h1>Verifizierung wird durchgeführt...</h1>
}
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,28 +0,0 @@
#background {
display: flex;
height: 100%;
}
.wrapper {
flex: 1;
background-color: lightsalmon;
display: flex;
.content{
flex-direction: column;
display: flex;
align-items: flex-start;
justify-content: center;
h1 {
font-size: 4rem;
margin-left: 3rem;
line-height: 1rem;
}
h2 {
margin-left: 3rem;
}
p {
margin-left: 3rem;
}
}
}

View File

@ -1,6 +1,8 @@
import { CommonModule } from '@angular/common';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ElementRef,
InputSignal, InputSignal,
OnInit, OnInit,
WritableSignal, WritableSignal,
@ -12,11 +14,12 @@ import { Router } from '@angular/router';
import { delay, filter, tap } from 'rxjs'; import { delay, filter, tap } from 'rxjs';
import { VerifyApiService } from '../../api'; import { VerifyApiService } from '../../api';
import { BackgroundPatternService } from '../../shared/service';
@Component({ @Component({
selector: 'app-email-verify-root', selector: 'app-email-verify-root',
standalone: true, standalone: true,
imports: [], imports: [CommonModule],
providers: [], providers: [],
templateUrl: './email-verify-root.component.html', templateUrl: './email-verify-root.component.html',
styleUrl: './email-verify-root.component.scss', styleUrl: './email-verify-root.component.scss',
@ -24,6 +27,8 @@ import { VerifyApiService } from '../../api';
}) })
export class EmailVerifyRootComponent implements OnInit { export class EmailVerifyRootComponent implements OnInit {
public token: InputSignal<string> = input<string>(''); public token: InputSignal<string> = input<string>('');
public email: WritableSignal<string> = signal<string>('');
public backgroundStyle: { 'background-image': string } | null = null;
public verifyStatus: WritableSignal<boolean | null> = signal<boolean | null>( public verifyStatus: WritableSignal<boolean | null> = signal<boolean | null>(
null null
); );
@ -31,19 +36,56 @@ export class EmailVerifyRootComponent implements OnInit {
public constructor( public constructor(
private readonly api: VerifyApiService, private readonly api: VerifyApiService,
private readonly router: Router private readonly router: Router,
private readonly el: ElementRef,
private readonly backgroundPatternService: BackgroundPatternService
) {} ) {}
public ngOnInit(): void { public ngOnInit(): void {
this.verifyEmail(); this.verifyEmail();
this.setBackground();
}
public setBackground(): void {
const color = getComputedStyle(this.el.nativeElement).getPropertyValue(
'--p'
);
const svgUrl = this.backgroundPatternService.getBubblesPattern(color, 0.1);
this.backgroundStyle = { 'background-image': `url("${svgUrl}")` };
}
public navigateToSignup(): void {
const email: string = this.extractEmail();
this.router.navigate(['/signup'], {
queryParams: { verified: true, email: email },
});
}
private extractVerifyToken(): string {
const [verifyToken]: string[] = this.token().split('|');
return verifyToken;
}
private extractEmail(): string {
const [, email]: string[] = this.token().split('|');
return email;
} }
private verifyEmail(): void { private verifyEmail(): void {
const [verifyToken, email]: string[] = this.token().split('|'); const verifyToken: string = this.extractVerifyToken();
const email: string = this.extractEmail();
this.email.set(decodeURIComponent(atob(email)));
this.api this.api
.verifyControllerVerifyEmail(verifyToken) .verifyControllerVerifyEmail(verifyToken)
.pipe( .pipe(
delay(1500),
tap((isVerified: boolean) => { tap((isVerified: boolean) => {
this.verifyStatus.set(isVerified); this.verifyStatus.set(isVerified);
}), }),
@ -51,12 +93,10 @@ export class EmailVerifyRootComponent implements OnInit {
tap(() => { tap(() => {
this.showRedirectMessage.set(true); this.showRedirectMessage.set(true);
}), }),
delay(5000) delay(10000)
) )
.subscribe(() => { .subscribe(() => {
this.router.navigate(['/signup'], { this.navigateToSignup();
queryParams: { verified: true, email: email },
});
}); });
} }
} }

View File

@ -281,6 +281,7 @@
<input <input
formControlName="email" formControlName="email"
type="text" type="text"
class="grow"
placeholder="name@example.com" /> placeholder="name@example.com" />
</label> </label>
</div> </div>
@ -332,31 +333,30 @@
<div class="modal modal-open" *ngIf="isDialogOpen()"> <div class="modal modal-open" *ngIf="isDialogOpen()">
<div <div
[ngStyle]="backgroundStyle" [ngStyle]="backgroundStyle"
class="modal-box w-11/12 h-3/6 max-w-5xl flex"> class="modal-box w-11/12 h-2/6 max-w-5xl flex">
<div class="w-full flex flex-col justify-center items-center"> <div class="w-full flex flex-col justify-center items-center">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="2" stroke-width="1"
stroke="currentColor" stroke="currentColor"
class="size-36"> class="size-28 animate-jump animate-once animate-duration-[2000ms] animate-delay-500 animate-ease-in-out animate-normal">
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
d="M21.75 9v.906a2.25 2.25 0 0 1-1.183 1.981l-6.478 3.488M2.25 9v.906a2.25 2.25 0 0 0 1.183 1.981l6.478 3.488m8.839 2.51-4.66-2.51m0 0-1.023-.55a2.25 2.25 0 0 0-2.134 0l-1.022.55m0 0-4.661 2.51m16.5 1.615a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V8.844a2.25 2.25 0 0 1 1.183-1.981l7.5-4.039a2.25 2.25 0 0 1 2.134 0l7.5 4.039a2.25 2.25 0 0 1 1.183 1.98V19.5Z" /> d="M21.75 9v.906a2.25 2.25 0 0 1-1.183 1.981l-6.478 3.488M2.25 9v.906a2.25 2.25 0 0 0 1.183 1.981l6.478 3.488m8.839 2.51-4.66-2.51m0 0-1.023-.55a2.25 2.25 0 0 0-2.134 0l-1.022.55m0 0-4.661 2.51m16.5 1.615a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V8.844a2.25 2.25 0 0 1 1.183-1.981l7.5-4.039a2.25 2.25 0 0 1 2.134 0l7.5 4.039a2.25 2.25 0 0 1 1.183 1.98V19.5Z" />
</svg> </svg>
<h1 class="font-bold text-6xl pt-5">Check your E-Mail</h1> <h1 class="font-bold text-3xl pt-5">Check your inbox, please!</h1>
<div class="flex flex-col items-center"> <div class="flex flex-col items-center text-center">
<p class="pt-8 text-xl font-semibold"> <p class="pt-3">
We sent an email link to complete your registration. Hey, to start using [APP-NAME], we need to verify your email.
</p> </p>
<p class="pt-1">
<p class="text-xs pt-20 pb-2"> We´ve already sent out the verification link. Please check it and
<a class="cursor-pointer"> <br />
Didn´t recieve the email? Click here to send it again. confirm it´s really you.
</a>
</p> </p>
</div> </div>
</div> </div>

View File

@ -78,10 +78,6 @@ export class RegisterRootComponent implements OnInit {
public form!: FormGroup; public form!: FormGroup;
public isSigninSignal: WritableSignal<boolean> = signal(false); public isSigninSignal: WritableSignal<boolean> = signal(false);
public isSignupSignal: WritableSignal<boolean> = signal(true); public isSignupSignal: WritableSignal<boolean> = signal(true);
//public isDisplayButtons: WritableSignal<boolean> = signal(true);
public emailInvalid: WritableSignal<string | null> = signal(null);
public passwordInvalid: WritableSignal<string | null> = signal(null);
public termsInvalid: WritableSignal<string | null> = signal(null);
public userSignupSuccess: WritableSignal<boolean> = signal(false); public userSignupSuccess: WritableSignal<boolean> = signal(false);
public isDialogOpen: WritableSignal<boolean> = signal(false); public isDialogOpen: WritableSignal<boolean> = signal(false);
private removeQueryParams: WritableSignal<boolean> = signal(false); private removeQueryParams: WritableSignal<boolean> = signal(false);
@ -100,17 +96,6 @@ export class RegisterRootComponent implements OnInit {
private readonly backgroundPatternService: BackgroundPatternService private readonly backgroundPatternService: BackgroundPatternService
) { ) {
effect(() => { effect(() => {
if (this.form) {
if (this.isSigninSignal()) {
this.form.addControl(
'terms',
new FormControl(false, Validators.requiredTrue)
);
} else {
this.form.removeControl('terms');
}
}
if (this.removeQueryParams()) { if (this.removeQueryParams()) {
this.clearRouteParams(); this.clearRouteParams();
} }
@ -134,7 +119,7 @@ export class RegisterRootComponent implements OnInit {
'--p' '--p'
); );
const svgUrl = this.backgroundPatternService.getWigglePattern(color, 0.04); const svgUrl = this.backgroundPatternService.getWigglePattern(color, 0.1);
this.backgroundStyle = { 'background-image': `url("${svgUrl}")` }; this.backgroundStyle = { 'background-image': `url("${svgUrl}")` };
} }
@ -160,6 +145,8 @@ export class RegisterRootComponent implements OnInit {
} }
public toggleAction(action: AuthAction): void { public toggleAction(action: AuthAction): void {
this.resetFormValidation();
if (action === 'signin') { if (action === 'signin') {
this.isSigninSignal.set(true); this.isSigninSignal.set(true);
this.isSignupSignal.set(false); this.isSignupSignal.set(false);
@ -167,11 +154,10 @@ export class RegisterRootComponent implements OnInit {
this.isSigninSignal.set(false); this.isSigninSignal.set(false);
this.isSignupSignal.set(true); this.isSignupSignal.set(true);
} }
//this.isDisplayButtons.set(false);
} }
public onSubmit(): void { public onSubmit(): void {
this.markControlsAsTouchedAndDirty(['email', 'password', 'terms']); this.markControlsAsTouchedAndDirty(['email', 'password']);
if (this.form?.valid) { if (this.form?.valid) {
if (this.isSigninSignal()) { if (this.isSigninSignal()) {
@ -182,18 +168,6 @@ export class RegisterRootComponent implements OnInit {
} }
} }
public switchMask(): void {
this.resetFormValidation();
if (this.isSignupSignal()) {
this.isSignupSignal.set(false);
this.isSigninSignal.set(true);
} else if (this.isSigninSignal()) {
this.isSignupSignal.set(true);
this.isSigninSignal.set(false);
}
}
private initializeForm(): void { private initializeForm(): void {
this.form = this.formBuilder.group({ this.form = this.formBuilder.group({
email: new FormControl('', { email: new FormControl('', {
@ -204,18 +178,14 @@ export class RegisterRootComponent implements OnInit {
validators: [Validators.required, customPasswordValidator()], validators: [Validators.required, customPasswordValidator()],
updateOn: 'change', updateOn: 'change',
}), }),
terms: new FormControl(false, {
validators: [Validators.requiredTrue],
updateOn: 'change',
}),
}); });
} }
private handleRedirect(): void { private handleRedirect(): void {
if (this.verified()) { if (this.verified()) {
//this.isDisplayButtons.set(false); //this.isDisplayButtons.set(false);
this.isSigninSignal.set(false); this.isSigninSignal.set(true);
this.isSignupSignal.set(true); this.isSignupSignal.set(false);
} }
if (this.email()) { if (this.email()) {
this.form?.get('email')?.setValue(decodeURIComponent(atob(this.email()))); this.form?.get('email')?.setValue(decodeURIComponent(atob(this.email())));
@ -223,7 +193,6 @@ export class RegisterRootComponent implements OnInit {
if (this.login()) { if (this.login()) {
this.isSignupSignal.set(true); this.isSignupSignal.set(true);
//this.isDisplayButtons.set(false);
this.isSigninSignal.set(false); this.isSigninSignal.set(false);
} }
} }
@ -288,7 +257,7 @@ export class RegisterRootComponent implements OnInit {
} }
private resetFormValidation(): void { private resetFormValidation(): void {
['email', 'password', 'terms'].forEach((controlName: string) => { ['email', 'password'].forEach((controlName: string) => {
this.resetControlValidation(controlName); this.resetControlValidation(controlName);
}); });
} }

View File

@ -6,7 +6,7 @@ import chroma from 'chroma-js';
providedIn: 'root', providedIn: 'root',
}) })
export class BackgroundPatternService { export class BackgroundPatternService {
public getWigglePattern(color: string, opacity: number = 0.08): string { public getWigglePattern(color: string, opacity: number): string {
const colorHex = this.convertOklchStringToHex(color); const colorHex = this.convertOklchStringToHex(color);
const encodedHex = encodeURIComponent(colorHex); const encodedHex = encodeURIComponent(colorHex);
const pattern = ` const pattern = `
@ -22,6 +22,18 @@ export class BackgroundPatternService {
return pattern.replace(/[\n\r]+|[\s]{2,}/g, ' ').trim(); return pattern.replace(/[\n\r]+|[\s]{2,}/g, ' ').trim();
} }
public getBubblesPattern(color: string, opacity: number): string {
const colorHex = this.convertOklchStringToHex(color);
const encodedHex = encodeURIComponent(colorHex);
const pattern = `
data:image/svg+xml,
%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E
%3Cpath d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z' fill='${encodedHex}' fill-opacity='${opacity}' fill-rule='evenodd'/%3E
%3C/svg%3E`;
return pattern.replace(/[\n\r]+|[\s]{2,}/g, ' ').trim();
}
private convertOklchStringToHex(oklchString: string): string { private convertOklchStringToHex(oklchString: string): string {
const parts = oklchString.split(' '); const parts = oklchString.split(' ');
const l = parseFloat(parts[0]); const l = parseFloat(parts[0]);