diff options
6 files changed, 238 insertions, 36 deletions
diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html index 8da7f86..16b537c 100644 --- a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html +++ b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html @@ -58,6 +58,27 @@ <label *ngIf="updateUserFormGroup.get('password')?.errors?.pattern" class="error">*At least 1 number</label> </div> </div> + <button type="button" (click)="toggleLanguages()">Edit Languages</button> + <div *ngIf="showLanguages"> + Type in your desired languages, separated by a space. + <input type="text" class="input-field" formControlName="languageInput" required> + Available languages: + <div *ngFor="let lang of availableLanguages"> + {{ lang.name }} + </div> + </div> + + <button type="button" (click)="toggleTechnologies()">Edit Technologies</button> + <div *ngIf="showTechnologies"> + Type in your desired technologies, separated by a space. + <input type="text" class="input-field" formControlName="technologyInput" required> + Available technologies: + <div *ngFor="let tech of availableTechnologies"> + {{ tech.name }} + </div> + </div> + + <button class="submit-btn" type="submit">Update profile</button> <app-success-bar></app-success-bar> <app-error-bar></app-error-bar> diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts index 0ea5ea2..5be160e 100644 --- a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts +++ b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts @@ -4,8 +4,10 @@ import { Component, OnInit, ViewChild } from '@angular/core'; import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; import {Router} from '@angular/router'; import {AppConstants} from 'src/app/app-constants.module'; +import {LanguageService} from 'src/app/services/language.service'; import {UserService} from 'src/app/services/user.service'; -import {User} from 'src/models/identity/user'; +import {TechnologyService} from 'src/app/services/technology.service'; +import {Language, Technology, User} from 'src/models/identity/user'; import {ErrorBarComponent} from '../error-bar/error-bar.component'; import {SuccessBarComponent} from '../success-bar/success-bar.component'; @@ -22,12 +24,16 @@ export class ProfileSettingsComponent implements OnInit { public dataArrived = false; public user: User; public deleteAccountConfirm = false; + public showLanguages = false; + public availableLanguages: Language[] = []; + public showTechnologies = false; + public availableTechnologies: Technology[] = []; - constructor(private _router: Router, private _userService: UserService, private _fb: FormBuilder, private _location: Location) + constructor(private _router: Router, private _userService: UserService, private _languageService: LanguageService, private _technologyService: TechnologyService, private _fb: FormBuilder, private _location: Location) { } ngOnInit(): void { - this._urlUsername = this._router.url.substring(9) + this._urlUsername = this._router.url.substring(9); this._urlUsername = this._urlUsername.substring(0, this._urlUsername.length - 9); this.user = this._userService.getDefaultUser(); @@ -35,6 +41,17 @@ export class ProfileSettingsComponent implements OnInit { (res: object) => this.finishUserLoading(res), (err: HttpErrorResponse) => { this._router.navigate(['/not-found']); } ); + + this._languageService.getAllLanguagesWithSessionStorageRequest().subscribe( + (result: object) => { + this.availableLanguages = result as Language[]; + } + ); + this._technologyService.getAllTechnologiesWithSessionStorageRequest().subscribe( + (result: object) => { + this.availableTechnologies = result as Technology[]; + } + ); } private finishUserLoading(res: object): void { @@ -89,14 +106,51 @@ export class ProfileSettingsComponent implements OnInit { Validators.minLength(3), Validators.pattern('.*[0-9].*') // Check if password contains atleast one number ]), + + // For language we have two different controls, + // the first one is used for input, the other one for sending data + // because if we edit the control for input, + // we're also gonna change the input field in the HTML + languageInput: new FormControl(''), // The one for input + languages: new FormControl(''), // The one that is sent + + // For technologies it's the same as it is with languages + technologyInput: new FormControl(''), + technologies: new FormControl('') }); + this.getLanguagesForShowing() + .then(value => this.updateUserFormGroup.patchValue({ languageInput : value })); + + this.getTechnologiesForShowing() + .then(value => this.updateUserFormGroup.patchValue({ technologyInput : value })); + this.updateUserFormGroup.valueChanges.subscribe(() => { this._successBar.hideMsg(); this._errorBar.hideError(); }); } + private getLanguagesForShowing(): Promise<string> { + return new Promise(resolve => + this._languageService.getFullLanguagesFromIncomplete(this.user.languages) + .then(value => { + this.user.languages = value; + resolve(value.map(x => x.name).join(' ')); + }) + ); + } + + private getTechnologiesForShowing(): Promise<string> { + return new Promise(resolve => + this._technologyService.getFullTechnologiesFromIncomplete(this.user.technologies) + .then(value => { + this.user.technologies = value; + resolve(value.map(x => x.name).join(' ')); + }) + ); + } + private bailOnBadToken(): void { this._userService.logoutUserFromSessionStorage(); this._router.navigate(['/login']); @@ -105,12 +159,69 @@ export class ProfileSettingsComponent implements OnInit { onSubmit(): void { this._successBar.hideMsg(); this._errorBar.hideError(); + + this.patchLanguagesControl(); + this.patchTechnologiesControl(); + this._userService.putUserFromSessionStorageRequest(this.updateUserFormGroup).subscribe( res => this._successBar.showMsg('Profile updated successfully!'), (err: HttpErrorResponse) => this._errorBar.showError(err) ); } + private patchLanguagesControl(): void { + // Get user input + const langControl = this.updateUserFormGroup.get('languageInput')?.value as string ?? ''; + + if (langControl === '') { + // Add the data to the form (to the value that is going to be sent) + this.updateUserFormGroup.patchValue({ + languages : [] + }); + } + else { + const names = langControl.split(' '); + + // Transfer user input to objects of type { "name": "value" } + const actualLanguages = []; + for (const lName of names) { + actualLanguages.push({ name : lName }); + } + + // Add the data to the form (to the value that is going to be sent) + this.updateUserFormGroup.patchValue({ + languages : actualLanguages + }); + } + } + + private patchTechnologiesControl(): void { + // Get user input + const techControl = this.updateUserFormGroup.get('technologyInput')?.value as string ?? ''; + + if (techControl === '') { + // Add the data to the form (to the value that is going to be sent) + this.updateUserFormGroup.patchValue({ + technologies : [] + }); + } + else { + const names = techControl.split(' '); + + // Transfer user input to objects of type { "name": "value" } + const actualTechnologies = []; + for (const tName of names) { + actualTechnologies.push({ name : tName }); + } + + // Add the data to the form (to the value that is going to be sent) + this.updateUserFormGroup.patchValue({ + technologies : actualTechnologies + }); + } + } + + goToProfile(): void { this._router.navigate([this._router.url.substring(0, this._router.url.length - 9)]); } @@ -128,6 +239,14 @@ export class ProfileSettingsComponent implements OnInit { this.goToProfile(); } + toggleLanguages(): void { + this.showLanguages = !this.showLanguages; + } + + toggleTechnologies(): void { + this.showTechnologies = !this.showTechnologies; + } + deleteAccount(): void { if (this.deleteAccountConfirm) { this._userService.deleteUserFromSessionStorageRequest().subscribe( diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.ts b/src/DevHive.Angular/src/app/components/profile/profile.component.ts index 5bc1976..69d7e72 100644 --- a/src/DevHive.Angular/src/app/components/profile/profile.component.ts +++ b/src/DevHive.Angular/src/app/components/profile/profile.component.ts @@ -42,21 +42,12 @@ export class ProfileComponent implements OnInit { Object.assign(this.user, res); if (this.user.languages.length > 0) { - // For each language in the user, request it's name and assign it, - // when you finally finish with them, start loading technologies - const lastGuid = this.user.languages[this.user.languages.length - 1].id; - for (const lang of this.user.languages) { - this._languageService.getLanguageRequest(lang.id).subscribe( - (result: object) => { - // this only assigns the response "name" property to language - Object.assign(lang, result); - - if (lastGuid === lang.id) { - this.loadTechnologies(); - } - } - ); - } + // When user has languages, get their names and load technologies + this._languageService.getFullLanguagesFromIncomplete(this.user.languages) + .then(value => { + this.user.languages = value; + this.loadTechnologies(); + }); } else { this.showNoLangMsg = true; @@ -66,21 +57,12 @@ export class ProfileComponent implements OnInit { private loadTechnologies(): void { if (this.user.technologies.length > 0) { - // For each language in the user, request it's name and assign it, - // when you finish with them, finally finish user loading - const lastGuid = this.user.technologies[this.user.technologies.length - 1].id; - for (const tech of this.user.technologies) { - this._technologyService.getTechnologyRequest(tech.id).subscribe( - (result: object) => { - // this only assigns the response "name" property to technology - Object.assign(tech, result); - - if (lastGuid === tech.id) { - this.finishUserLoading(); - } - } - ); - } + // When user has technologies, get their names and finally finish loading + this._technologyService.getFullTechnologiesFromIncomplete(this.user.technologies) + .then(value => { + this.user.technologies = value; + this.finishUserLoading(); + }); } else { this.showNoTechMsg = true; diff --git a/src/DevHive.Angular/src/app/services/language.service.ts b/src/DevHive.Angular/src/app/services/language.service.ts index 8613a65..d85f178 100644 --- a/src/DevHive.Angular/src/app/services/language.service.ts +++ b/src/DevHive.Angular/src/app/services/language.service.ts @@ -2,6 +2,7 @@ import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http'; import {Injectable} from '@angular/core'; import {Guid} from 'guid-typescript'; import {Observable} from 'rxjs'; +import {Language} from 'src/models/identity/user'; import {AppConstants} from '../app-constants.module'; @Injectable({ @@ -16,4 +17,43 @@ export class LanguageService { }; return this.http.get(AppConstants.API_LANGUAGE_URL, options); } + + getAllLanguagesWithSessionStorageRequest(): Observable<object> { + const token = sessionStorage.getItem('UserCred') ?? ''; + return this.getAllLanguagesRequest(token); + } + + getAllLanguagesRequest(authToken: string): Observable<object> { + const options = { + headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) + }; + return this.http.get(AppConstants.API_LANGUAGE_URL + '/GetLanguages', options); + } + + getFullLanguagesFromIncomplete(givenLanguages: Language[]): Promise<Language[]> { + if (givenLanguages.length === 0) { + return new Promise(resolve => resolve(givenLanguages)); + } + + // This accepts language array with incomplete languages, meaning + // languages that only have an id, but no name + return new Promise(resolve => { + const lastGuid = givenLanguages[givenLanguages.length - 1].id; + + // For each language, request his name and assign it + for (const lang of givenLanguages) { + this.getLanguageRequest(lang.id).subscribe( + (result: object) => { + // this only assigns the "name" property to the language, + // because only the name is returned from the request + Object.assign(lang, result); + + if (lastGuid === lang.id) { + resolve(givenLanguages); + } + } + ); + } + }); + } } diff --git a/src/DevHive.Angular/src/app/services/technology.service.ts b/src/DevHive.Angular/src/app/services/technology.service.ts index 4df0412..207303f 100644 --- a/src/DevHive.Angular/src/app/services/technology.service.ts +++ b/src/DevHive.Angular/src/app/services/technology.service.ts @@ -2,6 +2,7 @@ import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http'; import {Injectable} from '@angular/core'; import {Guid} from 'guid-typescript'; import {Observable} from 'rxjs'; +import {Technology} from 'src/models/identity/user'; import {AppConstants} from '../app-constants.module'; @Injectable({ @@ -16,4 +17,44 @@ export class TechnologyService { }; return this.http.get(AppConstants.API_TECHNOLOGY_URL, options); } + + getAllTechnologiesWithSessionStorageRequest(): Observable<object> { + const token = sessionStorage.getItem('UserCred') ?? ''; + return this.getAllTechnologiesRequest(token); + } + + getAllTechnologiesRequest(authToken: string): Observable<object> { + const options = { + headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) + }; + return this.http.get(AppConstants.API_TECHNOLOGY_URL + '/GetTechnologies', options); + } + + getFullTechnologiesFromIncomplete(givenTechnologies: Technology[]): Promise<Technology[]> { + if (givenTechnologies.length === 0) { + return new Promise(resolve => resolve(givenTechnologies)); + } + + // This accepts language array with incomplete languages, meaning + // languages that only have an id, but no name + return new Promise(resolve => { + const lastGuid = givenTechnologies[givenTechnologies.length - 1].id; + + // For each language, request his name and assign it + for (const tech of givenTechnologies) { + this.getTechnologyRequest(tech.id).subscribe( + (result: object) => { + // this only assigns the "name" property to the language, + // because only the name is returned from the request + Object.assign(tech, result); + + if (lastGuid === tech.id) { + resolve(givenTechnologies); + } + } + ); + } + }); + } + } diff --git a/src/DevHive.Angular/src/app/services/user.service.ts b/src/DevHive.Angular/src/app/services/user.service.ts index 5b4b63c..6badf94 100644 --- a/src/DevHive.Angular/src/app/services/user.service.ts +++ b/src/DevHive.Angular/src/app/services/user.service.ts @@ -94,7 +94,6 @@ export class UserService { } putUserRequest(userId: Guid, authToken: string, updateUserFormGroup: FormGroup): Observable<object> { - // TODO?: add a check for form data validity const body = { UserName: updateUserFormGroup.get('username')?.value, Email: updateUserFormGroup.get('email')?.value, @@ -104,8 +103,8 @@ export class UserService { // TODO: make the following fields dynamically selectable Roles: [ { Name: 'User' } ], Friends: [], - Languages: [], - Technologies: [] + Languages: updateUserFormGroup.get('languages')?.value, + Technologies: updateUserFormGroup.get('technologies')?.value }; const options = { params: new HttpParams().set('Id', userId.toString()), |
