diff options
Diffstat (limited to 'frontend/src/app/_pages')
12 files changed, 326 insertions, 68 deletions
diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.css b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.css index e69de29b..b4ac9669 100644 --- a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.css +++ b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.css @@ -0,0 +1,7 @@ +#container { + border-radius: 8px; +} + +#wrapper { + color: #003459; +}
\ No newline at end of file diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html index 01c4af82..a4ab6e2c 100644 --- a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html +++ b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html @@ -1 +1,38 @@ -<p>browse-predictors works!</p> + +<div id="wrapper"> + + <div id="container" class="container p-5" style="background-color: white; min-height: 100%;"> + <div class="row mt-3 mb-2 d-flex justify-content-center"> + + <div class="col-sm-6" style="margin-bottom: 10px;"> + <input type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term"> + </div> + + <div class="row"> + <div class="col-sm-4" style="margin-bottom: 10px;" *ngFor="let predictor of publicPredictors | filter:term"> + <div class="card h-100"> + <div class="card-body"> + <h3 class="card-title"><b>{{predictor.name}}</b></h3> + <p class="card-text">{{predictor.description}}</p> + <a class="btn btn-primary" (click)="openPredictor(predictor._id)">Otvori</a> + </div> + <div class="card-footer text-muted"> + Kreirao: {{predictor.username}} <br> + Datum kreiranja: {{predictor.dateCreated |date}} + </div> + </div> + </div> + + + </div> + <div class="text-center"*ngIf="( publicPredictors != undefined && publicPredictors|filter:term).length === 0"> + <h2>Nema rezultata</h2> + </div> + </div> + + </div> + + + + +</div>
\ No newline at end of file diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts index b4fb2a9d..4f96fc36 100644 --- a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts +++ b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts @@ -1,5 +1,7 @@ import { Component, OnInit } from '@angular/core'; - +import { PredictorsService } from 'src/app/_services/predictors.service'; +import Predictor from 'src/app/_data/Predictor'; +import {Router} from '@angular/router' @Component({ selector: 'app-browse-predictors', templateUrl: './browse-predictors.component.html', @@ -7,9 +9,18 @@ import { Component, OnInit } from '@angular/core'; }) export class BrowsePredictorsComponent implements OnInit { - constructor() { } + publicPredictors? :Predictor[]; + term: string=""; + constructor(private predictors: PredictorsService,private router:Router) { + this.predictors.getPublicPredictors().subscribe((predictors) => { + this.publicPredictors = predictors; + }); + } ngOnInit(): void { } + openPredictor(id:string):void{ + this.router.navigateByUrl('/predict?id='+id); + }; } diff --git a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.css b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.css diff --git a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.html b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.html new file mode 100644 index 00000000..84f5ebaf --- /dev/null +++ b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.html @@ -0,0 +1,38 @@ + +<div id="wrapper"> + + <div id="container" class="container p-5" style="background-color: white; min-height: 100%;"> + <div class="row mt-3 mb-2 d-flex justify-content-center"> + + <div class="col-sm-6" style="margin-bottom: 10px;"> + <input type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term"> + </div> + + <div class="row"> + <div class="col-sm-4" style="margin-bottom: 10px;" *ngFor="let dataset of publicDatasets | filter:term"> + <div class="card h-100"> + <div class="card-body"> + <h3 class="card-title"><b>{{dataset.name}}</b></h3> + <p class="card-text">{{dataset.description}}</p> + <a class="btn btn-primary" (click)="addDataset(dataset)">Dodaj dataset</a> + </div> + <div class="card-footer text-muted"> + Kreirao: {{dataset.username}} <br> + Datum kreiranja: {{dataset.dateCreated |date}} + </div> + </div> + </div> + + + </div> + <div class="text-center"*ngIf="( publicDatasets != undefined && publicDatasets|filter:term).length === 0"> + <h2>Nema rezultata</h2> + </div> + </div> + + </div> + + + + +</div> diff --git a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.spec.ts b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.spec.ts new file mode 100644 index 00000000..6ab894fd --- /dev/null +++ b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FilterDatasetsComponent } from './filter-datasets.component'; + +describe('FilterDatasetsComponent', () => { + let component: FilterDatasetsComponent; + let fixture: ComponentFixture<FilterDatasetsComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FilterDatasetsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(FilterDatasetsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts new file mode 100644 index 00000000..bc13a51c --- /dev/null +++ b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit } from '@angular/core'; +import { DatasetsService } from 'src/app/_services/datasets.service'; +import Dataset from 'src/app/_data/Dataset'; +import {Router} from '@angular/router' +import { JwtHelperService } from '@auth0/angular-jwt'; +import { CookieService } from 'ngx-cookie-service'; + +@Component({ + selector: 'app-filter-datasets', + templateUrl: './filter-datasets.component.html', + styleUrls: ['./filter-datasets.component.css'] +}) +export class FilterDatasetsComponent implements OnInit { + + publicDatasets?: Dataset[]; + term: string = ""; + constructor(private datasets: DatasetsService,private router:Router, private cookie: CookieService) { + this.datasets.getPublicDatasets().subscribe((datasets) => { + this.publicDatasets = datasets; + }); + } + + ngOnInit(): void { + + } + addDataset(dataset: Dataset):void{ + //this.router.navigateByUrl('/predict?id='+id); + const helper = new JwtHelperService(); + const decodedToken = helper.decodeToken(this.cookie.get("token")); + dataset._id = ""; + dataset.isPublic = false; + dataset.lastUpdated = new Date(); + dataset.username = decodedToken.name; + this.datasets.addDataset(dataset).subscribe((response:string)=>{ + console.log(response); + }); + }; + +} diff --git a/frontend/src/app/_pages/home/home.component.html b/frontend/src/app/_pages/home/home.component.html index 274f0fd8..689a302b 100644 --- a/frontend/src/app/_pages/home/home.component.html +++ b/frontend/src/app/_pages/home/home.component.html @@ -46,6 +46,7 @@ </div> <h2 class="my-4">Pogledajte javne izvore podataka!</h2> <app-carousel [items]="publicDatasets"> + </app-carousel> <h3><a routerLink="browse-datasets">Pogledaj sve javne izvore podataka...</a></h3> <h2 class="my-4">Iskoristite već trenirane modele!</h2> diff --git a/frontend/src/app/_pages/my-models/my-models.component.html b/frontend/src/app/_pages/my-models/my-models.component.html index 2fea257d..e69de29b 100644 --- a/frontend/src/app/_pages/my-models/my-models.component.html +++ b/frontend/src/app/_pages/my-models/my-models.component.html @@ -1 +0,0 @@ -<p>my-models works!</p> diff --git a/frontend/src/app/_pages/profile/profile.component.css b/frontend/src/app/_pages/profile/profile.component.css index 8c2ffa88..5565d105 100644 --- a/frontend/src/app/_pages/profile/profile.component.css +++ b/frontend/src/app/_pages/profile/profile.component.css @@ -4,13 +4,11 @@ color:#69707a; } .img-account-profile { height: 10rem; + border: 1px solid lightgray; } .rounded-circle { border-radius: 50% !important; } -.card { - box-shadow: 0 0.15rem 1.75rem 0 rgb(33 40 50 / 15%); -} .card .card-header { font-weight: 500; } @@ -40,3 +38,7 @@ color:#69707a; border-radius: 0.35rem; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + +.selectedPicture { + border: 2px solid darkgray; +} diff --git a/frontend/src/app/_pages/profile/profile.component.html b/frontend/src/app/_pages/profile/profile.component.html index bece7c46..d082a003 100644 --- a/frontend/src/app/_pages/profile/profile.component.html +++ b/frontend/src/app/_pages/profile/profile.component.html @@ -1,4 +1,4 @@ -<div class="container-xl px-4 mt-4"> +<div class="container-xl px-4 mt-1"> <hr class="mt-0 mb-4"> <div class="row"> @@ -7,15 +7,60 @@ <div class="card mb-4 mb-xl-0"> <div class="card-header">Moj profil</div> <div class="card-body text-center"> - <!-- Profile picture image--> - <img class="img-account-profile rounded-circle mb-2" src="http://bootdey.com/img/Content/avatar/avatar1.png" alt=""> - <!-- Profile picture help block--> - <div class="small font-italic text-muted mb-4">JPG or PNG no larger than 5 MB</div> - <!-- Profile picture upload button--> - <button class="btn btn-primary" type="button">Upload new image</button> + <div class=" image d-flex flex-column justify-content-center align-items-center"> + <!-- Profile picture image--> + <img class="img-account-profile rounded-circle mb-2" src="{{this.photoPath}}" alt="profilePicture"> + <!-- User's info --> + <span>@ {{this.user.username}}</span> + <span class="mt-3" style="font-weight: bold;">{{this.user.firstName}} {{this.user.lastName}}</span> + </div> + </div> + </div> + + <!-- Password Change card --> + <div class="card mt-3"> + <div class="card-header">Promena lozinke</div> + <div class="card-body"> + <form> + <div class="row"> + <!-- Form Row--> + <div class="row gx-3 mb-3"> + <!-- Form Group (password)--> + <div class="col-md-6"> + <label class="small mb-1" for="inputPassword">Važeća lozinka</label> + <input class="form-control" id="inputPassword" name="inputPassword" type="password" [(ngModel)]="this.oldPass" placeholder="Trenutna lozinka"> + <small *ngIf="wrongPassBool" class="form-text text-danger">Neispravna lozinka.</small> + </div> + <!-- Form Group (new password)--> + <div class="col-md-6"> + <label class="small mb-1" for="inputNewPassword">Nova lozinka</label> + <input class="form-control" id="inputNewPassword" name="inputNewPassword" type="password" [(ngModel)]="this.newPass1" placeholder="Ukucaj novu lozinku"> + <small *ngIf="wrongNewPassBool" class="form-text text-danger">Lozinke se ne podudaraju.</small> + </div> + </div> + + <!-- Form Row--> + <div class="row gx-3 mb-3"> + <div class="col-md-6"> + <div class="col text-center"> + <!-- Save changes button--> + <button class="btn btn-primary text-center mt-4" type="button" (click)="savePasswordChanges()">Promeni lozinku</button> + </div> + </div> + <!-- Form Group (new password again)--> + <div class="col-md-6"> + <label class="small mb-1" for="inputNewPasswordAgain">Ponovo nova lozinka</label> + <input class="form-control" id="inputNewPasswordAgain" name="inputNewPasswordAgain" type="password" [(ngModel)]="this.newPass2" placeholder="Ukucaj novu lozinku"> + <small *ngIf="wrongNewPassBool" class="form-text text-danger">Lozinke se ne podudaraju.</small> + </div> + </div> + </div> + </form> </div> </div> </div> + + <!-- Info Change card --> <div class="col-xl-8"> <!-- Account details card--> <div class="card mb-4"> @@ -49,6 +94,22 @@ <input class="form-control" id="inputLastName" name="inputLastName" type="text" [(ngModel)]="this.lastName"> </div> </div> + + <div> + <label class="small mt-2 mb-3">Kliknite na sliku kako biste je odabrali za profilnu:</label> + + <div class="container"> + <div class="card-group"> + <!--bootstrap card with 3 horizontal images--> + <div class="row overflow-auto" style="max-height: 200px;"> + <div class="card col-md-3" *ngFor="let picture of this.pictures" (click)="this.photoId = picture.photoId.toString()" + [ngClass]="{'selectedPicture': this.photoId == picture.photoId.toString()}"> + <img src="{{picture.path}}"> + </div> + </div> + </div> + </div> + </div> <div class="row mt-5"> <div class="col text-center"> @@ -65,48 +126,7 @@ <div class="row"> <div class="col-xl-4"> - <div class="card mb-4"> - <div class="card-header">Promena lozinke</div> - <div class="card-body"> - <form> - - <div class="row"> - <!-- Form Row--> - <div class="row gx-3 mb-3"> - <!-- Form Group (password)--> - <div class="col-md-6"> - <label class="small mb-1" for="inputPassword">Važeća lozinka</label> - <input class="form-control" id="inputPassword" name="inputPassword" type="password" [(ngModel)]="this.oldPass" placeholder="Trenutna lozinka"> - <small *ngIf="wrongPassBool" class="form-text text-danger">Neispravna lozinka.</small> - </div> - <!-- Form Group (new password)--> - <div class="col-md-6"> - <label class="small mb-1" for="inputNewPassword">Nova lozinka</label> - <input class="form-control" id="inputNewPassword" name="inputNewPassword" type="password" [(ngModel)]="this.newPass1" placeholder="Ukucaj novu lozinku"> - <small *ngIf="wrongNewPassBool" class="form-text text-danger">Lozinke se ne podudaraju.</small> - </div> - </div> - - <!-- Form Row--> - <div class="row gx-3 mb-3"> - <div class="col-md-6"> - <div class="col text-center"> - <!-- Save changes button--> - <button class="btn btn-primary text-center mt-4" type="button" (click)="savePasswordChanges()">Promeni lozinku</button> - </div> - </div> - <!-- Form Group (new password again)--> - <div class="col-md-6"> - <label class="small mb-1" for="inputNewPasswordAgain">Ponovo nova lozinka</label> - <input class="form-control" id="inputNewPasswordAgain" name="inputNewPasswordAgain" type="password" [(ngModel)]="this.newPass2" placeholder="Ukucaj novu lozinku"> - <small *ngIf="wrongNewPassBool" class="form-text text-danger">Lozinke se ne podudaraju.</small> - </div> - </div> - </div> - - </form> - </div> - </div> + </div> </div> diff --git a/frontend/src/app/_pages/profile/profile.component.ts b/frontend/src/app/_pages/profile/profile.component.ts index 4b474678..3e9a0d11 100644 --- a/frontend/src/app/_pages/profile/profile.component.ts +++ b/frontend/src/app/_pages/profile/profile.component.ts @@ -1,6 +1,12 @@ import { Component, OnInit } from '@angular/core'; import User from 'src/app/_data/User'; import { UserInfoService } from 'src/app/_services/user-info.service'; +import { AuthService } from 'src/app/_services/auth.service'; +import { Router } from '@angular/router'; +import { PICTURES } from 'src/app/_data/ProfilePictures'; +import { Picture } from 'src/app/_data/ProfilePictures'; +import shared from '../../Shared'; + @Component({ selector: 'app-profile', @@ -10,6 +16,7 @@ import { UserInfoService } from 'src/app/_services/user-info.service'; export class ProfileComponent implements OnInit { user: User = new User(); + pictures: Picture[] = PICTURES; username: string = ''; email: string = ''; @@ -19,24 +26,46 @@ export class ProfileComponent implements OnInit { newPass1: string = ''; newPass2: string = ''; photoId: string = ''; + photoPath: string = ''; wrongPassBool: boolean = false; wrongNewPassBool: boolean = false; - constructor(private userInfoService: UserInfoService) { } + wrongFirstNameBool: boolean = false; + wrongLastNameBool: boolean = false; + wrongUsernameBool: boolean = false; + wrongEmailBool: boolean = false; + wrongOldPassBool: boolean = false; + wrongNewPass1Bool: boolean = false; + wrongNewPass2Bool: 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 userInfoService: UserInfoService, private authService: AuthService, private router: Router) { } ngOnInit(): void { - this.userInfoService.getUsersInfo().subscribe((response) => { + this.userInfoService.getUserInfo().subscribe((response) => { this.user = response; - - this.user.password = 'sonja123'; + shared.photoId = this.user.photoId; this.username = this.user.username; this.email = this.user.email; this.firstName = this.user.firstName; this.lastName = this.user.lastName; this.photoId = this.user.photoId; + + for (let i = 0; i < this.pictures.length; i++) { + if (this.pictures[i].photoId.toString() === this.photoId) { + this.photoPath = this.pictures[i].path; + break; + } + } console.log(this.user); }); } @@ -49,14 +78,27 @@ export class ProfileComponent implements OnInit { password: this.user.password, firstName: this.firstName, lastName: this.lastName, - photoId: "1" + photoId: this.photoId } this.userInfoService.changeUserInfo(editedUser).subscribe((response: any) =>{ + if (this.user.username != editedUser.username) { //promenio username, ide logout + this.user = editedUser; + alert("Nakon promene korisničkog imena, moraćete ponovo da se ulogujete."); + this.authService.logOut(); + this.router.navigate(['']); + return; + } this.user = editedUser; console.log(this.user); + this.resetInfo(); }, (error: any) =>{ - console.log(error); + if (error.error == "Username already exists!") { + alert("Ukucano korisničko ime je već zauzeto!\nIzaberite neko drugo."); + (<HTMLSelectElement>document.getElementById("inputUsername")).focus(); + //poruka obavestenja ispod inputa + this.resetInfo(); + } }); } @@ -64,10 +106,10 @@ export class ProfileComponent implements OnInit { if (this.newPass1 == '' && this.newPass2 == '') //ne zeli da promeni lozinku return; console.log("zeli da promeni lozinku"); + if (this.newPass1 != this.newPass2) { //netacno ponovio novu lozinku this.wrongNewPassBool = true; - this.newPass1 = ''; - this.newPass2 = ''; + this.resetNewPassInputs(); console.log("Netacno ponovljena lozinka"); return; } @@ -75,12 +117,49 @@ export class ProfileComponent implements OnInit { this.wrongPassBool = false; this.wrongNewPassBool = false; - this.userInfoService.changeUserPassword(this.oldPass, this.newPass1).subscribe((response) => { - this.user = response; - console.log(this.user); + let passwordArray: string[] = [this.oldPass, this.newPass1]; + this.userInfoService.changeUserPassword(passwordArray).subscribe((response: any) => { + console.log("PROMENIO LOZINKU"); + this.resetNewPassInputs(); + alert("Nakon promene lozinke, moraćete ponovo da se ulogujete."); + this.authService.logOut(); + this.router.navigate(['']); }, (error: any) => { - + console.log("error poruka: ", error.error); + if (error.error == 'Wrong old password!') { + this.wrongPassBool = true; + (<HTMLSelectElement>document.getElementById("inputPassword")).focus(); + return; + } + else if (error.error == 'Identical password!') { + alert("Stara i nova lozinka su identične."); + this.resetNewPassInputs(); + (<HTMLSelectElement>document.getElementById("inputNewPassword")).focus(); + return; + } }); } + resetNewPassInputs() { + this.newPass1 = ''; + this.newPass2 = ''; + } + + resetInfo() { + this.username = this.user.username; + this.email = this.user.email; + this.firstName = this.user.firstName; + this.lastName = this.user.lastName; + this.photoId = this.user.photoId; + + for (let i = 0; i < this.pictures.length; i++) { + if (this.pictures[i].photoId.toString() === this.photoId) { + this.photoPath = this.pictures[i].path; + break; + } + } + shared.photoId = this.photoId; + } + + } |