diff --git a/angular.json b/angular.json index 63a3c15..a615ab1 100644 --- a/angular.json +++ b/angular.json @@ -118,5 +118,8 @@ "@schematics/angular:resolver": { "typeSeparator": "." } + }, + "cli": { + "analytics": false } } diff --git a/src/app/app.component.css b/src/app/app.component.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/app.component.html b/src/app/app.component.html index 36093e1..67e7bd4 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,336 +1 @@ - - - - - - - - - - - -
-
-
- -

Hello, {{ title }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
-
-
- - - - - - - - - - diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts deleted file mode 100644 index 24c5989..0000000 --- a/src/app/app.component.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [AppComponent], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it(`should have the 'auth' title`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('auth'); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, auth'); - }); -}); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index d176687..c9770ba 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -5,8 +5,6 @@ import { RouterOutlet } from '@angular/router'; selector: 'app-root', imports: [RouterOutlet], templateUrl: './app.component.html', - styleUrl: './app.component.css' }) export class AppComponent { - title = 'auth'; } diff --git a/src/app/app.config.ts b/src/app/app.config.ts index a1e7d6f..4973533 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,8 +1,14 @@ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; -import { provideRouter } from '@angular/router'; +import {provideRouter, withDebugTracing} from '@angular/router'; import { routes } from './app.routes'; +import {provideHttpClient, withInterceptors} from "@angular/common/http"; +import {httpInterceptor} from "./service/http.interceptor"; export const appConfig: ApplicationConfig = { - providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)] + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes, withDebugTracing()), + provideHttpClient(withInterceptors([httpInterceptor])), + ] }; diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index dc39edb..161940d 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,3 +1,33 @@ import { Routes } from '@angular/router'; +import {authGuard} from "./service/auth.guard"; -export const routes: Routes = []; +export const routes: Routes = [ + { + path: 'auth', + children: [ + { + path: 'register', + loadComponent: () => import('./component/auth/register/register.component'), + }, + { + path: 'login', + loadComponent: () => import('./component/auth/login/login.component'), + }, + ], + }, + { + path: '', + loadComponent: () => import('./component/random-ahh/random-ahh.component'), + children: [ + { + path: 'dashboard', + loadComponent: () => import('./component/dashboard/dashboard.component'), + }, + { + path: 'users', + loadComponent: () => import('./component/users/users.component'), + }, + ], + canActivate: [authGuard], + } +]; diff --git a/src/app/component/auth/login/login.component.html b/src/app/component/auth/login/login.component.html new file mode 100644 index 0000000..9c539cd --- /dev/null +++ b/src/app/component/auth/login/login.component.html @@ -0,0 +1,6 @@ +
+ + + + +
diff --git a/src/app/component/auth/login/login.component.ts b/src/app/component/auth/login/login.component.ts new file mode 100644 index 0000000..4f19f4d --- /dev/null +++ b/src/app/component/auth/login/login.component.ts @@ -0,0 +1,37 @@ +import {Component, inject} from '@angular/core'; +import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms"; +import AuthService from "../../../service/auth.service"; +import {AuthResponse, Token} from "../../../models"; +import {Router} from "@angular/router"; + +@Component({ + selector: 'app-login', + imports: [ + ReactiveFormsModule + ], + templateUrl: './login.component.html', +}) +export default class LoginComponent { + fb: FormBuilder = inject(FormBuilder); + authService: AuthService = inject(AuthService); + router: Router = inject(Router); + + form: FormGroup = this.fb.group({ + username: [null, [Validators.required]], + password: [null, [Validators.required]], + }); + + login() { + if (this.form.invalid) { + console.log(this.form.errors) + return; + } + + this.authService.login(this.form.value).subscribe((r: AuthResponse) => { + localStorage.setItem('access_token', r.accessToken); + localStorage.setItem('refresh_token', r.refreshToken); + + this.router.navigate(['dashboard']) + }); + } +} diff --git a/src/app/component/auth/register/register.component.html b/src/app/component/auth/register/register.component.html new file mode 100644 index 0000000..4b201d5 --- /dev/null +++ b/src/app/component/auth/register/register.component.html @@ -0,0 +1,48 @@ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + +
+ +
+ +
+ + + + + +
+ +
+ + + + + +
+ +
+ + +
diff --git a/src/app/component/auth/register/register.component.ts b/src/app/component/auth/register/register.component.ts new file mode 100644 index 0000000..ee56e6f --- /dev/null +++ b/src/app/component/auth/register/register.component.ts @@ -0,0 +1,52 @@ +import {Component, inject} from '@angular/core'; +import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms"; +import AuthService from "../../../service/auth.service"; +import CustomValidators from "../../../service/custom.validators"; + +@Component({ + selector: 'app-register', + imports: [ + ReactiveFormsModule + ], + templateUrl: './register.component.html', +}) +export default class RegisterComponent { + registerService: AuthService = inject(AuthService); + + fb: FormBuilder = inject(FormBuilder); + + emailGroup: FormGroup = this.fb.group({ + email: ['', Validators.required, Validators.email], + confirmEmail: ['', Validators.required, Validators.email], + }); + + passwordGroup: FormGroup = this.fb.group({ + password: ['', Validators.required], + confirmPassword: ['', Validators.required], + }); + + addressGroup: FormGroup = this.fb.group({ + city: ['', Validators.required], + state: ['', Validators.required], + zip: ['', Validators.required], + street: ['', Validators.required], + nr: ['', Validators.required], + }); + + form: FormGroup = this.fb.group({ + firstname: [null, Validators.required], + lastname: [null, Validators.required], + password: [this.passwordGroup, CustomValidators.password()], + email: [this.emailGroup, CustomValidators.email()], + address: [this.addressGroup, CustomValidators.address()], + }); + + submit() { + if (!this.form.valid) { + console.log(this.form.controls); + return; + } + + this.registerService.register(this.form.value); + } +} diff --git a/src/app/component/dashboard/dashboard.component.html b/src/app/component/dashboard/dashboard.component.html new file mode 100644 index 0000000..8bc168b --- /dev/null +++ b/src/app/component/dashboard/dashboard.component.html @@ -0,0 +1,3 @@ +

random ahh dashboard

+ + diff --git a/src/app/component/dashboard/dashboard.component.ts b/src/app/component/dashboard/dashboard.component.ts new file mode 100644 index 0000000..d9c5ccb --- /dev/null +++ b/src/app/component/dashboard/dashboard.component.ts @@ -0,0 +1,14 @@ +import {Component} from '@angular/core'; +import "../side/side.component"; +import {RouterOutlet} from "@angular/router"; + +@Component({ + selector: 'app-dashboard', + imports: [ + RouterOutlet + ], + templateUrl: './dashboard.component.html', +}) +export default class DashboardComponent { + +} diff --git a/src/app/component/header/header.component.html b/src/app/component/header/header.component.html new file mode 100644 index 0000000..cfa62db --- /dev/null +++ b/src/app/component/header/header.component.html @@ -0,0 +1,10 @@ + diff --git a/src/app/component/header/header.component.ts b/src/app/component/header/header.component.ts new file mode 100644 index 0000000..3f1ddc7 --- /dev/null +++ b/src/app/component/header/header.component.ts @@ -0,0 +1,13 @@ +import {Component} from '@angular/core'; +import {RouterLink} from "@angular/router"; + +@Component({ + selector: 'app-header', + imports: [ + RouterLink + ], + templateUrl: './header.component.html', +}) +export default class HeaderComponent { + +} diff --git a/src/app/component/random-ahh/random-ahh.component.html b/src/app/component/random-ahh/random-ahh.component.html new file mode 100644 index 0000000..08fbdeb --- /dev/null +++ b/src/app/component/random-ahh/random-ahh.component.html @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/component/random-ahh/random-ahh.component.ts b/src/app/component/random-ahh/random-ahh.component.ts new file mode 100644 index 0000000..1f0b126 --- /dev/null +++ b/src/app/component/random-ahh/random-ahh.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import HeaderComponent from "../header/header.component"; +import SideComponent from "../side/side.component"; +import {RouterOutlet} from "@angular/router"; + +@Component({ + selector: 'app-random-ahh', + imports: [ + HeaderComponent, + SideComponent, + RouterOutlet + ], + templateUrl: './random-ahh.component.html', +}) +export default class RandomAhhComponent { + +} diff --git a/src/app/component/side/side.component.html b/src/app/component/side/side.component.html new file mode 100644 index 0000000..4db32ea --- /dev/null +++ b/src/app/component/side/side.component.html @@ -0,0 +1 @@ + diff --git a/src/app/component/side/side.component.ts b/src/app/component/side/side.component.ts new file mode 100644 index 0000000..104aed7 --- /dev/null +++ b/src/app/component/side/side.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-side', + imports: [], + templateUrl: './side.component.html', +}) +export default class SideComponent { + +} diff --git a/src/app/component/users/users.component.html b/src/app/component/users/users.component.html new file mode 100644 index 0000000..d5b09c3 --- /dev/null +++ b/src/app/component/users/users.component.html @@ -0,0 +1 @@ +

mf users gng

diff --git a/src/app/component/users/users.component.ts b/src/app/component/users/users.component.ts new file mode 100644 index 0000000..7260a71 --- /dev/null +++ b/src/app/component/users/users.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-users', + imports: [], + templateUrl: './users.component.html', +}) +export default class UsersComponent { + +} diff --git a/src/app/models.ts b/src/app/models.ts new file mode 100644 index 0000000..39238d6 --- /dev/null +++ b/src/app/models.ts @@ -0,0 +1,16 @@ +export interface Token { + accessToken: string; + refreshToken: string; +} + +export interface User { + id: number; + username: string; + email: string; + firstName: string; + lastName: string; + gender: string; + image: string; +} + +export interface AuthResponse extends User, Token {} diff --git a/src/app/service/auth.guard.ts b/src/app/service/auth.guard.ts new file mode 100644 index 0000000..0b831d1 --- /dev/null +++ b/src/app/service/auth.guard.ts @@ -0,0 +1,18 @@ +import {CanActivateFn} from "@angular/router"; +import {inject} from "@angular/core"; +import {HttpClient, HttpErrorResponse, HttpResponse} from "@angular/common/http"; +import {catchError, map, of} from "rxjs"; +import {User} from "../models"; + +export const authGuard: CanActivateFn = () => { + return inject(HttpClient).get('https://dummyjson.com/auth/me', { observe: 'response' }).pipe( + map((res: HttpResponse) => { + console.log(res); + return res.status === 200; + }), + catchError((err: HttpErrorResponse) => { + console.log(err); + return of(false); + }) + ); +}; diff --git a/src/app/service/auth.service.ts b/src/app/service/auth.service.ts new file mode 100644 index 0000000..012ee00 --- /dev/null +++ b/src/app/service/auth.service.ts @@ -0,0 +1,21 @@ +import {inject, Injectable} from "@angular/core"; +import {HttpClient} from "@angular/common/http"; +import {AuthResponse} from "../models"; + + +@Injectable({ + providedIn: 'root', +}) +export default class AuthService { + http: HttpClient = inject(HttpClient); + + register(data: { email: string, password: string }): void { + console.log(data); + + } + + login(loginData: { username: string, password: string }) { + return this.http.post('https://dummyjson.com/auth/login', loginData) + } +} + diff --git a/src/app/service/custom.validators.ts b/src/app/service/custom.validators.ts new file mode 100644 index 0000000..3fb6391 --- /dev/null +++ b/src/app/service/custom.validators.ts @@ -0,0 +1,31 @@ +import {AbstractControl, ValidatorFn} from "@angular/forms"; + + +export default class CustomValidators { + public static email(): ValidatorFn { + return (emailGroup: AbstractControl) => { + if (emailGroup.get('email')?.value !== emailGroup.get('confirmEmail')) { + return {emailMismatch: true} + } + + return {}; + } + } + + public static password(): ValidatorFn { + return (passwordGroup: AbstractControl) => { + if (passwordGroup.get('password')?.value !== passwordGroup.get('confirmPassword')) { + return {emailMismatch: true} + } + + return {}; + } + } + + static address(): ValidatorFn { + return (addressGroup: AbstractControl) => { + + return {}; + } + } +} diff --git a/src/app/service/http.interceptor.ts b/src/app/service/http.interceptor.ts new file mode 100644 index 0000000..e030ba3 --- /dev/null +++ b/src/app/service/http.interceptor.ts @@ -0,0 +1,9 @@ +import {HttpHandlerFn, HttpInterceptorFn, HttpRequest} from "@angular/common/http"; + +export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest, next: HttpHandlerFn) => { + return next(req.clone({ + setHeaders: { + Authorization: "Bearer " + localStorage.getItem('access_token') + } + })); +}