diff options
Diffstat (limited to 'frontend/src/app')
78 files changed, 1340 insertions, 446 deletions
diff --git a/frontend/src/app/Shared.ts b/frontend/src/app/Shared.ts new file mode 100644 index 00000000..126dc846 --- /dev/null +++ b/frontend/src/app/Shared.ts @@ -0,0 +1,7 @@ +class Shared { + constructor( + public loggedIn: boolean + ) { } +} + +export default new Shared(false);
\ No newline at end of file diff --git a/frontend/src/app/_data/Dataset.ts b/frontend/src/app/_data/Dataset.ts new file mode 100644 index 00000000..aaee50eb --- /dev/null +++ b/frontend/src/app/_data/Dataset.ts @@ -0,0 +1,13 @@ +export default class Dataset { + constructor( + public name: string = 'Novi izvor podataka', + public description: string = '', + public header: string[] = [], + public fileId?: number, + public extension: string = '.csv', + public isPublic: boolean = false, + public accessibleByLink: boolean = false, + public dateCreated: Date = new Date(), + public lastUpdated: Date = new Date() + ) { } +}
\ No newline at end of file diff --git a/frontend/src/app/_data/Model.ts b/frontend/src/app/_data/Model.ts index 216e1c36..1a120ca7 100644 --- a/frontend/src/app/_data/Model.ts +++ b/frontend/src/app/_data/Model.ts @@ -3,11 +3,13 @@ export default class Model { public name: string = 'Novi model', public description: string = '', public dateCreated: Date = new Date(), + public lastUpdated: Date = new Date(), public datasetId?: number, - //Test set settings + // Test set settings public inputColumns: number[] = [0], public columnToPredict: number = 1, + public randomOrder: boolean = true, public randomTestSet: boolean = true, public randomTestSetDistribution: number = 0.10, //0.1-0.9 (10% - 90%) @@ -31,6 +33,8 @@ export enum ANNType { Convolutional = 'konvoluciona' } +// replaceMissing srednja vrednost mean, median, najcesca vrednost (mode) +// removeOutliers export enum Encoding { Label = 'label', OneHot = 'one hot' diff --git a/frontend/src/app/_data/Predictor.ts b/frontend/src/app/_data/Predictor.ts new file mode 100644 index 00000000..05b993f1 --- /dev/null +++ b/frontend/src/app/_data/Predictor.ts @@ -0,0 +1,11 @@ +export default class Predictor { + constructor( + public name: string = 'Novi prediktor', + public description: string = '', + public inputs: string[] = [], + public output: string = '', + public isPublic: boolean = false, + public accessibleByLink: boolean = false, + public dateCreated: Date = new Date() + ) { } +}
\ No newline at end of file diff --git a/frontend/src/app/_pages/login-page/login-page.component.css b/frontend/src/app/_elements/carousel/carousel.component.css index e69de29b..e69de29b 100644 --- a/frontend/src/app/_pages/login-page/login-page.component.css +++ b/frontend/src/app/_elements/carousel/carousel.component.css diff --git a/frontend/src/app/_elements/carousel/carousel.component.html b/frontend/src/app/_elements/carousel/carousel.component.html new file mode 100644 index 00000000..755899a7 --- /dev/null +++ b/frontend/src/app/_elements/carousel/carousel.component.html @@ -0,0 +1,14 @@ +<div class="container"> + <div class="row d-flex align-items-stretch flex-row mx-5 align-items-stretch"> + <div class="col my-1" *ngFor=" let item of items" [ngSwitch]="item.constructor.name"> + <ng-template ngSwitchCase="Dataset"> + <app-item-dataset [dataset]="item"> + </app-item-dataset> + </ng-template> + <ng-template ngSwitchCase="Predictor"> + <app-item-predictor [predictor]="item"> + </app-item-predictor> + </ng-template> + </div> + </div> +</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/_elements/carousel/carousel.component.spec.ts index 9da3aca8..9196e044 100644 --- a/frontend/src/app/_pages/login-page/login-page.component.spec.ts +++ b/frontend/src/app/_elements/carousel/carousel.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { LoginPageComponent } from './login-page.component'; +import { CarouselComponent } from './carousel.component'; -describe('LoginPageComponent', () => { - let component: LoginPageComponent; - let fixture: ComponentFixture<LoginPageComponent>; +describe('CarouselComponent', () => { + let component: CarouselComponent; + let fixture: ComponentFixture<CarouselComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ LoginPageComponent ] + declarations: [ CarouselComponent ] }) .compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(LoginPageComponent); + fixture = TestBed.createComponent(CarouselComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/frontend/src/app/_elements/carousel/carousel.component.ts b/frontend/src/app/_elements/carousel/carousel.component.ts new file mode 100644 index 00000000..ed4fa4a5 --- /dev/null +++ b/frontend/src/app/_elements/carousel/carousel.component.ts @@ -0,0 +1,17 @@ +import { Component, Input, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-carousel', + templateUrl: './carousel.component.html', + styleUrls: ['./carousel.component.css'] +}) +export class CarouselComponent { + + @Input() items: any[] = []; + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.css b/frontend/src/app/_elements/dataset-load/dataset-load.component.css index e69de29b..05819702 100644 --- a/frontend/src/app/_elements/dataset-load/dataset-load.component.css +++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.css @@ -0,0 +1,6 @@ +#divInputs { + margin-left: 20px; +} +#divOutputs { + margin-left: 20px; +}
\ No newline at end of file diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.html b/frontend/src/app/_elements/dataset-load/dataset-load.component.html index c89add43..16830e11 100644 --- a/frontend/src/app/_elements/dataset-load/dataset-load.component.html +++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.html @@ -1,19 +1,21 @@ <div> - <input style="display: inline-block; width:350px;" list=delimiterOptions + <div class="d-flex justify-content-center"> + <input style="display: inline-block; width:350px;" list=delimiterOptions placeholder="Izaberite ili ukucajte delimiter za .csv fajl" class="form-control" [(ngModel)]="delimiter" (input)="update()"> - <datalist id=delimiterOptions> - <option *ngFor="let option of delimiterOptions">{{option}}</option> - </datalist> - - <label for="checkboxHeader">Da li .csv ima header?</label> - <input (input)="update()" [(ngModel)]="hasHeader" type="checkbox" value="" id="checkboxHeader" checked> - <br><br> + <datalist id=delimiterOptions> + <option *ngFor="let option of delimiterOptions">{{option}}</option> + </datalist> + + <label for="type" class="form-check-label">Da li .csv ima header? + <input class="mx-3 form-check-input" type="checkbox" (input)="update()" [(ngModel)]="hasHeader" type="checkbox" value="" id="checkboxHeader" checked> + </label> - <input id="fileInput" class="form-control mb-5" type="file" class="upload" (change)="changeListener($event)" accept=".csv"> - - <table *ngIf="csvRecords.length > 0 && hasHeader" class="table table-bordered table-light mt-5"> + <input id="fileInput" class="form-control" type="file" class="upload" (change)="changeListener($event)" accept=".csv"> + </div> + + <table *ngIf="csvRecords.length > 0 && hasHeader" class="table table-bordered table-light my-4"> <thead> <tr> <th *ngFor="let item of csvRecords[0]; let i = index">{{item}}</th> @@ -26,7 +28,7 @@ </tbody> </table> - <table *ngIf="csvRecords.length > 0 && !hasHeader" class="table table-bordered table-light mt-5"> + <table *ngIf="csvRecords.length > 0 && !hasHeader" class="table table-bordered table-light mt-4"> <tbody> <tr *ngFor="let row of csvRecords | slice:0:10"> <td *ngFor="let col of row">{{col}}</td> @@ -39,4 +41,35 @@ {{rowsNumber}} x {{colsNumber}} </div> + <div *ngIf="csvRecords.length > 0" class="mt-2"> + <div class="row"> + <div class="col d-flex justify-content-center"> + <h3>Izaberite ulazne kolone:</h3> + <div id="divInputs" class="form-check"> + <br> + <div *ngFor="let item of csvRecords[0]; let i = index"> + <input *ngIf="i == 0" class="form-check-input" type="checkbox" value="{{item}}" id="cb_{{item}}" name="cbs" checked> + <input *ngIf="i != 0" class="form-check-input" type="checkbox" value="{{item}}" id="cb_{{item}}" name="cbs"> + <label class="form-check-label" for="cb_{{item}}"> + {{item}} + </label> + </div> + </div> + </div> + <div class="col d-flex justify-content-left"> + <h3>Izaberite izlaznu kolonu:</h3> + <div id="divOutputs" class="form-check"> + <br> + <div *ngFor="let item of csvRecords[0]; let i = index"> + <input *ngIf="i == 0" class="form-check-input" type="radio" value="{{item}}" id="rb_{{item}}" name="rbs" checked> + <input *ngIf="i != 0" class="form-check-input" type="radio" value="{{item}}" id="rb_{{item}}" name="rbs"> + <label class="form-check-label" for="rb_{{item}}"> + {{item}} + </label> + </div> + </div> + </div> + </div> + </div> + </div>
\ No newline at end of file diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts index 843a5709..c772dc35 100644 --- a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts +++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts @@ -20,6 +20,9 @@ export class DatasetLoadComponent { rowsNumber: number = 0; colsNumber: number = 0; + checkedInputCols: Array<string> = []; + checkedOutputCol: string = ''; + constructor(private ngxCsvParser: NgxCsvParser) { } @@ -38,7 +41,7 @@ export class DatasetLoadComponent { this.ngxCsvParser.parse(this.files[0], { header: false, delimiter: (this.delimiter == "razmak") ? " " : (this.delimiter == "") ? "," : this.delimiter}) .pipe().subscribe((result) => { - //console.log('Result', result); + console.log('Result', result); if (result.constructor === Array) { this.csvRecords = result; if (this.hasHeader) @@ -51,4 +54,44 @@ export class DatasetLoadComponent { console.log('Error', error); }); } + + getCheckedInputCols() : Array<string> { + this.checkedInputCols = new Array<string>(); + let checkboxes = document.getElementsByName("cbs"); + + for (let i = 0; i < checkboxes.length; i++) { + let thatCb = <HTMLInputElement>checkboxes[i]; + if (thatCb.checked) + this.checkedInputCols.push(thatCb.value); + } + //console.log(this.checkedInputCols); + return this.checkedInputCols; + } + getCheckedOutputCol() : string { + this.checkedOutputCol = ''; + let radiobuttons = document.getElementsByName("rbs"); + + for (let i = 0; i < radiobuttons.length; i++) { + let thatRb = <HTMLInputElement>radiobuttons[i]; + if (thatRb.checked) { + this.checkedOutputCol = thatRb.value; + break; + } + } + //console.log(this.checkedOutputCol); + return this.checkedOutputCol; + } + validationInputsOutput() { + if (this.checkedInputCols.length == 0) { + alert("Molimo Vas da izaberete ulaznu kolonu/kolone za mrežu.") + return; + } + for (let i = 0; i < this.checkedInputCols.length; i++) { + if (this.checkedInputCols[i] == this.checkedOutputCol) { + let colName = this.checkedOutputCol; + alert("Izabrali ste istu kolonu (" + colName + ") kao ulaznu i izlaznu iz mreže. Korigujte izbor."); + return; + } + } + } } diff --git a/frontend/src/app/_pages/only-authorized/only-authorized.component.css b/frontend/src/app/_elements/item-dataset/item-dataset.component.css index e69de29b..e69de29b 100644 --- a/frontend/src/app/_pages/only-authorized/only-authorized.component.css +++ b/frontend/src/app/_elements/item-dataset/item-dataset.component.css diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.html b/frontend/src/app/_elements/item-dataset/item-dataset.component.html new file mode 100644 index 00000000..cf39a125 --- /dev/null +++ b/frontend/src/app/_elements/item-dataset/item-dataset.component.html @@ -0,0 +1,15 @@ +<div class="card" style="min-width: 12rem;"> + <div class="card-header"> + {{dataset.name}} + </div> + <div class="card-body"> + <p class="card-text"> + {{dataset.description}} + </p> + <table class="table table-bordered table-sm"> + <thead> + <th scope="col" *ngFor="let column of dataset.header">{{column}}</th> + </thead> + </table> + </div> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts b/frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts new file mode 100644 index 00000000..603889b2 --- /dev/null +++ b/frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ItemDatasetComponent } from './item-dataset.component'; + +describe('ItemDatasetComponent', () => { + let component: ItemDatasetComponent; + let fixture: ComponentFixture<ItemDatasetComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ItemDatasetComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemDatasetComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.ts b/frontend/src/app/_elements/item-dataset/item-dataset.component.ts new file mode 100644 index 00000000..e12de34d --- /dev/null +++ b/frontend/src/app/_elements/item-dataset/item-dataset.component.ts @@ -0,0 +1,15 @@ +import { Component, Input, OnInit } from '@angular/core'; +import Dataset from 'src/app/_data/Dataset'; + +@Component({ + selector: 'app-item-dataset', + templateUrl: './item-dataset.component.html', + styleUrls: ['./item-dataset.component.css'] +}) +export class ItemDatasetComponent { + + @Input() dataset: Dataset = new Dataset(); + + constructor() { + } +} diff --git a/frontend/src/app/_pages/register-page/register-page.component.css b/frontend/src/app/_elements/item-predictor/item-predictor.component.css index e69de29b..e69de29b 100644 --- a/frontend/src/app/_pages/register-page/register-page.component.css +++ b/frontend/src/app/_elements/item-predictor/item-predictor.component.css diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.html b/frontend/src/app/_elements/item-predictor/item-predictor.component.html new file mode 100644 index 00000000..92d747e2 --- /dev/null +++ b/frontend/src/app/_elements/item-predictor/item-predictor.component.html @@ -0,0 +1,24 @@ +<div class="card" style="min-width: 12rem;"> + <div class="card-header"> + {{predictor.name}} + </div> + <div class="card-body"> + <p class="card-text"> + {{predictor.description}} + </p> + <div class="d-flex flex-column align-items-center"> + <table class="table table-bordered table-sm"> + <thead> + <th class="text-center" *ngFor="let column of predictor.inputs">{{column}}</th> + </thead> + </table> + <mat-icon>arrow_downward</mat-icon> + <p> + {{predictor.output}} + </p> + </div> + </div> + <div class="card-footer text-center"> + <a routerLink="predict" mat-raised-button color="primary">Iskoristi</a> + </div> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts b/frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts new file mode 100644 index 00000000..b5c2d91c --- /dev/null +++ b/frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ItemPredictorComponent } from './item-predictor.component'; + +describe('ItemPredictorComponent', () => { + let component: ItemPredictorComponent; + let fixture: ComponentFixture<ItemPredictorComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ItemPredictorComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemPredictorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.ts b/frontend/src/app/_elements/item-predictor/item-predictor.component.ts new file mode 100644 index 00000000..cc782f45 --- /dev/null +++ b/frontend/src/app/_elements/item-predictor/item-predictor.component.ts @@ -0,0 +1,18 @@ +import { Component, Input, OnInit } from '@angular/core'; +import Predictor from 'src/app/_data/Predictor'; + +@Component({ + selector: 'app-item-predictor', + templateUrl: './item-predictor.component.html', + styleUrls: ['./item-predictor.component.css'] +}) +export class ItemPredictorComponent implements OnInit { + + @Input() predictor: Predictor = new Predictor(); + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_elements/navbar/navbar.component.css b/frontend/src/app/_elements/navbar/navbar.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_elements/navbar/navbar.component.css diff --git a/frontend/src/app/_elements/navbar/navbar.component.html b/frontend/src/app/_elements/navbar/navbar.component.html new file mode 100644 index 00000000..b9c450af --- /dev/null +++ b/frontend/src/app/_elements/navbar/navbar.component.html @@ -0,0 +1,49 @@ +<header class="sticky-top p-3 bg-dark text-white"> + <div class="container"> + <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start"> + <a routerLink="" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none"> + <img src="../../../assets/svg/logo_no_text.svg" class="bi me-2" width="64" height="40"> + </a> + + <ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0"> + <li><a routerLink="" class="nav-link px-2" + [class]="(currentUrl === '') ? 'text-secondary' : 'text-white'">Početna</a></li> + <li><a routerLink="add-model" class="nav-link px-2" + [class]="(currentUrl === '/add-model') ? 'text-secondary' : 'text-white'">Dodaj model</a></li> + <li><a routerLink="my-predictors" class="nav-link px-2" + [class]="(currentUrl === '/my-predictors') ? 'text-secondary' : 'text-white'">Predvidi</a></li> + </ul> + + <div *ngIf="shared.loggedIn" class="dropdown text-end"> + <a href="#" class="d-block link-light text-decoration-none dropdown-toggle" id="dropdownUser1" + data-bs-toggle="dropdown" aria-expanded="false"> + <img src="https://github.com/mdo.png" alt="mdo" width="32" height="32" class="rounded-circle"> + </a> + <ul class="dropdown-menu text-small" aria-labelledby="dropdownUser1" + style="position: absolute; inset: 0px 0px auto auto; margin: 0px; transform: translate(0px, 34px);" + data-popper-placement="bottom-end"> + <li><a class="dropdown-item" routerLink="add-model">Nov model...</a></li> + <li><a class="dropdown-item" routerLink="settings">Podešavanja</a></li> + <li><a class="dropdown-item" routerLink="profile">Moj profil</a></li> + <li> + <hr class="dropdown-divider"> + </li> + <li><a class="dropdown-item" routerLink="" (click)="logOut()">Odjavi se</a></li> + </ul> + </div> + <div *ngIf="!shared.loggedIn" class="dropdown text-end"> + <button type="button" mat-raised-button color="primary" class="mx-2" data-bs-toggle="modal" + data-bs-target="#modalForLogin"> + Prijavi se + </button> + <button type="button" mat-raised-button color="primary" data-bs-toggle="modal" + data-bs-target="#modalForRegister"> + Registruj se + </button> + </div> + </div> + </div> +</header> + +<app-login-modal></app-login-modal> +<app-register-modal></app-register-modal>
\ No newline at end of file diff --git a/frontend/src/app/_elements/navbar/navbar.component.spec.ts b/frontend/src/app/_elements/navbar/navbar.component.spec.ts new file mode 100644 index 00000000..f8ccd6f4 --- /dev/null +++ b/frontend/src/app/_elements/navbar/navbar.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NavbarComponent } from './navbar.component'; + +describe('NavbarComponent', () => { + let component: NavbarComponent; + let fixture: ComponentFixture<NavbarComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ NavbarComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(NavbarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_elements/navbar/navbar.component.ts b/frontend/src/app/_elements/navbar/navbar.component.ts new file mode 100644 index 00000000..c16e3e9d --- /dev/null +++ b/frontend/src/app/_elements/navbar/navbar.component.ts @@ -0,0 +1,30 @@ +import { Component, OnInit } from '@angular/core'; +import { Location } from '@angular/common'; +import { AuthService } from '../../_services/auth.service'; +import shared from 'src/app/Shared'; + +@Component({ + selector: 'app-navbar', + templateUrl: './navbar.component.html', + styleUrls: ['./navbar.component.css'] +}) +export class NavbarComponent implements OnInit { + + currentUrl: string; + shared = shared; + + constructor(public location: Location, private auth: AuthService) { + this.currentUrl = this.location.path(); + this.location.onUrlChange(() => { + this.currentUrl = this.location.path(); + }) + } + + ngOnInit(): void { + this.auth.updateUser(); + } + + logOut() { + this.auth.logOut(); + } +} diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.css b/frontend/src/app/_modals/login-modal/login-modal.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_modals/login-modal/login-modal.component.css diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.html b/frontend/src/app/_modals/login-modal/login-modal.component.html new file mode 100644 index 00000000..d694ea58 --- /dev/null +++ b/frontend/src/app/_modals/login-modal/login-modal.component.html @@ -0,0 +1,42 @@ +<!-- Modal --> +<div class="modal fade" id="modalForLogin" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true"> + <div class="modal-dialog modal-dialog-centered"> + <div class="modal-content"> + <div class="modal-header" style="background-color: #003459;"> + <button id="closeButton" type="button" class="btn-close" style="background-color:white;" data-bs-dismiss="modal" aria-label="Close" (click)="resetData()"></button> + </div> + <div class="modal-body px-5" style="color:#003459"> + <h1 class="text-center mt-2 mb-4">Prijavite se</h1> + <form> + <!-- Korisnicko ime --> + <div class="form-outline mb-3"> + <label class="form-label" for="username">Korisničko ime</label> + <input [(ngModel)]="username" name="username" type="text" id="username" + class="form-control form-control" placeholder="Unesite korisničko ime..." /> + </div> + <!-- Lozinka --> + <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" placeholder="Unesite lozinku..." /> + </div> + + <div class="text-center text-lg-start mt-5 pt-2"> + <p *ngIf="wrongCreds" class="small fw-bold mt-2 pt-1 mb-0 text-danger">Lozinka ili e-mail su pogrešni + </p> + </div> + </form> + <div class="col-md-12 d-flex justify-content-center"> + <button type="button" class="btn btn-lg" style="color:white; background-color: #003459; margin-right: 10px;" (click)="doLogin()">Prijavite se</button> + <button type="button" class="btn btn-lg btn-outline-secondary" data-bs-dismiss="modal" (click)="resetData()">Odustanite</button> + </div> + <br> + </div> + <div class="modal-footer justify-content-center"> + <p class="small fw-bold">Još uvek nemate nalog? + <a data-bs-toggle="modal" data-bs-target="#modalForRegister" class="link-danger">Registrujte se</a> + </p> + </div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.spec.ts b/frontend/src/app/_modals/login-modal/login-modal.component.spec.ts new file mode 100644 index 00000000..7d0d526a --- /dev/null +++ b/frontend/src/app/_modals/login-modal/login-modal.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginModalComponent } from './login-modal.component'; + +describe('LoginModalComponent', () => { + let component: LoginModalComponent; + let fixture: ComponentFixture<LoginModalComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LoginModalComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.ts b/frontend/src/app/_modals/login-modal/login-modal.component.ts new file mode 100644 index 00000000..87686f10 --- /dev/null +++ b/frontend/src/app/_modals/login-modal/login-modal.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; +import { CookieService } from 'ngx-cookie-service'; +import { AuthService } from 'src/app/_services/auth.service'; + +@Component({ + selector: 'app-login-modal', + templateUrl: './login-modal.component.html', + styleUrls: ['./login-modal.component.css'] +}) +export class LoginModalComponent implements OnInit { + + username: string = ''; + password: string = ''; + + public wrongCreds: boolean = false; //RAZMOTRITI + + constructor( + private authService: AuthService, + private cookie: CookieService, + private router: Router + ) { } + + ngOnInit(): void { + } + + doLogin() { + this.authService.login(this.username, this.password).subscribe((response) => { //ako nisu ok podaci, ne ide hide nego mora opet da ukucava!!!!podesi + console.log(response); + this.authService.authenticate(response); + (<HTMLSelectElement>document.getElementById('closeButton')).click(); + }, error => { + console.warn(error); //NETACNI PODACI + }); + } + resetData() { + this.username = ''; + this.password = ''; + } +} diff --git a/frontend/src/app/_modals/register-modal/register-modal.component.css b/frontend/src/app/_modals/register-modal/register-modal.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_modals/register-modal/register-modal.component.css diff --git a/frontend/src/app/_modals/register-modal/register-modal.component.html b/frontend/src/app/_modals/register-modal/register-modal.component.html new file mode 100644 index 00000000..7098c040 --- /dev/null +++ b/frontend/src/app/_modals/register-modal/register-modal.component.html @@ -0,0 +1,88 @@ +<!-- Modal --> +<div class="modal fade" id="modalForRegister" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" + aria-labelledby="staticBackdropLabel" aria-hidden="true"> + <div class="modal-dialog modal-dialog-centered modal-dialog modal-lg"> + <div class="modal-content"> + <div class="modal-header" style="background-color: #003459;"> + <button type="button" class="btn-close" data-bs-dismiss="modal" style="background-color: white;" + aria-label="Close" (click)="resetData()"></button> + </div> + <div class="modal-body" style="color:#003459"> + <h1 class="text-center mt-2 mb-4">Registracija</h1> + + <form class="mx-5"> + <!--Ime--> + <div class="row"> + <div class="col-6 px-3 py-3"> + <label class="form-label" for="firstName">Ime</label> + <input type="text" id="firstName" class="form-control" [(ngModel)]="firstName" + name="firstName" placeholder="Unesite ime..."> + <small *ngIf="wrongFirstNameBool" class="form-text text-danger">Unesite ispravno + ime.</small> + </div> + <!--Prezime--> + <div class="col-6 px-3 py-3"> + <label class="form-label" for="lastName">Prezime</label> + <input type="text" id="lastName" class="form-control" [(ngModel)]="lastName" name="lastName" + placeholder="Unesite prezime..." /> + <small *ngIf="wrongLastNameBool" class="form-text text-danger">Unesite ispravno + prezime.</small> + </div> + </div> + <div class="row"> + <!--Korisnicko ime--> + <div class="col-12 px-3 py-3"> + <label class="form-label" for="username-register">Korisničko ime</label> + <input type="text" id="username-register" class="form-control" [(ngModel)]="username" + name="username-register" placeholder="Unesite korisničko ime..." /> + <small *ngIf="wrongUsernameBool" class="form-text text-danger">Unesite ispravno korisničko + ime.</small> + </div> + </div> + <div class="row"> + <!--Email--> + <div class="col-12 px-3 py-3"> + <label class="form-label" for="email">E-mail adresa</label> + <input type="email" id="email" class="form-control" [(ngModel)]="email" name="email" + placeholder="Unesite email adresu..." /> + <small *ngIf="wrongEmailBool" class="form-text text-danger">Unesite ispravno e-mail + adresu.</small> + </div> + </div> + <div class="row"> + <!-- Lozinka 1. --> + <div class="col-6 px-3 py-3"> + <label class="form-label" for="pass1">Lozinka</label> + <input type="password" id="pass1" class="form-control" [(ngModel)]="pass1" name="pass1" + placeholder="Unesite lozinku..." /> + <small *ngIf="wrongPass1Bool" class="form-text text-danger">Lozinka se mora sastojati od + najmanje 6 karaktera.</small> + </div> + <!-- Lozinka 2. --> + <div class="col-6 px-3 py-3"> + <label class="form-label" for="pass2">Potvrdite lozinku</label> + <input type="password" id="pass2" class="form-control" [(ngModel)]="pass2" name="pass2" + placeholder="Ponovite lozinku..." /> + <small *ngIf="wrongPass2Bool" class="form-text text-danger">Lozinke se ne + podudaraju.</small> + </div> + </div> + </form> + <div class="col-md-12 d-flex justify-content-center mt-5"> + <button type="button" class="btn btn-lg" + style="color:white; background-color: #003459; margin-right: 10px;" + (click)="doRegister()">Registrujte se</button> + <button type="button" class="btn btn-lg btn-outline-secondary" style="margin-left: 15px;" + data-bs-dismiss="modal" (click)="resetData()">Odustanite</button> + </div> + <br> + </div> + <div class="modal-footer justify-content-center"> + <p class="small fw-bold">Već imate kreiran nalog? + <a id="linkToLoginModal" data-bs-toggle="modal" data-bs-target="#modalForLogin" + class="link-danger">Prijavite se</a> + </p> + </div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_modals/register-modal/register-modal.component.spec.ts b/frontend/src/app/_modals/register-modal/register-modal.component.spec.ts new file mode 100644 index 00000000..e371b3d8 --- /dev/null +++ b/frontend/src/app/_modals/register-modal/register-modal.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegisterModalComponent } from './register-modal.component'; + +describe('RegisterModalComponent', () => { + let component: RegisterModalComponent; + let fixture: ComponentFixture<RegisterModalComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ RegisterModalComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RegisterModalComponent); + 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/_modals/register-modal/register-modal.component.ts index 712fc55e..c02a4e1a 100644 --- a/frontend/src/app/_pages/register-page/register-page.component.ts +++ b/frontend/src/app/_modals/register-modal/register-modal.component.ts @@ -1,45 +1,58 @@ 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'] + selector: 'app-register-modal', + templateUrl: './register-modal.component.html', + styleUrls: ['./register-modal.component.css'] }) -export class RegisterPageComponent implements OnInit { +export class RegisterModalComponent implements OnInit { + firstName: string = ''; lastName: string = ''; - nickName: string = ''; + username: string = ''; email: string = ''; pass1: string = ''; pass2: string = ''; wrongFirstNameBool: boolean = false; wrongLastNameBool: boolean = false; - wrongNickNameBool: boolean = false; + wrongUsernameBool: boolean = false; wrongEmailBool: boolean = false; wrongPass1Bool: boolean = false; wrongPass2Bool: boolean = false; pattName: RegExp = /^[a-zA-ZšŠđĐčČćĆžŽ]+([ \-][a-zA-ZšŠđĐčČćĆžŽ]+)*$/; + pattUsername: RegExp = /^[a-zA-Z0-9]{6,18}$/; 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, + private authService: AuthService ) { } ngOnInit(): void { } + doRegister() { + this.validation(); + } + resetData() { + this.firstName = this.lastName = this.username = this.email = this.pass1 = this.pass2 = ''; + this.wrongFirstNameBool = this.wrongLastNameBool = this.wrongUsernameBool = this.wrongEmailBool = this.wrongPass1Bool = this.wrongPass2Bool = false; + } + isCorrectName(element: string): boolean { if (this.pattName.test(element) && !(this.pattTwoSpaces.test(element)) && (element.length >= 1 && element.length <= 30)) return true; return false; } + isCorrectUsername(element: string): boolean { + if (this.pattUsername.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; @@ -67,13 +80,13 @@ export class RegisterPageComponent implements OnInit { (<HTMLSelectElement>document.getElementById('lastName')).focus(); this.wrongLastNameBool = true; } - nickNameValidation() { - if (this.isCorrectName(this.nickName) == true) { - this.wrongNickNameBool = false; + usernameValidation() { + if (this.isCorrectUsername(this.username) == true) { + this.wrongUsernameBool = false; return; } - (<HTMLSelectElement>document.getElementById('nickName')).focus(); - this.wrongNickNameBool = true; + (<HTMLSelectElement>document.getElementById('username-register')).focus(); + this.wrongUsernameBool = true; } emailValidation() { if (this.isCorrectEmail(this.email) == true) { @@ -99,22 +112,22 @@ export class RegisterPageComponent implements OnInit { validation() { this.firstName = this.firstName.trim(); this.lastName = this.lastName.trim(); - this.nickName = this.nickName.trim(); + this.username = this.username.trim(); this.email = this.email.trim(); this.firstNameValidation(); this.lastNameValidation(); - //this.nickNameValidation(); + this.usernameValidation(); this.emailValidation(); this.passwordValidation(); - if (!(this.wrongFirstNameBool || this.wrongLastNameBool || this.wrongNickNameBool || + if (!(this.wrongFirstNameBool || this.wrongLastNameBool || this.wrongUsernameBool || this.wrongEmailBool || this.wrongPass1Bool || this.wrongPass2Bool)) { //sve ok, registruj ga let user = { firstName: this.firstName, lastName: this.lastName, - username: this.nickName, + username: this.username, password: this.pass1, email: this.email } @@ -123,18 +136,22 @@ export class RegisterPageComponent implements OnInit { .subscribe( (response) => { console.log(response); - if (response === 'User added') - this.router.navigate(['/login']); //registracija uspesna, idi na login - else if (response === 'Email Already Exists') + if (response === 'User added') { + this.resetData(); + (<HTMLSelectElement>document.getElementById('linkToLoginModal')).click(); + } + else if (response === 'Email Already Exists') { alert('Nalog sa unetim email-om već postoji!'); - else if (response === 'Username Already Exists') - alert('Nalog sa unetim korisnićkim imenom već postoji!'); + (<HTMLSelectElement>document.getElementById('email')).focus(); + } + else if (response === 'Username Already Exists') { + alert('Nalog sa unetim korisničkim imenom već postoji!'); + (<HTMLSelectElement>document.getElementById('username-register')).focus(); + } } ); } } - - } diff --git a/frontend/src/app/_pages/add-model/add-model.component.css b/frontend/src/app/_pages/add-model/add-model.component.css index e69de29b..004b9cac 100644 --- a/frontend/src/app/_pages/add-model/add-model.component.css +++ b/frontend/src/app/_pages/add-model/add-model.component.css @@ -0,0 +1,17 @@ +#header { + background-color: #003459; + padding-top: 25px; + padding-bottom: 20px; +} +#header h1 { + font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; + text-align: center; + color: white; +} + +#wrapper { + background-image: url('/assets/images/add_model_background.jpg'); +} +#container { + border-radius: 8px; +}
\ No newline at end of file diff --git a/frontend/src/app/_pages/add-model/add-model.component.html b/frontend/src/app/_pages/add-model/add-model.component.html index bc292bb9..19d69148 100644 --- a/frontend/src/app/_pages/add-model/add-model.component.html +++ b/frontend/src/app/_pages/add-model/add-model.component.html @@ -1,189 +1,146 @@ -<div class="container p-3" style="background-color: rgb(249, 251, 253); min-height: 100%;"> - - <h2 class="my-4 text-primary"> Nov model: </h2> - <div class="form-group row align-items-center"> - <label for="name" class="col-sm-2 col-form-label col-form-label-lg">Naziv</label> - <div class="col-sm-7"> - <input type="text" class="form-control form-control-lg" name="name" placeholder="Naziv..." - [(ngModel)]="newModel.name"> - </div> - - <div class="col-sm-3"> - <input type="text" class="form-control-plaintext text-center" id="dateCreated" placeholder="--/--/--" - value="{{newModel.dateCreated | date: 'dd/MM/yyyy'}}" readonly> - </div> - </div> - - <div class="form-group row my-2"> - <label for="desc" class="col-sm-2 col-form-label">Opis</label> - <div class="col-sm-10"> - <textarea class="form-control" name="desc" rows="3" [(ngModel)]="newModel.description"></textarea> - </div> - </div> - - <!--<div class="form-group row"> - <label for="value" class="col-4">Vrednost</label> - <div class="input-group"> - <input type="number" min="0" class="form-control" name="value" placeholder="Vrednost..." - [(ngModel)]="newModel.value"> - <div class="input-group-prepend"> - <span class="input-group-text">#</span> - </div> - <input type="number" min="1" class="form-control" name="count" placeholder="Br." [(ngModel)]="newModel.count"> - <input type="text" class="form-control" name="sum" placeholder="Suma" - value="=({{newModel.value * newModel.count}})" readonly> - </div> - </div>--> - - <div class="my-4"> - <label for="dataset">Izvor podataka:</label> - <app-dataset-load id="dataset"></app-dataset-load> - </div> - - <div class="form-group row my-2"> - <div class="col-sm-2 col-form-label"> - <label for="type" class="form-check-label">Podela test skupa: - <input class="mx-3 form-check-input" type="checkbox" [checked]="newModel.randomTestSet" - (change)="newModel.randomTestSet = !newModel.randomTestSet"> - </label> - - </div> - <div> - <input type="range" min="0.1" max="0.9" step="0.1" class="form-control" name="randomTestSetDistribution" - [disabled]="!newModel.randomTestSet" [(ngModel)]="newModel.randomTestSetDistribution"> - </div> - </div> - - <h3> Parametri treniranja </h3> - - <div class="form-group row my-2"> - <label for="type" class="col-sm-2 col-form-label">Tip mreže: </label> - <div class="col-sm-10"> - <select id=typeOptions class="form-control" name="type" [(ngModel)]="newModel.type"> - <option *ngFor="let option of Object.keys(ANNType); let optionName of Object.values(ANNType)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - </div> - - <div class="form-group row my-2"> - <label for="encoding" class="col-sm-2 col-form-label">Enkoding: </label> - <div class="col-sm-10"> - <select id=encodingOptions class="form-control" name="encoding" [(ngModel)]="newModel.encoding"> - <option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" - [value]="option"> - {{ optionName }} +<div id="header"> + <h1>Napravite svoj model veštačke neuronske mreže</h1> +</div> + +<div id="wrapper"> + + <div id="container" class="container p-5" style="background-color: white; min-height: 100%;"> + + <div class="form-group row mb-4 d-flex"> <!--justify-content-center--> + <h2 class="col-sm-2" style="color: #00171f"> Nov model: </h2> + <div class="col-sm-3"> + <div class="mb-4"> + <label for="name" class="col-form-label">Naziv modela:</label> + <input type="text" class="form-control" name="name" placeholder="Naziv..." + [(ngModel)]="newModel.name"> + </div> + <div class="d-inline-flex align-items-center"> + <label for="dateCreated" class="col-form-label">Datum:</label> + <input type="text" class="form-control-plaintext" id="dateCreated" placeholder="--/--/--" + value="{{newModel.dateCreated | date: 'dd/MM/yyyy'}}" readonly> + </div> + </div> + <div class="col-sm-5"> + <label for="desc" class="col-sm-2 col-form-label">Opis:</label> + <div> + <textarea class="form-control" name="desc" rows="3" [(ngModel)]="newModel.description"></textarea> + </div> + </div> + </div> + + <div class="my-5 justify-content-center"> + <h2>Izvor podataka:</h2> + <app-dataset-load id="dataset"></app-dataset-load> + </div> + + <h2>Parametri treniranja:</h2> + + <div class="row"> + <div class="col-2 mt-4"> + <label for="type" class="col-form-label">Tip mreže: </label> + <select id=typeOptions class="form-control" name="type" [(ngModel)]="newModel.type"> + <option *ngFor="let option of Object.keys(ANNType); let optionName of Object.values(ANNType)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + <div class="col-1 mt-4"> + <label for="encoding" class="col-form-label">Enkoding: </label> + <select id=encodingOptions class="form-control" name="encoding" [(ngModel)]="newModel.encoding"> + <option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + <div class="col-1 mt-4"> + <label for="optimizer" class="col-form-label">Optimizacija: </label> + <select id=optimizerOptions class="form-control" name="optimizer" [(ngModel)]="newModel.optimizer"> + <option *ngFor="let option of Object.keys(Optimizer); let optionName of Object.values(Optimizer)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + <div class="col mt-4"> + <label for="lossFunction" class="col-form-label">Funkcija obrade gubitka: </label> + <select id=lossFunctionOptions class="form-control" name="lossFunction" [(ngModel)]="newModel.lossFunction"> + <option *ngFor="let option of Object.keys(LossFunction); let optionName of Object.values(LossFunction)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + <div class="col-2"> + <label for="inputLayerActivationFunction" class="col-form-label">Funkcija aktivacije<br>ulaznog sloja:</label> + <select id=inputLayerActivationFunctionOptions class="form-control" name="inputLayerActivationFunction" + [(ngModel)]="newModel.inputLayerActivationFunction"> + <option + *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + <div class="col"> + <label for="hiddenLayerActivationFunction" class="col-form-label">Funkcija aktivacije<br>skrivenih slojeva:</label> + <select id=hiddenLayerActivationFunctionOptions class="form-control" name="hiddenLayerActivationFunction" + [(ngModel)]="newModel.hiddenLayerActivationFunction"> + <option + *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" + [value]="option"> + {{ optionName }} </option> - </select> - </div> - </div> - - <div class="form-group row my-2"> - <label for="optimizer" class="col-sm-2 col-form-label">Optimizacija: </label> - <div class="col-sm-10"> - <select id=optimizerOptions class="form-control" name="optimizer" [(ngModel)]="newModel.optimizer"> - <option *ngFor="let option of Object.keys(Optimizer); let optionName of Object.values(Optimizer)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - </div> - - <div class="form-group row my-2"> - <label for="lossFunction" class="col-sm-2 col-form-label">Funkcija obrade gubitka: </label> - <div class="col-sm-10"> - <select id=lossFunctionOptions class="form-control" name="lossFunction" [(ngModel)]="newModel.lossFunction"> - <option *ngFor="let option of Object.keys(LossFunction); let optionName of Object.values(LossFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - </div> - - <div class="form-group row my-2"> - <label for="inputNeurons" class="col-sm-2 col-form-label">Broj ulaznih neurona: </label> - <div class="col-sm-10"> - <input type="number" min="1" class="form-control" name="inputNeurons" [(ngModel)]="newModel.inputNeurons"> - </div> - </div> - - <div class="form-group row my-2"> - <label for="inputLayerActivationFunction" class="col-sm-2 col-form-label">Funkcija aktivacije ulaznog sloja: - </label> - <div class="col-sm-10"> - <select id=inputLayerActivationFunctionOptions class="form-control" name="inputLayerActivationFunction" - [(ngModel)]="newModel.inputLayerActivationFunction"> - <option - *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - </div> - - <div class="form-group row my-2"> - <label for="hiddenLayerNeurons" class="col-sm-2 col-form-label">Broj neurona skrivenih slojeva: </label> - <div class="col-sm-10"> - <input type="number" min="1" class="form-control" name="hiddenLayerNeurons" - [(ngModel)]="newModel.hiddenLayerNeurons"> - </div> - </div> - - <div class="form-group row my-2"> - <label for="hiddenLayerActivationFunction" class="col-sm-2 col-form-label">Funkcija aktivacije skrivenih - slojeva: - </label> - <div class="col-sm-10"> - <select id=hiddenLayerActivationFunctionOptions class="form-control" name="hiddenLayerActivationFunction" - [(ngModel)]="newModel.hiddenLayerActivationFunction"> - <option - *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - </div> - - <div class="form-group row my-2"> - <label for="hiddenLayers" class="col-sm-2 col-form-label">Broj skrivenih slojeva: </label> - <div class="col-sm-10"> - <input type="number" min="1" class="form-control" name="hiddenLayers" [(ngModel)]="newModel.hiddenLayers"> - </div> - </div> - - <div class="form-group row my-2"> - <label for="outputLayerActivationFunction" class="col-sm-2 col-form-label">Funkcija aktivacije izlaznog - sloja: - </label> - <div class="col-sm-10"> - <select id=outputLayerActivationFunctionOptions class="form-control" name="outputLayerActivationFunction" - [(ngModel)]="newModel.outputLayerActivationFunction"> - <option - *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - </div> - - <div class="form-group row my-2"> - <label for="batchSize" class="col-sm-2 col-form-label">Broj uzorka po iteraciji: </label> - <div class="col-sm-10"> - <input type="number" min="1" class="form-control" name="batchSize" [(ngModel)]="newModel.batchSize"> - </div> - </div> - - <div class=" form-group row my-4"> - <div class="col-4"></div> - <button class="btn btn-lg btn-primary col-4" (click)="addModel();">Dodaj</button> - <div class="col-4"></div> - </div> - -</div>
\ No newline at end of file + </select> + </div> + <div class="col"> + <label for="outputLayerActivationFunction" class="col-form-label">Funkcija aktivacije<br>izlaznog sloja:</label> + <select id=outputLayerActivationFunctionOptions class="form-control" name="outputLayerActivationFunction" + [(ngModel)]="newModel.outputLayerActivationFunction"> + <option + *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + </div> + + <div class="row mt-5"> + <div class="col-2"> + <label for="inputNeurons" class="col-form-label">Broj ulaznih neurona: </label> + <input type="number" min="1" class="form-control" name="inputNeurons" [(ngModel)]="newModel.inputNeurons"> + </div> + <div class="col-2"> + <label for="hiddenLayers" class="col-form-label">Broj skrivenih slojeva: </label> + <input type="number" min="1" class="form-control" name="hiddenLayers" [(ngModel)]="newModel.hiddenLayers"> + </div> + <div class="col-3 "> + <label for="hiddenLayerNeurons" class="col-form-label">Broj neurona skrivenih slojeva: </label> + <input type="number" min="1" class="form-control" name="hiddenLayerNeurons" [(ngModel)]="newModel.hiddenLayerNeurons"> + </div> + <div class="col-2"> + <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji: </label> + <input type="number" min="1" class="form-control" name="batchSize" [(ngModel)]="newModel.batchSize"> + </div> + <div class="col-3 mt-1"> + <label for="type" class="form-check-label mb-3"> Podela test skupa: + <input class="mx-3 form-check-input" type="checkbox" [checked]="newModel.randomTestSet" + (change)="newModel.randomTestSet = !newModel.randomTestSet"> + </label> + <mat-slider min="0.1" max="0.9" step="0.1" value="0.2" name="randomTestSetDistribution" thumbLabel + [disabled]="!newModel.randomTestSet" [(ngModel)]="newModel.randomTestSetDistribution"> + </mat-slider> + </div> + </div> + + <br><br> + <div class="form-group row mt-5"> + <div class="col-4"></div> + <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="addModel();">Napravi model</button> + <div class="col-4"></div> + </div> + + </div> +</div> diff --git a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.css b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.css diff --git a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.html b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.html new file mode 100644 index 00000000..fa38a1bc --- /dev/null +++ b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.html @@ -0,0 +1 @@ +<p>browse-datasets works!</p> diff --git a/frontend/src/app/_pages/only-authorized/only-authorized.component.spec.ts b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.spec.ts index 82a01f63..fda74dbe 100644 --- a/frontend/src/app/_pages/only-authorized/only-authorized.component.spec.ts +++ b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { OnlyAuthorizedComponent } from './only-authorized.component'; +import { BrowseDatasetsComponent } from './browse-datasets.component'; -describe('OnlyAuthorizedComponent', () => { - let component: OnlyAuthorizedComponent; - let fixture: ComponentFixture<OnlyAuthorizedComponent>; +describe('BrowseDatasetsComponent', () => { + let component: BrowseDatasetsComponent; + let fixture: ComponentFixture<BrowseDatasetsComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ OnlyAuthorizedComponent ] + declarations: [ BrowseDatasetsComponent ] }) .compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(OnlyAuthorizedComponent); + fixture = TestBed.createComponent(BrowseDatasetsComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.ts b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.ts new file mode 100644 index 00000000..dba6c25e --- /dev/null +++ b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-browse-datasets', + templateUrl: './browse-datasets.component.html', + styleUrls: ['./browse-datasets.component.css'] +}) +export class BrowseDatasetsComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.css b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.css diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html new file mode 100644 index 00000000..01c4af82 --- /dev/null +++ b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html @@ -0,0 +1 @@ +<p>browse-predictors works!</p> diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.spec.ts b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.spec.ts new file mode 100644 index 00000000..6d13fedf --- /dev/null +++ b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BrowsePredictorsComponent } from './browse-predictors.component'; + +describe('BrowsePredictorsComponent', () => { + let component: BrowsePredictorsComponent; + let fixture: ComponentFixture<BrowsePredictorsComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BrowsePredictorsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BrowsePredictorsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts new file mode 100644 index 00000000..b4fb2a9d --- /dev/null +++ b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-browse-predictors', + templateUrl: './browse-predictors.component.html', + styleUrls: ['./browse-predictors.component.css'] +}) +export class BrowsePredictorsComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_pages/home/home.component.css b/frontend/src/app/_pages/home/home.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/home/home.component.css diff --git a/frontend/src/app/_pages/home/home.component.html b/frontend/src/app/_pages/home/home.component.html new file mode 100644 index 00000000..374cb324 --- /dev/null +++ b/frontend/src/app/_pages/home/home.component.html @@ -0,0 +1,53 @@ +<div class="d-flex flex-column align-items-center"> + <img src="../../../assets/svg/logo.svg" class="bi me-2" width="256" height="256" role="img"> + <div *ngIf="shared.loggedIn" class="d-flex flex-column align-items-center"> + <h2 class="my-4">Započnite sa treniranjem!</h2> + <div id="cards" class="row align-items-center justify-content-center"> + <div class="card shadow col-3 m-1" style="width: 18rem;"> + <div class="card-body"> + <mat-icon width="48px" height="48px" + style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">storage</mat-icon> + <h3 class="card-title my-2">Moji izvori podataka</h3> + <p class="card-text"> + <a class="stretched-link" routerLink="my-datasets">Preuredite</a> vaše izvore + podataka, ili + dodajte novi. + </p> + </div> + </div> + <div class="card shadow col-3 m-1" style="width: 18rem;"> + <div class="card-body"> + <mat-icon width="48px" height="48px" + style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">model_training + </mat-icon> + <h3 class="card-title my-2">Moji modeli</h3> + <p class="card-text"> + <a class="stretched-link" routerLink="my-models">Pregledajte</a> vaše modele, menjajte ih, + napravite nove modele, ili + ih obrišite. + </p> + </div> + </div> + <div class="card shadow col-3 m-1" style="width: 18rem;"> + <div class="card-body"> + <mat-icon width="48px" height="48px" + style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">batch_prediction + </mat-icon> + <h3 class="card-title my-2">Rezultati treniranja</h3> + <p class="card-text"> + <a class="stretched-link" routerLink="my-predictors">Pregledajte</a> sve vaše trenirane + modele, + koristite ih da predvidite vrednosti za red ili skup podataka, ili ih obrišite. + </p> + </div> + </div> + </div> + + </div> + <h2 class="my-4">Pogledajte javne izvore podataka!</h2> + <app-carousel [items]="publicDatasets"> + </app-carousel> + <h2 class="my-4">Iskoristite već trenirane modele!</h2> + <app-carousel [items]="publicPredictors"> + </app-carousel> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_pages/home/home.component.spec.ts b/frontend/src/app/_pages/home/home.component.spec.ts new file mode 100644 index 00000000..2c5a1726 --- /dev/null +++ b/frontend/src/app/_pages/home/home.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; + +describe('HomeComponent', () => { + let component: HomeComponent; + let fixture: ComponentFixture<HomeComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ HomeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/home/home.component.ts b/frontend/src/app/_pages/home/home.component.ts new file mode 100644 index 00000000..7e4471e8 --- /dev/null +++ b/frontend/src/app/_pages/home/home.component.ts @@ -0,0 +1,45 @@ +import { Component, OnInit } from '@angular/core'; +import Dataset from 'src/app/_data/Dataset'; +import Predictor from 'src/app/_data/Predictor'; +import { ItemDatasetComponent } from 'src/app/_elements/item-dataset/item-dataset.component'; +import shared from 'src/app/Shared'; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.css'] +}) +export class HomeComponent implements OnInit { + + publicDatasets: Dataset[]; + publicPredictors: Predictor[]; + + shared = shared; + + constructor() { + this.publicDatasets = [ + new Dataset('Titanik', 'Titanik', ['Kolona1', 'Kolona2', 'Ime', 'OsobaJePreživela']), + new Dataset('Drugi Dataset', 'Lorem ipsum dolor sir amet', ['jabuka', 'kruska', 'jagoda']), + new Dataset('Dataset III', 'Kratak opis izvora podataka', ['c1', 'c2', 'c3', 'c4', 'c5']), + new Dataset('Drugi Dataset', 'Lorem ipsum dolor sir amet', ['jabuka', 'kruska', 'jagoda']), + new Dataset('Dataset III', 'Kratak opis izvora podataka', ['c1', 'c2', 'c3', 'c4', 'c5']), + new Dataset('Drugi Dataset', 'Lorem ipsum dolor sir amet', ['jabuka', 'kruska', 'jagoda']), + new Dataset('Dataset III', 'Kratak opis izvora podataka', ['c1', 'c2', 'c3', 'c4', 'c5']), + new Dataset('Dataset III', 'Kratak opis izvora podataka', ['c1', 'c2', 'c3', 'c4', 'c5']) + ] + this.publicPredictors = [ + new Predictor('Preživeli', 'Za uneto ime osobe, predvidja da li je ta osoba preživela ili ne.', ['Ime'], 'OsobaJePreživela'), + new Predictor('Drugi model', 'Lorem ipsum dolor sir amet', ['kruska'], 'jagoda'), + new Predictor('Treći model', 'Kratak opis modela', ['c1', 'c2', 'c3'], 'c5'), + new Predictor('Drugi model', 'Lorem ipsum dolor sir amet', ['kruska'], 'jagoda'), + new Predictor('Treći model', 'Kratak opis modela', ['c1', 'c2', 'c3'], 'c5'), + new Predictor('Drugi model', 'Lorem ipsum dolor sir amet', ['kruska'], 'jagoda'), + new Predictor('Treći model', 'Kratak opis modela', ['c1', 'c2', 'c3'], 'c5'), + new Predictor('Treći model', 'Kratak opis modela', ['c1', 'c2', 'c3'], 'c5') + ] + } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_pages/login-page/login-page.component.html b/frontend/src/app/_pages/login-page/login-page.component.html deleted file mode 100644 index 76a4ee7b..00000000 --- a/frontend/src/app/_pages/login-page/login-page.component.html +++ /dev/null @@ -1,44 +0,0 @@ -<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> - <!-- Username input --> - <!-- Username input --> - <div class="form-outline mb-4"> - <label class="form-label" for="username">Korisničko ime</label> - <input [(ngModel)]="username" name="username" type="text" id="username" - class="form-control form-control-lg" placeholder="Unesite korisničko ime..." /> - </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.ts b/frontend/src/app/_pages/login-page/login-page.component.ts deleted file mode 100644 index 0825d435..00000000 --- a/frontend/src/app/_pages/login-page/login-page.component.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -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 = ''; - username: 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, - private router: Router - ) { } - - ngOnInit(): void { - } - - onSubmit() { - /*if (!this.pattEmail.test(this.email)) { - console.warn('Bad email!'); - return; - } - else {*/ - this.authService.login(this.username, this.password).subscribe((response) => { - console.log(response); - this.cookie.set('token', response); - this.router.navigate(['add-model']); - // }) - }); - } - -} diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.css b/frontend/src/app/_pages/my-datasets/my-datasets.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/my-datasets/my-datasets.component.css diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.html b/frontend/src/app/_pages/my-datasets/my-datasets.component.html new file mode 100644 index 00000000..4b3095be --- /dev/null +++ b/frontend/src/app/_pages/my-datasets/my-datasets.component.html @@ -0,0 +1 @@ +<p>my-datasets works!</p> diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.spec.ts b/frontend/src/app/_pages/my-datasets/my-datasets.component.spec.ts new file mode 100644 index 00000000..fc1fc3f3 --- /dev/null +++ b/frontend/src/app/_pages/my-datasets/my-datasets.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyDatasetsComponent } from './my-datasets.component'; + +describe('MyDatasetsComponent', () => { + let component: MyDatasetsComponent; + let fixture: ComponentFixture<MyDatasetsComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MyDatasetsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MyDatasetsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.ts b/frontend/src/app/_pages/my-datasets/my-datasets.component.ts new file mode 100644 index 00000000..af5eab97 --- /dev/null +++ b/frontend/src/app/_pages/my-datasets/my-datasets.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-my-datasets', + templateUrl: './my-datasets.component.html', + styleUrls: ['./my-datasets.component.css'] +}) +export class MyDatasetsComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_pages/my-models/my-models.component.css b/frontend/src/app/_pages/my-models/my-models.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/my-models/my-models.component.css diff --git a/frontend/src/app/_pages/my-models/my-models.component.html b/frontend/src/app/_pages/my-models/my-models.component.html new file mode 100644 index 00000000..2fea257d --- /dev/null +++ b/frontend/src/app/_pages/my-models/my-models.component.html @@ -0,0 +1 @@ +<p>my-models works!</p> diff --git a/frontend/src/app/_pages/my-models/my-models.component.spec.ts b/frontend/src/app/_pages/my-models/my-models.component.spec.ts new file mode 100644 index 00000000..e431d04c --- /dev/null +++ b/frontend/src/app/_pages/my-models/my-models.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyModelsComponent } from './my-models.component'; + +describe('MyModelsComponent', () => { + let component: MyModelsComponent; + let fixture: ComponentFixture<MyModelsComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MyModelsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MyModelsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/my-models/my-models.component.ts b/frontend/src/app/_pages/my-models/my-models.component.ts new file mode 100644 index 00000000..e9bc52de --- /dev/null +++ b/frontend/src/app/_pages/my-models/my-models.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-my-models', + templateUrl: './my-models.component.html', + styleUrls: ['./my-models.component.css'] +}) +export class MyModelsComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_pages/my-predictors/my-predictors.component.css b/frontend/src/app/_pages/my-predictors/my-predictors.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.css diff --git a/frontend/src/app/_pages/my-predictors/my-predictors.component.html b/frontend/src/app/_pages/my-predictors/my-predictors.component.html new file mode 100644 index 00000000..32d085af --- /dev/null +++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.html @@ -0,0 +1 @@ +<p>my-predictors works!</p> diff --git a/frontend/src/app/_pages/register-page/register-page.component.spec.ts b/frontend/src/app/_pages/my-predictors/my-predictors.component.spec.ts index 347fe9f4..37dddf6d 100644 --- a/frontend/src/app/_pages/register-page/register-page.component.spec.ts +++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RegisterPageComponent } from './register-page.component'; +import { MyPredictorsComponent } from './my-predictors.component'; -describe('RegisterPageComponent', () => { - let component: RegisterPageComponent; - let fixture: ComponentFixture<RegisterPageComponent>; +describe('MyPredictorsComponent', () => { + let component: MyPredictorsComponent; + let fixture: ComponentFixture<MyPredictorsComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ RegisterPageComponent ] + declarations: [ MyPredictorsComponent ] }) .compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(RegisterPageComponent); + fixture = TestBed.createComponent(MyPredictorsComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/frontend/src/app/_pages/my-predictors/my-predictors.component.ts b/frontend/src/app/_pages/my-predictors/my-predictors.component.ts new file mode 100644 index 00000000..b0d6e9dd --- /dev/null +++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-my-predictors', + templateUrl: './my-predictors.component.html', + styleUrls: ['./my-predictors.component.css'] +}) +export class MyPredictorsComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_pages/only-authorized/only-authorized.component.html b/frontend/src/app/_pages/only-authorized/only-authorized.component.html deleted file mode 100644 index 27bbcf09..00000000 --- a/frontend/src/app/_pages/only-authorized/only-authorized.component.html +++ /dev/null @@ -1 +0,0 @@ -<p>only-authorized works!</p> diff --git a/frontend/src/app/_pages/only-authorized/only-authorized.component.ts b/frontend/src/app/_pages/only-authorized/only-authorized.component.ts deleted file mode 100644 index be88365f..00000000 --- a/frontend/src/app/_pages/only-authorized/only-authorized.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -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/predict/predict.component.css b/frontend/src/app/_pages/predict/predict.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/predict/predict.component.css diff --git a/frontend/src/app/_pages/predict/predict.component.html b/frontend/src/app/_pages/predict/predict.component.html new file mode 100644 index 00000000..74a83b71 --- /dev/null +++ b/frontend/src/app/_pages/predict/predict.component.html @@ -0,0 +1 @@ +<p>predict works!</p> diff --git a/frontend/src/app/_pages/predict/predict.component.spec.ts b/frontend/src/app/_pages/predict/predict.component.spec.ts new file mode 100644 index 00000000..65871ecc --- /dev/null +++ b/frontend/src/app/_pages/predict/predict.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PredictComponent } from './predict.component'; + +describe('PredictComponent', () => { + let component: PredictComponent; + let fixture: ComponentFixture<PredictComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PredictComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PredictComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/predict/predict.component.ts b/frontend/src/app/_pages/predict/predict.component.ts new file mode 100644 index 00000000..0e313c65 --- /dev/null +++ b/frontend/src/app/_pages/predict/predict.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-predict', + templateUrl: './predict.component.html', + styleUrls: ['./predict.component.css'] +}) +export class PredictComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_pages/profile/profile.component.css b/frontend/src/app/_pages/profile/profile.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/profile/profile.component.css diff --git a/frontend/src/app/_pages/profile/profile.component.html b/frontend/src/app/_pages/profile/profile.component.html new file mode 100644 index 00000000..9df0576d --- /dev/null +++ b/frontend/src/app/_pages/profile/profile.component.html @@ -0,0 +1 @@ +<p>profile works!</p> diff --git a/frontend/src/app/_pages/profile/profile.component.spec.ts b/frontend/src/app/_pages/profile/profile.component.spec.ts new file mode 100644 index 00000000..e88012e7 --- /dev/null +++ b/frontend/src/app/_pages/profile/profile.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProfileComponent } from './profile.component'; + +describe('ProfileComponent', () => { + let component: ProfileComponent; + let fixture: ComponentFixture<ProfileComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ProfileComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ProfileComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/profile/profile.component.ts b/frontend/src/app/_pages/profile/profile.component.ts new file mode 100644 index 00000000..93040f3e --- /dev/null +++ b/frontend/src/app/_pages/profile/profile.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-profile', + templateUrl: './profile.component.html', + styleUrls: ['./profile.component.css'] +}) +export class ProfileComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_pages/register-page/register-page.component.html b/frontend/src/app/_pages/register-page/register-page.component.html deleted file mode 100644 index f8ae046e..00000000 --- a/frontend/src/app/_pages/register-page/register-page.component.html +++ /dev/null @@ -1,80 +0,0 @@ -<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/settings/settings.component.css b/frontend/src/app/_pages/settings/settings.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/settings/settings.component.css diff --git a/frontend/src/app/_pages/settings/settings.component.html b/frontend/src/app/_pages/settings/settings.component.html new file mode 100644 index 00000000..4ab2a415 --- /dev/null +++ b/frontend/src/app/_pages/settings/settings.component.html @@ -0,0 +1 @@ +<p>settings works!</p> diff --git a/frontend/src/app/_pages/settings/settings.component.spec.ts b/frontend/src/app/_pages/settings/settings.component.spec.ts new file mode 100644 index 00000000..a3a508b0 --- /dev/null +++ b/frontend/src/app/_pages/settings/settings.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SettingsComponent } from './settings.component'; + +describe('SettingsComponent', () => { + let component: SettingsComponent; + let fixture: ComponentFixture<SettingsComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SettingsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/settings/settings.component.ts b/frontend/src/app/_pages/settings/settings.component.ts new file mode 100644 index 00000000..19862fb0 --- /dev/null +++ b/frontend/src/app/_pages/settings/settings.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-settings', + templateUrl: './settings.component.html', + styleUrls: ['./settings.component.css'] +}) +export class SettingsComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_services/auth.service.ts b/frontend/src/app/_services/auth.service.ts index c96c2dae..afc1567b 100644 --- a/frontend/src/app/_services/auth.service.ts +++ b/frontend/src/app/_services/auth.service.ts @@ -3,6 +3,7 @@ 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'; +import shared from '../Shared'; const jwtHelper = new JwtHelperService(); @@ -11,6 +12,8 @@ const jwtHelper = new JwtHelperService(); }) export class AuthService { + shared = shared; + constructor(private http: HttpClient, private cookie: CookieService) { } login(username: string, password: string) { @@ -28,4 +31,49 @@ export class AuthService { } return false; } + + lastToken?: string; + refresher: any; + + enableAutoRefresh() { + this.lastToken = this.cookie.get('token'); + let exp = jwtHelper.getTokenExpirationDate(this.lastToken); + if (!exp) { + exp = new Date(); + } + this.refresher = setTimeout(() => { + console.log('refreshing token!'); + this.http.post(`${API_SETTINGS.apiURL}/auth/renewJwt`, {}, { headers: this.authHeader(), responseType: 'text' }).subscribe((response) => { + this.authenticate(response); + }); + }, exp.getTime() - new Date().getTime()); + } + + authenticate(token: string) { + let exp = jwtHelper.getTokenExpirationDate(token); + if (!exp) { + exp = new Date(); + } + this.cookie.set('token', token, exp); + this.updateUser(); + } + + updateUser() { + if (this.cookie.check('token')) { + const token = this.cookie.get('token'); + this.shared.loggedIn = this.isAuthenticated(); + this.enableAutoRefresh(); + } + } + + logOut() { + this.cookie.delete('token'); + if (this.refresher) + clearTimeout(this.refresher); + this.shared.loggedIn = false; + } + + authHeader() { + return new HttpHeaders().set("Authorization", "Bearer " + this.cookie.get('token')); + } } diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index c592d57b..ee43b522 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -2,17 +2,28 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AuthGuardService } from './_services/auth-guard.service'; -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 { AddModelComponent } from './_pages/add-model/add-model.component'; +import { HomeComponent } from './_pages/home/home.component'; +import { MyDatasetsComponent } from './_pages/my-datasets/my-datasets.component'; +import { MyModelsComponent } from './_pages/my-models/my-models.component'; +import { MyPredictorsComponent } from './_pages/my-predictors/my-predictors.component'; +import { BrowsePredictorsComponent } from './_pages/browse-predictors/browse-predictors.component'; +import { BrowseDatasetsComponent } from './_pages/browse-datasets/browse-datasets.component'; +import { SettingsComponent } from './_pages/settings/settings.component'; +import { ProfileComponent } from './_pages/profile/profile.component'; +import { PredictComponent } from './_pages/predict/predict.component'; const routes: Routes = [ - { path: '', redirectTo: '/login', pathMatch: 'full' }, - { path: 'login', component: LoginPageComponent }, - { path: 'register', component: RegisterPageComponent }, - { path: 'only-authorized', component: OnlyAuthorizedComponent, canActivate: [AuthGuardService] }, - { path: 'add-model', component: AddModelComponent } + { path: '', component: HomeComponent }, + { path: 'add-model', component: AddModelComponent }, + { path: 'my-datasets', component: MyDatasetsComponent, canActivate: [AuthGuardService] }, + { path: 'my-models', component: MyModelsComponent, canActivate: [AuthGuardService] }, + { path: 'my-predictors', component: MyPredictorsComponent, canActivate: [AuthGuardService] }, + { path: 'settings', component: SettingsComponent, canActivate: [AuthGuardService] }, + { path: 'profile', component: ProfileComponent, canActivate: [AuthGuardService] }, + { path: 'browse-datasets', component: BrowseDatasetsComponent }, + { path: 'browse-predictors', component: BrowsePredictorsComponent }, + { path: 'predict', component: PredictComponent } ]; @NgModule({ diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index 90c6b646..7f2d4225 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1 +1,4 @@ -<router-outlet></router-outlet>
\ No newline at end of file +<app-navbar></app-navbar> +<div class="mat-app-background container h-100"> + <router-outlet></router-outlet> +</div>
\ No newline at end of file diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index aa5d018d..5aa405fb 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -3,26 +3,53 @@ import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppRoutingModule } from './app-routing.module'; import { HttpClientModule } from '@angular/common/http'; +import { MatSliderModule } from '@angular/material/slider'; +import { MatIconModule } from '@angular/material/icon'; 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'; import { DatasetLoadComponent } from './_elements/dataset-load/dataset-load.component'; import { AddModelComponent } from './_pages/add-model/add-model.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { LoginModalComponent } from './_modals/login-modal/login-modal.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RegisterModalComponent } from './_modals/register-modal/register-modal.component'; import { MaterialModule } from './material.module'; +import { HomeComponent } from './_pages/home/home.component'; +import { NavbarComponent } from './_elements/navbar/navbar.component'; +import { ItemPredictorComponent } from './_elements/item-predictor/item-predictor.component'; +import { ItemDatasetComponent } from './_elements/item-dataset/item-dataset.component'; +import { CarouselComponent } from './_elements/carousel/carousel.component'; +import { SettingsComponent } from './_pages/settings/settings.component'; +import { ProfileComponent } from './_pages/profile/profile.component'; +import { MyPredictorsComponent } from './_pages/my-predictors/my-predictors.component'; +import { MyDatasetsComponent } from './_pages/my-datasets/my-datasets.component'; +import { MyModelsComponent } from './_pages/my-models/my-models.component'; +import { BrowseDatasetsComponent } from './_pages/browse-datasets/browse-datasets.component'; +import { BrowsePredictorsComponent } from './_pages/browse-predictors/browse-predictors.component'; +import { PredictComponent } from './_pages/predict/predict.component'; @NgModule({ declarations: [ AppComponent, - LoginPageComponent, - RegisterPageComponent, - OnlyAuthorizedComponent, DatasetLoadComponent, - AddModelComponent + AddModelComponent, + LoginModalComponent, + RegisterModalComponent, + HomeComponent, + NavbarComponent, + ItemPredictorComponent, + ItemDatasetComponent, + CarouselComponent, + SettingsComponent, + ProfileComponent, + MyPredictorsComponent, + MyDatasetsComponent, + MyModelsComponent, + BrowseDatasetsComponent, + BrowsePredictorsComponent, + PredictComponent ], imports: [ BrowserModule, @@ -31,7 +58,10 @@ import { MaterialModule } from './material.module'; HttpClientModule, NgbModule, BrowserAnimationsModule, - MaterialModule + MaterialModule, + ReactiveFormsModule, + MatSliderModule, + MatIconModule ], providers: [], bootstrap: [AppComponent] |