diff options
Diffstat (limited to 'frontend/src')
21 files changed, 492 insertions, 13 deletions
diff --git a/frontend/src/app/_pages/login-page/login-page.component.css b/frontend/src/app/_pages/login-page/login-page.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/login-page/login-page.component.css diff --git a/frontend/src/app/_pages/login-page/login-page.component.html b/frontend/src/app/_pages/login-page/login-page.component.html new file mode 100644 index 00000000..906eaba6 --- /dev/null +++ b/frontend/src/app/_pages/login-page/login-page.component.html @@ -0,0 +1,43 @@ +<div style="min-height: 100vh; position: relative;"> + + <!-- TODO : <app-navbar [activeNav]="'login'"></app-navbar>--> + + <div class="container p-5 rounded-3 shadow-sm border" style="max-width: 50em; margin-top: 50px;"> + <h3 class="text-center pb-5">Prijavite se</h3> + <form> + <!-- Email input --> + <div class="form-outline mb-4"> + <label class="form-label" for="email">Email adresa</label> + <input [(ngModel)]="email" name="email" type="email" id="email" class="form-control form-control-lg" + placeholder="Unesite email adresu..." /> + </div> + + <!-- Password input --> + <div class="form-outline mb-3"> + <label class="form-label" for="password">Lozinka</label> + <input [(ngModel)]="password" name="password" type="password" id="password" + class="form-control form-control-lg" placeholder="Unesite lozinku..." /> + </div> + + <div class="text-center text-lg-start mt-4 pt-2"> + <!-- Pogresna lozinka --> + <p *ngIf="wrongCreds" class="small fw-bold mt-2 pt-1 mb-0 text-danger">Lozinka ili e-mail su pogrešni + </p> + <!-- Nepotvrdjena registracija + <p *ngIf="notApproved" class="small fw-bold mt-2 pt-1 mb-0 text-danger">Vaša registracija još uvek nije potvrđena</p>--> + <br> + + <button type="button" class="btn btn-primary btn-lg" + style="padding-left: 2.5rem; padding-right: 2.5rem;" (click)="onSubmit()">Prijava + </button> + + <p class="small fw-bold mt-2 pt-1 mb-0">Još uvek nemate nalog? + <a routerLink="/register" class="link-danger">Registrujte se</a> + </p> + </div> + </form> + </div> + + <!-- TODO: <app-footer></app-footer>--> + +</div>
\ No newline at end of file diff --git a/frontend/src/app/_pages/login-page/login-page.component.spec.ts b/frontend/src/app/_pages/login-page/login-page.component.spec.ts new file mode 100644 index 00000000..9da3aca8 --- /dev/null +++ b/frontend/src/app/_pages/login-page/login-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginPageComponent } from './login-page.component'; + +describe('LoginPageComponent', () => { + let component: LoginPageComponent; + let fixture: ComponentFixture<LoginPageComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LoginPageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/login-page/login-page.component.ts b/frontend/src/app/_pages/login-page/login-page.component.ts new file mode 100644 index 00000000..f8b429e3 --- /dev/null +++ b/frontend/src/app/_pages/login-page/login-page.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit } from '@angular/core'; +import { CookieService } from 'ngx-cookie-service'; +import { AuthService } from 'src/app/_services/auth.service'; + +@Component({ + selector: 'app-login-page', + templateUrl: './login-page.component.html', + styleUrls: ['./login-page.component.css'] +}) +export class LoginPageComponent implements OnInit { + email: string = ''; + password: string = ''; + + public wrongCreds: boolean = false; //RAZMOTRITI + //public notApproved: boolean = false; //RAZMOTRITI + + pattEmail: RegExp = /^[a-zA-Z0-9]+([\.\-\+][a-zA-Z0-9]+)*\@([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}$/; + + constructor( + private authService: AuthService, + private cookie: CookieService + ) { } + + ngOnInit(): void { + } + + onSubmit() { + if (!this.pattEmail.test(this.email)) { + console.warn('Bad email!'); + return; + } + else { + this.authService.login(this.email, this.password).subscribe((response) => { + console.log(response); + this.cookie.set('token', response); + }) + } + } + +} diff --git a/frontend/src/app/_pages/only-authorized/only-authorized.component.css b/frontend/src/app/_pages/only-authorized/only-authorized.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/only-authorized/only-authorized.component.css diff --git a/frontend/src/app/_pages/only-authorized/only-authorized.component.html b/frontend/src/app/_pages/only-authorized/only-authorized.component.html new file mode 100644 index 00000000..27bbcf09 --- /dev/null +++ b/frontend/src/app/_pages/only-authorized/only-authorized.component.html @@ -0,0 +1 @@ +<p>only-authorized works!</p> diff --git a/frontend/src/app/_pages/only-authorized/only-authorized.component.spec.ts b/frontend/src/app/_pages/only-authorized/only-authorized.component.spec.ts new file mode 100644 index 00000000..82a01f63 --- /dev/null +++ b/frontend/src/app/_pages/only-authorized/only-authorized.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OnlyAuthorizedComponent } from './only-authorized.component'; + +describe('OnlyAuthorizedComponent', () => { + let component: OnlyAuthorizedComponent; + let fixture: ComponentFixture<OnlyAuthorizedComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ OnlyAuthorizedComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(OnlyAuthorizedComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/only-authorized/only-authorized.component.ts b/frontend/src/app/_pages/only-authorized/only-authorized.component.ts new file mode 100644 index 00000000..be88365f --- /dev/null +++ b/frontend/src/app/_pages/only-authorized/only-authorized.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-only-authorized', + templateUrl: './only-authorized.component.html', + styleUrls: ['./only-authorized.component.css'] +}) +export class OnlyAuthorizedComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_pages/register-page/register-page.component.css b/frontend/src/app/_pages/register-page/register-page.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/register-page/register-page.component.css diff --git a/frontend/src/app/_pages/register-page/register-page.component.html b/frontend/src/app/_pages/register-page/register-page.component.html new file mode 100644 index 00000000..f8ae046e --- /dev/null +++ b/frontend/src/app/_pages/register-page/register-page.component.html @@ -0,0 +1,80 @@ +<div style="min-height: 100vh; position: relative;"> + + <!-- TODO <app-navbar [activeNav]="'register'"></app-navbar>--> + + <div class="container" style="margin-top: 50px;"> + <div class="text-black"> + <div class="row justify-content-center"> + <div class="col-8 bg-white border rounded-3 shadow-sm p-5"> + <p class="text-center h2 fw-bold mb-5 mx-1 mx-md-4">Registracija</p> + + <form class="mx-1 mx-md-4"> + <!--Ime--> + <div class="row"> + <div class="col-6 p-2"> + <label class="form-label" for="firstName">Ime</label> + <input type="text" id="firstName" class="form-control" [(ngModel)]="firstName" name="firstName"> + <p *ngIf="wrongFirstNameBool" class="small fw-bold text-danger">Unesite ispravno ime. (minimum 1, maksimum 30 karaktera)</p> + </div> + <!--Prezime--> + <div class="col-6 p-2"> + <label class="form-label" for="lastName">Prezime</label> + <input type="text" id="lastName" class="form-control" [(ngModel)]="lastName" name="lastName" /> + <p *ngIf="wrongLastNameBool" class="small fw-bold text-danger">Unesite ispravno prezime. (minimum 1, maksimum 30 karaktera)</p> + </div> + </div> + <br> + + <div class="row"> + <!--Korisnicko ime--> + <div class="col-6 p-2"> + <label class="form-label" for="nickName">Korisničko ime</label> + <input type="text" id="nickName" class="form-control" [(ngModel)]="nickName" name="nickName" /> + <p *ngIf="wrongNickNameBool" class="small fw-bold text-danger">Unesite ispravno korisničko ime.</p> + </div> + <!--Email--> + <div class="col-6 p-2"> + <label class="form-label" for="email">E-mail adresa</label> + <input type="email" id="email" class="form-control" [(ngModel)]="email" name="email" /> + <p *ngIf="wrongEmailBool" class="small fw-bold text-danger">Unesite ispravnu e-mail adresu.</p> + </div> + </div> + <br> + + <div class="row"> + <!-- Lozinka 1. --> + <div class="col-6 p-2"> + <label class="form-label" for="pass1">Lozinka</label> + <input type="password" id="pass1" class="form-control" [(ngModel)]="pass1" name="pass1" /> + <p *ngIf="wrongPass1Bool" class="small fw-bold text-danger">Lozinka se mora sastojati od najmanje 6 karaktera.</p> + </div> + + <!-- Lozinka 2. --> + <div class="col-6 p-2"> + <label class="form-label" for="pass2">Potvrdite lozinku</label> + <input type="password" id="pass2" class="form-control" [(ngModel)]="pass2" name="pass2" /> + <p *ngIf="wrongPass2Bool" class="small fw-bold text-danger">Lozinke se ne podudaraju.</p> + </div> + </div> + + <br><br><br> + <!--Dugme Registruj se--> + <div class="d-flex justify-content-center mx-4 mb-3 mb-lg-4"> + <button type="button" class="btn btn-primary btn-lg" (click)="validation()">Registrujte se</button> + </div> + + <div class="form-check d-flex justify-content-center mb-5"> + <label class="form-check-label" class="small fw-bold mt-2 pt-1 mb-0" for="form2Example3"> + Već imate kreiran nalog? + <a routerLink="/login" class="link-danger">Prijavite se</a> + </label> + </div> + </form> + </div> + </div> + </div> + </div> + + <!-- TODO <app-footer></app-footer> --> + +</div>
\ No newline at end of file diff --git a/frontend/src/app/_pages/register-page/register-page.component.spec.ts b/frontend/src/app/_pages/register-page/register-page.component.spec.ts new file mode 100644 index 00000000..347fe9f4 --- /dev/null +++ b/frontend/src/app/_pages/register-page/register-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegisterPageComponent } from './register-page.component'; + +describe('RegisterPageComponent', () => { + let component: RegisterPageComponent; + let fixture: ComponentFixture<RegisterPageComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ RegisterPageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RegisterPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/register-page/register-page.component.ts b/frontend/src/app/_pages/register-page/register-page.component.ts new file mode 100644 index 00000000..e8d4c036 --- /dev/null +++ b/frontend/src/app/_pages/register-page/register-page.component.ts @@ -0,0 +1,138 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { AuthService } from 'src/app/_services/auth.service'; + +@Component({ + selector: 'app-register-page', + templateUrl: './register-page.component.html', + styleUrls: ['./register-page.component.css'] +}) +export class RegisterPageComponent implements OnInit { + firstName: string = ''; + lastName: string = ''; + nickName: string = ''; + email: string = ''; + pass1: string = ''; + pass2: string = ''; + + wrongFirstNameBool: boolean = false; + wrongLastNameBool: boolean = false; + wrongNickNameBool: boolean = false; + wrongEmailBool: boolean = false; + wrongPass1Bool: boolean = false; + wrongPass2Bool: boolean = false; + + pattName: RegExp = /^[a-zA-ZšŠđĐčČćĆžŽ]+([ \-][a-zA-ZšŠđĐčČćĆžŽ]+)*$/; + pattTwoSpaces: RegExp = / /; + pattEmail: RegExp = /^[a-zA-Z0-9]+([\.\-\+][a-zA-Z0-9]+)*\@([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}$/; + pattPassword: RegExp = /.{6,30}$/; + + constructor( + private router: Router, + private authService: AuthService, + ) { } + + ngOnInit(): void { + } + + isCorrectName(element: string): boolean { + if (this.pattName.test(element) && !(this.pattTwoSpaces.test(element)) && (element.length >= 1 && element.length <= 30)) + return true; + return false; + } + isCorrectEmail(element: string): boolean { + if (this.pattEmail.test(element.toLowerCase()) && element.length <= 320) + return true; + return false; + } + isCorrectPassword(element: string): boolean { + if (this.pattPassword.test(element)) + return true; + return false; + } + + firstNameValidation() { + if (this.isCorrectName(this.firstName) == true) { + this.wrongFirstNameBool = false; + return; + } + (<HTMLSelectElement>document.getElementById('firstName')).focus(); + this.wrongFirstNameBool = true; + } + lastNameValidation() { + if (this.isCorrectName(this.lastName) == true) { + this.wrongLastNameBool = false; + return; + } + (<HTMLSelectElement>document.getElementById('lastName')).focus(); + this.wrongLastNameBool = true; + } + nickNameValidation() { + if (this.isCorrectName(this.nickName) == true) { + this.wrongNickNameBool = false; + return; + } + (<HTMLSelectElement>document.getElementById('nickName')).focus(); + this.wrongNickNameBool = true; + } + emailValidation() { + if (this.isCorrectEmail(this.email) == true) { + this.wrongEmailBool = false; + return; + } + (<HTMLSelectElement>document.getElementById('email')).focus(); + this.wrongEmailBool = true; + } + passwordValidation() { + if (this.isCorrectPassword(this.pass1) && this.isCorrectPassword(this.pass2) && this.pass1 == this.pass2) { + this.wrongPass1Bool = false; + this.wrongPass2Bool = false; + return; + } + this.pass1 = ''; //brisi obe ukucane lozinke + this.pass2 = ''; + (<HTMLSelectElement>document.getElementById('pass1')).focus(); + this.wrongPass1Bool = true; + this.wrongPass2Bool = true; + } + + validation() { + this.firstName = this.firstName.trim(); + this.lastName = this.lastName.trim(); + this.nickName = this.nickName.trim(); + this.email = this.email.trim(); + + this.firstNameValidation(); + this.lastNameValidation(); + this.nickNameValidation(); + this.emailValidation(); + this.passwordValidation(); + + if (!(this.wrongFirstNameBool || this.wrongLastNameBool || this.wrongNickNameBool || + this.wrongEmailBool || this.wrongPass1Bool || this.wrongPass2Bool)) { //sve ok, registruj ga + + let user = { + firstName: this.firstName, + lastName: this.lastName, + nickName: this.nickName, + email: this.email, + password: this.pass1 + } + + this.authService.register(user) + .subscribe( + (response) => { + console.log(response); + /*if () + this.router.navigate(['/login']); //registracija uspesna, idi na login + else if () + alert('Nalog sa unetim email-om već postoji!');*/ + } + ); + } + } + + + + +} diff --git a/frontend/src/app/_services/auth-guard.service.spec.ts b/frontend/src/app/_services/auth-guard.service.spec.ts new file mode 100644 index 00000000..35afd377 --- /dev/null +++ b/frontend/src/app/_services/auth-guard.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthGuardService } from './auth-guard.service'; + +describe('AuthGuardService', () => { + let service: AuthGuardService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AuthGuardService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_services/auth-guard.service.ts b/frontend/src/app/_services/auth-guard.service.ts new file mode 100644 index 00000000..b6d3678d --- /dev/null +++ b/frontend/src/app/_services/auth-guard.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { Observable } from 'rxjs'; +import { AuthService } from './auth.service'; + + +@Injectable({ + providedIn: 'root' +}) +export class AuthGuardService implements CanActivate { + + constructor(private auth: AuthService, private router: Router) { } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> { + if (this.auth.isAuthenticated()) { + return true; + } + this.router.navigate(['login']); + return false; + } +} diff --git a/frontend/src/app/auth.service.spec.ts b/frontend/src/app/_services/auth.service.spec.ts index f1251cac..f1251cac 100644 --- a/frontend/src/app/auth.service.spec.ts +++ b/frontend/src/app/_services/auth.service.spec.ts diff --git a/frontend/src/app/_services/auth.service.ts b/frontend/src/app/_services/auth.service.ts new file mode 100644 index 00000000..7129b95b --- /dev/null +++ b/frontend/src/app/_services/auth.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { JwtHelperService } from '@auth0/angular-jwt'; +import { CookieService } from 'ngx-cookie-service'; +import { API_SETTINGS } from 'src/config'; + +const jwtHelper = new JwtHelperService(); + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + + constructor(private http: HttpClient, private cookie: CookieService) { } + + login(username: string, password: string) { + return this.http.post(`${API_SETTINGS.apiURL}/auth/login`, { username, password }, { responseType: 'text' }); + } + + register(user: any) { + return this.http.post(`${API_SETTINGS.apiURL}/auth/register`, user); + } + + isAuthenticated(): boolean { + if (this.cookie.check('token')) { + var token = this.cookie.get('token'); + return !jwtHelper.isTokenExpired(token); + } + return false; + } +} diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 02972627..1868e56c 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -1,7 +1,16 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -const routes: Routes = []; +import { LoginPageComponent } from './_pages/login-page/login-page.component'; +import { OnlyAuthorizedComponent } from './_pages/only-authorized/only-authorized.component'; +import { RegisterPageComponent } from './_pages/register-page/register-page.component'; +import { AuthGuardService } from './_services/auth-guard.service'; + +const routes: Routes = [ + { path: 'login', component: LoginPageComponent }, + { path: 'register', component: RegisterPageComponent }, + { path: 'only-authorized', component: OnlyAuthorizedComponent, canActivate: [AuthGuardService] } +]; @NgModule({ imports: [RouterModule.forRoot(routes)], diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index b1c6c96a..9ccd7ddb 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -1,16 +1,28 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; - +import { FormsModule } from '@angular/forms'; import { AppRoutingModule } from './app-routing.module'; +import { HttpClientModule } from '@angular/common/http'; + import { AppComponent } from './app.component'; +import { LoginPageComponent } from './_pages/login-page/login-page.component'; +import { RegisterPageComponent } from './_pages/register-page/register-page.component'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { OnlyAuthorizedComponent } from './_pages/only-authorized/only-authorized.component'; @NgModule({ declarations: [ - AppComponent + AppComponent, + LoginPageComponent, + RegisterPageComponent, + OnlyAuthorizedComponent ], imports: [ BrowserModule, - AppRoutingModule + AppRoutingModule, + FormsModule, + HttpClientModule, + NgbModule ], providers: [], bootstrap: [AppComponent] diff --git a/frontend/src/app/auth.service.ts b/frontend/src/app/auth.service.ts deleted file mode 100644 index af27fdec..00000000 --- a/frontend/src/app/auth.service.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root' -}) -export class AuthService { - - constructor() { } -} diff --git a/frontend/src/config.ts b/frontend/src/config.ts new file mode 100644 index 00000000..8c48672e --- /dev/null +++ b/frontend/src/config.ts @@ -0,0 +1,3 @@ +export const API_SETTINGS = { + apiURL: 'http://localhost:5283/api' +}
\ No newline at end of file diff --git a/frontend/src/polyfills.ts b/frontend/src/polyfills.ts index 429bb9ef..9194e82b 100644 --- a/frontend/src/polyfills.ts +++ b/frontend/src/polyfills.ts @@ -1,3 +1,7 @@ +/*************************************************************************************************** + * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. + */ +import '@angular/localize/init'; /** * This file includes polyfills needed by Angular and is loaded before the app. * You can add your own extra polyfills to this file. |