diff options
author | Danijel Andjelkovic <adanijel99@gmail.com> | 2022-03-24 06:31:08 +0100 |
---|---|---|
committer | Danijel Andjelkovic <adanijel99@gmail.com> | 2022-03-24 06:31:08 +0100 |
commit | 042482a1bbd6ea3460acf3d71bfe3ef64b05524a (patch) | |
tree | e79413f97a8c9f97c978b5048ab89278ab0e6a41 /frontend | |
parent | 0aa45260963dbf0a52726f791c3813928a1bcebc (diff) | |
parent | 1113f094ffee030fd9892deb7577927419860013 (diff) |
Merge branch 'dev' of http://gitlab.pmf.kg.ac.rs/igrannonica/neuronstellar into dev
# Conflicts:
# frontend/src/app/app.module.ts
Diffstat (limited to 'frontend')
44 files changed, 517 insertions, 87 deletions
diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 61251632..8c025c8b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -29,6 +29,7 @@ "mdb-angular-ui-kit": "^2.0.0", "ng-uikit-pro-standard": "^1.0.0", "ng2-charts": "^3.0.8", + "ng2-search-filter": "^0.5.1", "ngx-cookie-service": "^13.1.2", "ngx-csv-parser": "^0.0.7", "rxjs": "~7.5.0", @@ -7984,6 +7985,11 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/ng2-search-filter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/ng2-search-filter/-/ng2-search-filter-0.5.1.tgz", + "integrity": "sha512-noN8R+Gyxo5ZuboEOvq+u0zKio6pEf1IVYQTCZfAfXm6ONmzWu/M2xK0di9oVUprDbPBQXCGUuvD5i2GD+35HA==" + }, "node_modules/ngx-cookie-service": { "version": "13.1.2", "resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-13.1.2.tgz", @@ -17351,6 +17357,11 @@ "tslib": "^2.3.0" } }, + "ng2-search-filter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/ng2-search-filter/-/ng2-search-filter-0.5.1.tgz", + "integrity": "sha512-noN8R+Gyxo5ZuboEOvq+u0zKio6pEf1IVYQTCZfAfXm6ONmzWu/M2xK0di9oVUprDbPBQXCGUuvD5i2GD+35HA==" + }, "ngx-cookie-service": { "version": "13.1.2", "resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-13.1.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 607d5b17..8cd6db58 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,6 +31,7 @@ "mdb-angular-ui-kit": "^2.0.0", "ng-uikit-pro-standard": "^1.0.0", "ng2-charts": "^3.0.8", + "ng2-search-filter": "^0.5.1", "ngx-cookie-service": "^13.1.2", "ngx-csv-parser": "^0.0.7", "rxjs": "~7.5.0", diff --git a/frontend/src/app/Shared.ts b/frontend/src/app/Shared.ts index 0adcd4d6..31afb1a6 100644 --- a/frontend/src/app/Shared.ts +++ b/frontend/src/app/Shared.ts @@ -1,7 +1,8 @@ class Shared { constructor( public loggedIn: boolean, - public username: string = '' + public username: string = '', + public photoId: string = '1' ) { } } diff --git a/frontend/src/app/_data/ProfilePictures.ts b/frontend/src/app/_data/ProfilePictures.ts new file mode 100644 index 00000000..217810d9 --- /dev/null +++ b/frontend/src/app/_data/ProfilePictures.ts @@ -0,0 +1,63 @@ +export class Picture { + photoId!: number; + path!: string; +} + +export const PICTURES = [ + { + photoId: 1, + path: "/assets/profilePictures/1.png" + }, + { + photoId: 2, + path: "/assets/profilePictures/2.png" + }, + { + photoId: 3, + path: "/assets/profilePictures/3.png" + }, + { + photoId: 4, + path: "/assets/profilePictures/4.png" + }, + { + photoId: 5, + path: "/assets/profilePictures/5.png" + }, + { + photoId: 6, + path: "/assets/profilePictures/6.png" + }, + { + photoId: 7, + path: "/assets/profilePictures/7.png" + }, + { + photoId: 8, + path: "/assets/profilePictures/8.png" + }, + { + photoId: 9, + path: "/assets/profilePictures/9.png" + }, + { + photoId: 10, + path: "/assets/profilePictures/10.png" + }, + { + photoId: 11, + path: "/assets/profilePictures/11.png" + }, + { + photoId: 12, + path: "/assets/profilePictures/12.png" + }, + { + photoId: 13, + path: "/assets/profilePictures/13.png" + }, + { + photoId: 14, + path: "/assets/profilePictures/14.png" + } +]
\ No newline at end of file diff --git a/frontend/src/app/_data/User.ts b/frontend/src/app/_data/User.ts index 58383d38..be42ed0a 100644 --- a/frontend/src/app/_data/User.ts +++ b/frontend/src/app/_data/User.ts @@ -1,5 +1,5 @@ export default class User { - _id: string = ''; + _id?: string = ''; constructor( public username: string = '', public email: string = '', diff --git a/frontend/src/app/_elements/navbar/navbar.component.html b/frontend/src/app/_elements/navbar/navbar.component.html index 52e26e6b..82a1ea07 100644 --- a/frontend/src/app/_elements/navbar/navbar.component.html +++ b/frontend/src/app/_elements/navbar/navbar.component.html @@ -18,7 +18,7 @@ <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"> + <img [src]="'/assets/profilePictures/'+ shared.photoId +'.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);" diff --git a/frontend/src/app/_elements/navbar/navbar.component.ts b/frontend/src/app/_elements/navbar/navbar.component.ts index c16e3e9d..5fd98c8f 100644 --- a/frontend/src/app/_elements/navbar/navbar.component.ts +++ b/frontend/src/app/_elements/navbar/navbar.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { Location } from '@angular/common'; import { AuthService } from '../../_services/auth.service'; import shared from 'src/app/Shared'; +import { UserInfoService } from 'src/app/_services/user-info.service'; @Component({ selector: 'app-navbar', @@ -13,7 +14,7 @@ export class NavbarComponent implements OnInit { currentUrl: string; shared = shared; - constructor(public location: Location, private auth: AuthService) { + constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService) { this.currentUrl = this.location.path(); this.location.onUrlChange(() => { this.currentUrl = this.location.path(); @@ -22,6 +23,9 @@ export class NavbarComponent implements OnInit { ngOnInit(): void { this.auth.updateUser(); + this.userInfoService.getUserInfo().subscribe((response) => { + shared.photoId = response.photoId; + }); } logOut() { diff --git a/frontend/src/app/_modals/register-modal/register-modal.component.ts b/frontend/src/app/_modals/register-modal/register-modal.component.ts index c045f1ce..13ef7eba 100644 --- a/frontend/src/app/_modals/register-modal/register-modal.component.ts +++ b/frontend/src/app/_modals/register-modal/register-modal.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { AuthService } from 'src/app/_services/auth.service'; +import User from 'src/app/_data/User'; @Component({ selector: 'app-register-modal', @@ -124,12 +125,13 @@ export class RegisterModalComponent implements OnInit { if (!(this.wrongFirstNameBool || this.wrongLastNameBool || this.wrongUsernameBool || this.wrongEmailBool || this.wrongPass1Bool || this.wrongPass2Bool)) { //sve ok, registruj ga - let user = { + let user: User = { firstName: this.firstName, lastName: this.lastName, username: this.username, password: this.pass1, - email: this.email + email: this.email, + photoId: "1" } this.authService.register(user) 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; + } + + } diff --git a/frontend/src/app/_services/auth.service.ts b/frontend/src/app/_services/auth.service.ts index 20ff45f3..449b8802 100644 --- a/frontend/src/app/_services/auth.service.ts +++ b/frontend/src/app/_services/auth.service.ts @@ -61,8 +61,10 @@ export class AuthService { updateUser() { if (this.cookie.check('token')) { const token = this.cookie.get('token'); + const decodedToken = jwtHelper.decodeToken(token); + console.log("decoded:", decodedToken); this.shared.loggedIn = this.isAuthenticated(); - this.shared.username = jwtHelper.decodeToken(token).name; + this.shared.username = decodedToken.name; this.enableAutoRefresh(); } } diff --git a/frontend/src/app/_services/datasets.service.spec.ts b/frontend/src/app/_services/datasets.service.spec.ts new file mode 100644 index 00000000..87b46acd --- /dev/null +++ b/frontend/src/app/_services/datasets.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { DatasetsService } from './datasets.service'; + +describe('DatasetsService', () => { + let service: DatasetsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DatasetsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_services/datasets.service.ts b/frontend/src/app/_services/datasets.service.ts new file mode 100644 index 00000000..fadcdeae --- /dev/null +++ b/frontend/src/app/_services/datasets.service.ts @@ -0,0 +1,24 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { API_SETTINGS } from 'src/config'; +import Dataset from '../_data/Dataset'; +import { AuthService } from './auth.service'; + +@Injectable({ + providedIn: 'root' +}) +export class DatasetsService { + + constructor(private http: HttpClient, private authService: AuthService) { } + + getPublicDatasets(): Observable<Dataset[]> { + return this.http.get<Dataset[]>(`${API_SETTINGS.apiURL}/Dataset/publicdatasets`, { headers: this.authService.authHeader() }); + } + + addDataset(dataset:Dataset):any{ + return this.http.post(`${API_SETTINGS.apiURL}/dataset/add`, dataset, { headers: this.authService.authHeader() }); + } + + +} diff --git a/frontend/src/app/_services/predictors.service.spec.ts b/frontend/src/app/_services/predictors.service.spec.ts new file mode 100644 index 00000000..7733780b --- /dev/null +++ b/frontend/src/app/_services/predictors.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { PredictorsService } from './predictors.service'; + +describe('PredictorsService', () => { + let service: PredictorsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PredictorsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_services/predictors.service.ts b/frontend/src/app/_services/predictors.service.ts new file mode 100644 index 00000000..0cd7f0f6 --- /dev/null +++ b/frontend/src/app/_services/predictors.service.ts @@ -0,0 +1,21 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { API_SETTINGS } from 'src/config'; +import Predictor from '../_data/Predictor'; +import { AuthService } from './auth.service'; + +@Injectable({ + providedIn: 'root' +}) +export class PredictorsService { + + + + constructor(private http: HttpClient, private authService: AuthService) { } + + getPublicPredictors(): Observable<Predictor[]> { + return this.http.get<Predictor[]>(`${API_SETTINGS.apiURL}/Predictor/publicpredictors`, { headers: this.authService.authHeader() }); + } + +} diff --git a/frontend/src/app/_services/user-info.service.ts b/frontend/src/app/_services/user-info.service.ts index b66a73e1..7ed2970c 100644 --- a/frontend/src/app/_services/user-info.service.ts +++ b/frontend/src/app/_services/user-info.service.ts @@ -12,15 +12,19 @@ export class UserInfoService { constructor(private http: HttpClient, private authService: AuthService) { } - getUsersInfo(): Observable<User> { + getUserInfo(): Observable<User> { return this.http.get<User>(`${API_SETTINGS.apiURL}/user/myprofile`, { headers: this.authService.authHeader() }); } changeUserInfo(user: User): any { - return this.http.put(`${API_SETTINGS.apiURL}/user/${user._id}`, user, { headers: this.authService.authHeader() }); + return this.http.put(`${API_SETTINGS.apiURL}/user/changeinfo`, user, { headers: this.authService.authHeader() }); } - changeUserPassword(oldPassword: string, newPassword: string): Observable<User> { - return this.http.put<User>(`${API_SETTINGS.apiURL}/user/`, { oldPassword, newPassword }, { headers: this.authService.authHeader() }); + changeUserPassword(passwordArray: string[]): any { + return this.http.put(`${API_SETTINGS.apiURL}/user/changepass`, passwordArray, { headers: this.authService.authHeader(), responseType: 'text' }); + } + + deleteUser(): any { + return this.http.delete(`${API_SETTINGS.apiURL}/user/deleteprofile`, { headers: this.authService.authHeader() }); } } diff --git a/frontend/src/app/_services/web-socket.service.ts b/frontend/src/app/_services/web-socket.service.ts index fc292a62..890ada6b 100644 --- a/frontend/src/app/_services/web-socket.service.ts +++ b/frontend/src/app/_services/web-socket.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; +import { ConstantBackoff, Websocket, WebsocketBuilder } from 'websocket-ts'; import { API_SETTINGS } from 'src/config'; -import { ConstantBackoff, Websocket, WebsocketBuilder } from 'websocket-ts/lib'; @Injectable({ providedIn: 'root' diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 001d0b4f..a52d66a8 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -12,6 +12,7 @@ import { BrowseDatasetsComponent } from './_pages/browse-datasets/browse-dataset import { SettingsComponent } from './_pages/settings/settings.component'; import { ProfileComponent } from './_pages/profile/profile.component'; import { PredictComponent } from './_pages/predict/predict.component'; +import { FilterDatasetsComponent } from './_pages/filter-datasets/filter-datasets.component'; const routes: Routes = [ { path: '', component: HomeComponent, data: { title: 'Početna strana' } }, @@ -23,7 +24,8 @@ const routes: Routes = [ { path: 'profile', component: ProfileComponent, canActivate: [AuthGuardService], data: { title: 'Profil' } }, { path: 'browse-datasets', component: BrowseDatasetsComponent, data: { title: 'Javni izvori podataka' } }, { path: 'browse-predictors', component: BrowsePredictorsComponent, data: { title: 'Javni trenirani modeli' } }, - { path: 'predict', component: PredictComponent, data: { title: 'Predvidi vrednosti' } } + { path: 'predict', component: PredictComponent, data: { title: 'Predvidi vrednosti' } }, + { path: 'filter-datasets', component: FilterDatasetsComponent, data: { title: 'Filter datasets' } } ]; @NgModule({ diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 3387de8c..4612e3a7 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -6,8 +6,8 @@ import { HttpClientModule } from '@angular/common/http'; import { MatSliderModule } from '@angular/material/slider'; import { MatIconModule } from '@angular/material/icon'; -import {NgChartsModule} from 'ng2-charts'; - +import { NgChartsModule } from 'ng2-charts'; +import { Ng2SearchPipeModule } from 'ng2-search-filter'; import { AppComponent } from './app.component'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { DatasetLoadComponent } from './_elements/dataset-load/dataset-load.component'; @@ -35,6 +35,7 @@ import { ScatterchartComponent } from './scatterchart/scatterchart.component'; import { BarchartComponent } from './barchart/barchart.component'; import { NotificationsComponent } from './_elements/notifications/notifications.component'; import { DatatableComponent } from './_elements/datatable/datatable.component'; +import { FilterDatasetsComponent } from './_pages/filter-datasets/filter-datasets.component'; @NgModule({ declarations: [ @@ -59,7 +60,8 @@ import { DatatableComponent } from './_elements/datatable/datatable.component'; ScatterchartComponent, BarchartComponent, NotificationsComponent, - DatatableComponent + DatatableComponent, + FilterDatasetsComponent ], imports: [ BrowserModule, @@ -72,7 +74,8 @@ import { DatatableComponent } from './_elements/datatable/datatable.component'; ReactiveFormsModule, MatSliderModule, MatIconModule, - NgChartsModule + NgChartsModule, + Ng2SearchPipeModule ], providers: [], bootstrap: [AppComponent] diff --git a/frontend/src/assets/profilePictures/1.png b/frontend/src/assets/profilePictures/1.png Binary files differnew file mode 100644 index 00000000..6e2f8b73 --- /dev/null +++ b/frontend/src/assets/profilePictures/1.png diff --git a/frontend/src/assets/profilePictures/10.png b/frontend/src/assets/profilePictures/10.png Binary files differnew file mode 100644 index 00000000..cbd270ca --- /dev/null +++ b/frontend/src/assets/profilePictures/10.png diff --git a/frontend/src/assets/profilePictures/11.png b/frontend/src/assets/profilePictures/11.png Binary files differnew file mode 100644 index 00000000..982fdae4 --- /dev/null +++ b/frontend/src/assets/profilePictures/11.png diff --git a/frontend/src/assets/profilePictures/12.png b/frontend/src/assets/profilePictures/12.png Binary files differnew file mode 100644 index 00000000..2aedbdb0 --- /dev/null +++ b/frontend/src/assets/profilePictures/12.png diff --git a/frontend/src/assets/profilePictures/13.png b/frontend/src/assets/profilePictures/13.png Binary files differnew file mode 100644 index 00000000..f8d771d9 --- /dev/null +++ b/frontend/src/assets/profilePictures/13.png diff --git a/frontend/src/assets/profilePictures/14.png b/frontend/src/assets/profilePictures/14.png Binary files differnew file mode 100644 index 00000000..d3ec8ae1 --- /dev/null +++ b/frontend/src/assets/profilePictures/14.png diff --git a/frontend/src/assets/profilePictures/2.png b/frontend/src/assets/profilePictures/2.png Binary files differnew file mode 100644 index 00000000..d8dc7967 --- /dev/null +++ b/frontend/src/assets/profilePictures/2.png diff --git a/frontend/src/assets/profilePictures/3.png b/frontend/src/assets/profilePictures/3.png Binary files differnew file mode 100644 index 00000000..b4219c22 --- /dev/null +++ b/frontend/src/assets/profilePictures/3.png diff --git a/frontend/src/assets/profilePictures/4.png b/frontend/src/assets/profilePictures/4.png Binary files differnew file mode 100644 index 00000000..ef0701ef --- /dev/null +++ b/frontend/src/assets/profilePictures/4.png diff --git a/frontend/src/assets/profilePictures/5.png b/frontend/src/assets/profilePictures/5.png Binary files differnew file mode 100644 index 00000000..8523f582 --- /dev/null +++ b/frontend/src/assets/profilePictures/5.png diff --git a/frontend/src/assets/profilePictures/6.png b/frontend/src/assets/profilePictures/6.png Binary files differnew file mode 100644 index 00000000..96540607 --- /dev/null +++ b/frontend/src/assets/profilePictures/6.png diff --git a/frontend/src/assets/profilePictures/7.png b/frontend/src/assets/profilePictures/7.png Binary files differnew file mode 100644 index 00000000..f0557738 --- /dev/null +++ b/frontend/src/assets/profilePictures/7.png diff --git a/frontend/src/assets/profilePictures/8.png b/frontend/src/assets/profilePictures/8.png Binary files differnew file mode 100644 index 00000000..835ba0ab --- /dev/null +++ b/frontend/src/assets/profilePictures/8.png diff --git a/frontend/src/assets/profilePictures/9.png b/frontend/src/assets/profilePictures/9.png Binary files differnew file mode 100644 index 00000000..fd38fac4 --- /dev/null +++ b/frontend/src/assets/profilePictures/9.png diff --git a/frontend/src/index.html b/frontend/src/index.html index 0079969e..1461c9ae 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -1,5 +1,6 @@ <!doctype html> <html lang="en"> + <head> <meta charset="utf-8"> <title>Frontend</title> @@ -10,8 +11,9 @@ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> </head> + <body class="mat-typography"> <app-root></app-root> - <script src="node_modules/chart.js/src/chart.js"></script> </body> -</html> + +</html>
\ No newline at end of file |