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

Jc 679 authorization feature #6

Open
wants to merge 64 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
ddba767
Create pagination JC-660
TebyakinaEkaterina Jul 25, 2024
75d1b19
Create table sort JC-660
TebyakinaEkaterina Jul 25, 2024
0b3879b
Create search JC-644
TebyakinaEkaterina Jul 25, 2024
7b129f0
Create filter by type JC-660
TebyakinaEkaterina Jul 25, 2024
cb24b48
Add query params JC-660
TebyakinaEkaterina Jul 25, 2024
fcc162f
Create filters, sort and search JC-660
TebyakinaEkaterina Jul 29, 2024
234859a
Refactoring sort JC-660
TebyakinaEkaterina Jul 29, 2024
8997c70
Create services JC-660
TebyakinaEkaterina Jul 29, 2024
bf77abf
Create observable for params JC-660
TebyakinaEkaterina Jul 30, 2024
a356d61
Realize work with query params service JC-660
TebyakinaEkaterina Jul 30, 2024
f74d6b9
Improving code JC-660
TebyakinaEkaterina Jul 30, 2024
852f2dc
Write documentation JC-660
TebyakinaEkaterina Jul 31, 2024
90190cd
Add navigation JC-679
TebyakinaEkaterina Jul 31, 2024
1cb1ff8
Create login component JC-679
TebyakinaEkaterina Jul 31, 2024
d38c2fe
Create registration component JC-679
TebyakinaEkaterina Jul 31, 2024
dc5e5d9
Create service for form fields compare JC-679
TebyakinaEkaterina Jul 31, 2024
251d141
Add css variables JC-679
TebyakinaEkaterina Jul 31, 2024
a3b942b
Create dto, model and mapper for registration JC-679
TebyakinaEkaterina Jul 31, 2024
f4a6103
Realize registration JC-679
TebyakinaEkaterina Jul 31, 2024
0d62fce
Development login JC-679
TebyakinaEkaterina Aug 1, 2024
7e9371f
Development authorization JC-679
TebyakinaEkaterina Aug 5, 2024
6c7a733
Refactoring authorization JC-679
TebyakinaEkaterina Aug 6, 2024
58d128d
Rename and refactoring dashboard component JC-660
TebyakinaEkaterina Aug 2, 2024
5653ce6
Refactoring api service JC-660
TebyakinaEkaterina Aug 2, 2024
952d1fa
Add util for check enums value JC-660
TebyakinaEkaterina Aug 2, 2024
5df32d4
Rename and refactoring dashboard component JC-660
TebyakinaEkaterina Aug 5, 2024
2e101ee
Refactoring Jc-679
TebyakinaEkaterina Aug 7, 2024
f263a3f
Refactoring JC-679
TebyakinaEkaterina Aug 7, 2024
ad13d50
Fix refresh url JC-679
TebyakinaEkaterina Aug 7, 2024
87a93f2
Fix lint issues JC-679
TebyakinaEkaterina Aug 7, 2024
bf6811d
Fix errors JC-679
TebyakinaEkaterina Aug 7, 2024
14c67d1
Refactoring JC-679
TebyakinaEkaterina Aug 9, 2024
909e0fb
Add services for forms JC-679
TebyakinaEkaterina Aug 12, 2024
302907d
Fix attributes formatting JC-679
TebyakinaEkaterina Aug 12, 2024
395a571
Fix attributes formatting JC-679
TebyakinaEkaterina Aug 13, 2024
3e9cca8
Fix merge issue with anime type mapper JC-679
TebyakinaEkaterina Aug 13, 2024
e75fff9
Fix merge issues JC-679
TebyakinaEkaterina Aug 13, 2024
89b05d1
Fix merge issues JC-679
TebyakinaEkaterina Aug 13, 2024
38e10e0
Fix merge issues JC-679
TebyakinaEkaterina Aug 13, 2024
3f5454d
Code refactoring JC-679
TebyakinaEkaterina Aug 13, 2024
e77acc7
Code refactoring JC-679
TebyakinaEkaterina Aug 13, 2024
fe21172
Move logic of getting an error into a reusable function JC-679
TebyakinaEkaterina Aug 21, 2024
1a3bfcc
Remove remove disable attribute from submit buttons and add a validit…
TebyakinaEkaterina Aug 21, 2024
70a4669
Replace next and error with catchError and tap JC-679
TebyakinaEkaterina Aug 21, 2024
293b06d
Improve refreshAccessToken method JC-679
TebyakinaEkaterina Aug 21, 2024
b7c0896
Add comment to get user method about a throwing an error JC-679
TebyakinaEkaterina Aug 21, 2024
c9bc34b
Fix type in matchFieldsValidator JC-679
TebyakinaEkaterina Aug 21, 2024
9f9b1dd
Rename menu links JC-679
TebyakinaEkaterina Aug 21, 2024
a371cf6
Replace next and error with catchError and tap JC-679
TebyakinaEkaterina Aug 21, 2024
e1bcb20
Fix JSDoc comments JC-679
TebyakinaEkaterina Aug 21, 2024
ed0d531
Improve getErrorMessage method JC-679
TebyakinaEkaterina Aug 23, 2024
fb53cd7
Implement user data storage in the service JC-679
TebyakinaEkaterina Aug 23, 2024
4f8a0b6
Remove unused variable Jc-679
TebyakinaEkaterina Aug 23, 2024
7b236b3
Remove unused import JC-679
TebyakinaEkaterina Aug 23, 2024
76ca386
Fix JSDoc comment JC-679
TebyakinaEkaterina Aug 23, 2024
883b301
Improve confirm password validation JC-726
TebyakinaEkaterina Aug 23, 2024
a1b5aa4
Fix infinite loop of refreshing tokens JC-679
TebyakinaEkaterina Aug 26, 2024
43d2c55
Add JSDoc comments JC-679
TebyakinaEkaterina Aug 26, 2024
adeb549
Create variable for user JC-679
TebyakinaEkaterina Aug 26, 2024
4c71c06
Fix html JC-679
TebyakinaEkaterina Aug 26, 2024
51db6a1
Remove redundant type for error status JC-679
TebyakinaEkaterina Aug 26, 2024
880e59f
Implement local storage service for a a wide range of keys and values…
TebyakinaEkaterina Aug 26, 2024
e1008e3
Create enum for tokens JC-679
TebyakinaEkaterina Aug 28, 2024
189993f
Remove redundant service for local storage JC-679
TebyakinaEkaterina Aug 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/angular/src/app/app.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
}

.header {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
border-bottom: 2px var(--border-color) solid;
padding: 0 var(--space-m);
}

.header__logo-link {
font-size: var(--font-size-xl);
font-weight: var(--font-weight-medium);
}
8 changes: 7 additions & 1 deletion apps/angular/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
<mat-toolbar class="header">
<h1>Anime app</h1>
<a
class="header__logo-link header__link"
[routerLink]="['/' + routerPaths.Main]"
>
Anime app
</a>
<camp-authorization-menu/>
</mat-toolbar>
<router-outlet></router-outlet>
19 changes: 16 additions & 3 deletions apps/angular/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { RouterModule, RouterLink } from '@angular/router';
import { MatToolbarModule } from '@angular/material/toolbar';

import { RouterPaths } from '../core/model/router-paths';

import { AuthorizationMenuComponent } from './features/authorization-menu/authorization-menu.component';

/** Main component of application. */
@Component({
selector: 'camp-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
standalone: true,
imports: [RouterModule, MatToolbarModule],
imports: [
MatToolbarModule,
RouterModule,
RouterLink,
AuthorizationMenuComponent,
],
})
export class AppComponent {}
export class AppComponent {

/** Enum with paths for link. */
protected readonly routerPaths = RouterPaths;
}
14 changes: 13 additions & 1 deletion apps/angular/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { Routes } from '@angular/router';

import { RouterPaths } from '../core/model/router-paths';

import { AnimeDashboardComponent } from './features/anime-dashboard/anime-dashboard.component';

/** Routes object. */
export const appRoutes: Routes = [
{
path: '',
path: RouterPaths.Main,
component: AnimeDashboardComponent,
},
{
path: RouterPaths.Login,
loadComponent: () => import('./features/login-form/login-form.component')
.then(c => c.LoginFormComponent),
},
{
path: RouterPaths.Registration,
loadComponent: () => import('./features/registration-form/registration-form.component')
.then(c => c.RegistrationFormComponent),
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.authorization-menu {
display: flex;
flex-direction: row;
justify-content: end;
gap: var(--space-s);
}

.greeting-message {
font-size: var(--font-size-md);
font-weight: var(--font-weight-medium);
}

.authorization-menu__link {
font-size: var(--font-size-md);
}

.authorization-menu__logout {
font-size: var(--font-size-md);
background-color: var(--primary-background-color);
border: none;
padding: 0;
}

.authorization-menu__logout:hover {
cursor: pointer;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<nav class="authorization-menu">
@if (user$ | async; as user) {
<div class="greeting-message">
Hi,&nbsp;
<span>{{user.firstName}}</span>
&nbsp;
<span>{{user.lastName}}</span>
!
</div>
<button
class="authorization-menu__logout"
type="button"
(click)="onLogoutClick()"
>
Logout
</button>
} @else {
<a
class="authorization-menu__link"
[routerLink]="['/' + routerPaths.Registration]"
>
Register
</a>
<a
class="authorization-menu__link"
[routerLink]="['/' + routerPaths.Login]"
>
Login
</a>
}
</nav>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { RouterLink } from '@angular/router';
import { UserApiService } from '@js-camp/angular/core/services/users-api.service';
import { AsyncPipe } from '@angular/common';
import { AuthorizationApiService } from '@js-camp/angular/core/services/authorization-api.service';
import { RouterPaths } from '@js-camp/angular/core/model/router-paths';

/** Component with authorization navigation menu. */
@Component({
selector: 'camp-authorization-menu',
standalone: true,
templateUrl: './authorization-menu.component.html',
styleUrl: './authorization-menu.component.css',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
RouterLink,
AsyncPipe,
],
})
export class AuthorizationMenuComponent {

private readonly authApiService = inject(AuthorizationApiService);

private readonly usersApiService = inject(UserApiService);

/** Enum with paths for link. */
protected readonly routerPaths = RouterPaths;

/** Contains information about current user. */
protected readonly user$ = this.usersApiService.user$;

/** Handle click on log out button. */
protected onLogoutClick(): void {
this.authApiService.logout();
}
}
29 changes: 29 additions & 0 deletions apps/angular/src/app/features/login-form/login-form.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
:host {
--login-form-min-width: 360px;
}

.login-form-wrapper {
display: flex;
flex-direction: column;
align-items: center;
padding: var(--space-xl);
}

.login-header {
font-size: var(--font-size-xl);
}

.login-form {
min-width: var(--login-form-min-width);
padding: var(--space-m);
border-radius: 24px;
border: 2px var(--border-color) solid;
display: flex;
flex-direction: column;
gap: var(--space-s);
align-items: center;
}

.login-form__field {
width: 100%;
}
49 changes: 49 additions & 0 deletions apps/angular/src/app/features/login-form/login-form.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<div class="login-form-wrapper">
<h2 class="login-header">Login</h2>
<form
class="login-form"
[formGroup]="loginFormService.form"
(ngSubmit)="onLoginSubmit()"
>

<mat-form-field
appearance="outline"
class="login-form__field"
>
<mat-label>Email</mat-label>
<input
matInput
type="email"
placeholder="ivan.pupkin@gmail.com"
[formControl]="loginFormService.form.controls.email"
>
<mat-error>{{loginFormService.getErrorMessage('email')}}</mat-error>
</mat-form-field>

<mat-form-field
appearance="outline"
class="login-form__field"
>
<mat-label>Password</mat-label>
<input
matInput
type="password"
placeholder="veryStrongPassW0rd"
[formControl]="loginFormService.form.controls.password"
>
<mat-error>{{loginFormService.getErrorMessage('password')}}</mat-error>
</mat-form-field>

@if (loginFormService.form.hasError('serverError')) {
<mat-error>{{loginFormService.form.getError('serverError')}}</mat-error>
}

<button
mat-raised-button
type="submit"
>
Login
</button>

</form>
</div>
72 changes: 72 additions & 0 deletions apps/angular/src/app/features/login-form/login-form.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { AuthorizationApiService } from '@js-camp/angular/core/services/authorization-api.service';
import { catchError, tap, throwError } from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';
import { Router } from '@angular/router';
import { RouterPaths } from '@js-camp/angular/core/model/router-paths';
import { LoginData } from '@js-camp/core/models/login-data';

import { LoginFormService } from './login-form.service';

/** Component with form for authorization. */
@Component({
selector: 'camp-login-form',
standalone: true,
templateUrl: './login-form.component.html',
styleUrl: './login-form.component.css',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
],
})
export class LoginFormComponent {

/** Enum with paths for link. */
protected readonly routerPaths = RouterPaths;

/** Login form management service. */
protected readonly loginFormService = inject(LoginFormService);

private readonly authorizationApiService = inject(AuthorizationApiService);

private readonly destroyRef = inject(DestroyRef);

private readonly changeDetectorRef = inject(ChangeDetectorRef);

private readonly router = inject(Router);

/** Handle login form submit. */
protected onLoginSubmit(): void {

if (this.loginFormService.form.invalid) {
return;
}

const formData = new LoginData(this.loginFormService.form.getRawValue());

this.authorizationApiService.login(formData).pipe(
takeUntilDestroyed(this.destroyRef),
catchError((error: unknown) => {
if (error instanceof HttpErrorResponse) {
this.loginFormService.handleServerError(error);
this.changeDetectorRef.markForCheck();
}
return throwError(() => error);
}),
tap(() => {
this.loginFormService.form.reset();
this.router.navigate([this.routerPaths.Main]);
}),
)
.subscribe();

}
}
Loading
Loading