diff --git a/dashboard/sharedComponents/page-content/page-content.component.ts b/dashboard/sharedComponents/page-content/page-content.component.ts index 9c7b5c14..ec9f0f86 100644 --- a/dashboard/sharedComponents/page-content/page-content.component.ts +++ b/dashboard/sharedComponents/page-content/page-content.component.ts @@ -81,11 +81,13 @@ export class PageContentComponent implements OnInit, AfterViewInit, OnDestroy { } ngOnInit() { + if(this.isBrowser) { + this.stickyBugWorkaround(); + } this.subscriptions.push(this.layoutService.isMobile.subscribe(isMobile => { this.isMobile = isMobile; - if (typeof document !== "undefined") { + if(this.isBrowser) { this.offset = this.isMobile?0:Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--header-height')); - this.stickyBugWorkaround(); } this.cdr.detectChanges(); })); diff --git a/dashboard/sharedComponents/sidebar/sideBar.component.html b/dashboard/sharedComponents/sidebar/sideBar.component.html index 6e4bd598..ad3f6fe9 100644 --- a/dashboard/sharedComponents/sidebar/sideBar.component.html +++ b/dashboard/sharedComponents/sidebar/sideBar.component.html @@ -14,6 +14,16 @@ +
+ +
+ +
+ {{backItem.title}} +
+
-
diff --git a/dashboard/sharedComponents/sidebar/sideBar.component.ts b/dashboard/sharedComponents/sidebar/sideBar.component.ts index 12a6fc88..49d0f622 100644 --- a/dashboard/sharedComponents/sidebar/sideBar.component.ts +++ b/dashboard/sharedComponents/sidebar/sideBar.component.ts @@ -26,7 +26,7 @@ export class SideBarComponent implements OnInit, AfterViewInit, OnDestroy, OnCha @Input() items: MenuItem[] = []; @Input() activeItem: string = ''; @Input() activeSubItem: string = ''; - @Input() specialMenuItem: MenuItem = null; + @Input() backItem: MenuItem = null; @Input() queryParamsHandling; @ViewChild("nav") nav: ElementRef; @ViewChild("sidebar_offcanvas") sidebar_offcanvas: ElementRef; diff --git a/fos/fos-selection/fos-selection.component.html b/fos/fos-selection/fos-selection.component.html new file mode 100644 index 00000000..eb877e23 --- /dev/null +++ b/fos/fos-selection/fos-selection.component.html @@ -0,0 +1,136 @@ +
+ +
+
+
+
+
+
+
+ +
+
+
+
+ + +
+
+
+
+
+
+
+
+
+ + +
+ + +
+
+
+
+ +
+
+

+ {{item.id}} + +

+
+
+
+
+

+ {{child.id}} + +

+
+ + +
+
+
+
+
+
+ +
+
+

+ +

+
+
+

+ +

+
+ + +
+
+
+
+
+
+
+
+ +
+

No results were found.

+
+
+
+
+
\ No newline at end of file diff --git a/fos/fos-selection/fos-selection.component.less b/fos/fos-selection/fos-selection.component.less new file mode 100644 index 00000000..e69de29b diff --git a/fos/fos-selection/fos-selection.component.ts b/fos/fos-selection/fos-selection.component.ts new file mode 100644 index 00000000..dc300c29 --- /dev/null +++ b/fos/fos-selection/fos-selection.component.ts @@ -0,0 +1,217 @@ +import {HttpClient} from "@angular/common/http"; +import {ChangeDetectorRef, Component, ElementRef, Input, ViewChild} from "@angular/core"; +import {FormBuilder, FormControl} from "@angular/forms"; +import {ActivatedRoute, Router} from "@angular/router"; +import {Subscription} from "rxjs"; +import {EnvProperties} from "../../utils/properties/env-properties"; +import {properties} from "../../../../environments/environment"; +import {StringUtils} from "../../utils/string-utils.class"; +import {debounceTime, distinctUntilChanged} from "rxjs/operators"; +import {HelperFunctions} from "../../utils/HelperFunctions.class"; +import Timeout = NodeJS.Timeout; + +declare var UIkit; + +@Component({ + selector: 'fos-selection', + templateUrl: 'fos-selection.component.html', + styleUrls: ['fos-selection.component.less'] +}) +export class FosSelectionComponent { + public properties: EnvProperties = properties; + @Input() subjects: string[]; + @Input() inModal: boolean = false; + @Input() contentHeight: number = 0; + @ViewChild("searchElement") searchElement: ElementRef; + + public loading: boolean; + public fos: any[] = []; + public fosOptions: Map; + public activeSection: string; + + public keywordControl: FormControl; + public keyword: string; + + public viewResults = []; + + public result = []; + + private subscriptions: Subscription[] = []; + private observer: IntersectionObserver; + private timeout: Timeout; + @ViewChild('tabs') tabs: ElementRef; + public sliderInit: boolean = false; + + constructor( + private httpClient: HttpClient, + private fb: FormBuilder, + private cdr: ChangeDetectorRef, + private route: ActivatedRoute, + private _router: Router + ) {} + + ngOnInit() { + this.loading = true; + this.httpClient.get(this.properties.domain+'/assets/common-assets/vocabulary/fos.json').subscribe(data => { + this.fos = data['fos']; + this.convertFosToOptions(); + this.convertFosToOptions(); + if (typeof document !== 'undefined') { + setTimeout(()=> { + let slider = UIkit.slider(this.tabs.nativeElement); + slider.clsActive = 'uk-slider-active'; + slider.updateActiveClasses(); + this.sliderInit = true; + slider.slides.forEach(item => { + item.classList.remove('uk-active'); + }); + if (this.route.snapshot.fragment) { + this.activeSection = this.route.snapshot.fragment; + let i = this.fos.findIndex(item => item.id == this.route.snapshot.fragment); + slider.show(i); + } else { + this.activeSection = this.fos[0].id; + } + + this.setObserver(); + this.cdr.detectChanges(); + }); + } + this.subscriptions.push(this.route.fragment.subscribe(fragment => { + if(fragment) { + this.activeSection = fragment; + if(this.tabs) { + let slider = UIkit.slider(this.tabs.nativeElement); + let i = this.fos.findIndex(item => item.id == fragment); + slider.show(i); + } + } else { + this.activeSection = this.fos[0].id; + } + })); + this.keywordControl = this.fb.control(''); + this.subscriptions.push(this.keywordControl.valueChanges.pipe(debounceTime(500), distinctUntilChanged()).subscribe(value => { + this.keyword = value; + this.findMatches(this.keyword); + if (typeof document !== 'undefined') { + setTimeout(() => { + this.setObserver(); + }); + } + })); + this.loading = false; + }); + } + + ngOnDestroy() { + for (let sub of this.subscriptions) { + sub.unsubscribe(); + } + if(this.observer) { + this.observer.disconnect(); + } + } + + private setObserver() { + if(this.observer) { + this.observer.disconnect(); + } + this.observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if(entry.isIntersecting) { + if(this.timeout) { + clearTimeout(this.timeout); + } + this.timeout = setTimeout(() => { + if(!this.inModal) { + this._router.navigate(['./'], { + fragment: entry.target.id, + relativeTo: this.route, + state: {disableScroll: true} + }); + } else { + this.activeSection = entry.target.id; + } + }, 200); + } + }); + }, {threshold: 0.1, rootMargin: '-100px'}); + this.fos.forEach(fos => { + let element = document.getElementById(fos.id); + if(element) { + this.observer.observe(element); + } + }); + } + + convertFosToOptions() { + this.fosOptions = new Map(); + this.fos.forEach(fos => { + this.fosOptions.set(fos.id, false); + if(fos.children) { + fos.children.forEach(child => { + this.fosOptions.set(child.id, false); + if(child.children) { + child.children.forEach(child2 => { + this.fosOptions.set(child2.id, this.subjects?.includes(child2.id)); + }); + } + }); + } + }); + } + + findMatches(value: string) { + this.viewResults = JSON.parse(JSON.stringify(this.fos)); + let matchLevel1: boolean = false; + let matchLevel2: boolean = false; + // 1st level search + if(this.viewResults.length) { + this.viewResults = this.viewResults.filter(item => { + matchLevel1 = !!item.id.includes(value?.toLowerCase()); + // // 2nd level search + if(item.children?.length && !matchLevel1) { + item.children = item.children.filter(subItem => { + matchLevel2 = !!subItem.id.includes(value?.toLowerCase()); + // 3rd level search + if(subItem.children?.length && !matchLevel2) { + subItem.children = subItem.children.filter(subSubItem => subSubItem.id.includes(value?.toLowerCase())); + } + return subItem.children?.length > 0 || matchLevel2; + }); + } + return item.children?.length > 0; + }); + } + } + + highlightKeyword(name) { + if(name.includes(this.keyword.toLowerCase())) { + return name.replace(new RegExp(this.keyword, "gi"), (matchedValue) => `${matchedValue}`); + } else { + return name; + } + } + + public urlEncodeAndQuote(str: string): string { + return StringUtils.quote(StringUtils.URIEncode(str)); + } + + public scrollToId(value: string) { + HelperFunctions.scrollToId(value); + this.activeSection = value; + } + + public getSelectedSubjects() { + let checked = Array.from(this.fosOptions, function (entry) { + return {id: entry[0], checked: entry[1]}; + }); + return checked.filter(sub => sub.checked == true); + } + + get calculatedHeight(): number { + if(this.contentHeight && this.searchElement) { + return this.contentHeight - this.searchElement.nativeElement.offsetHeight - 100; + } + } +} \ No newline at end of file diff --git a/fos/fos-selection/fos-selection.module.ts b/fos/fos-selection/fos-selection.module.ts new file mode 100644 index 00000000..8c6b40f5 --- /dev/null +++ b/fos/fos-selection/fos-selection.module.ts @@ -0,0 +1,25 @@ +import {CommonModule} from "@angular/common"; +import {NgModule} from "@angular/core"; +import {FormsModule} from "@angular/forms"; +import {InputModule} from "../../sharedComponents/input/input.module"; +import {SearchInputModule} from "../../sharedComponents/search-input/search-input.module"; +import {LoadingModule} from "../../utils/loading/loading.module"; +import {FosSelectionComponent} from './fos-selection.component'; + +@NgModule({ + imports: [ + CommonModule, FormsModule, LoadingModule, InputModule, SearchInputModule + ], + declarations: [ + FosSelectionComponent + ], + providers: [ + + ], + exports: [ + FosSelectionComponent + ] +}) +export class FosSelectionModule { + +} \ No newline at end of file diff --git a/sdg/sdg-selection/sdg-selection.component.html b/sdg/sdg-selection/sdg-selection.component.html new file mode 100644 index 00000000..6ebd36f4 --- /dev/null +++ b/sdg/sdg-selection/sdg-selection.component.html @@ -0,0 +1,30 @@ +
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/sdg/sdg-selection/sdg-selection.component.less b/sdg/sdg-selection/sdg-selection.component.less new file mode 100644 index 00000000..e69de29b diff --git a/sdg/sdg-selection/sdg-selection.component.ts b/sdg/sdg-selection/sdg-selection.component.ts new file mode 100644 index 00000000..1a9101a2 --- /dev/null +++ b/sdg/sdg-selection/sdg-selection.component.ts @@ -0,0 +1,50 @@ +import {HttpClient} from "@angular/common/http"; +import {Component, Input} from "@angular/core"; +import {properties} from "../../../../environments/environment"; +import {EnvProperties} from "../../utils/properties/env-properties"; +import {StringUtils} from "../../utils/string-utils.class"; + +@Component({ + selector: 'sdg-selection', + templateUrl: 'sdg-selection.component.html', + styleUrls: ['sdg-selection.component.less'] +}) +export class SdgSelectionComponent { + public properties: EnvProperties = properties; + @Input() subjects: string[]; + @Input() entityType: string; + + public loading: boolean; + public sdgs: any = []; + + constructor( + private httpClient: HttpClient + ) {} + + ngOnInit() { + this.loading = true; + this.httpClient.get(this.properties.domain+'/assets/common-assets/vocabulary/sdg.json').subscribe(data => { + data['sdg'].forEach(element => { + this.sdgs.push({code: element.code, id: element.id, label: element.label, html: element.html, checked: this.subjects?.includes(element.id)}); + }); + this.sdgs.push({code: '18', id: 'No SDGs are relevant for this ' + this.getEntityName(this.entityType), label: 'Not relevant', html: 'Not relevant', checked: false}); + this.loading = false; + }); + } + + public get firstColumn() { + return this.sdgs.slice(0, this.sdgs.length/2); + } + + public get secondColumn() { + return this.sdgs.slice(this.sdgs.length/2, this.sdgs.length); + } + + public getSelectedSubjects() { + return this.sdgs.filter(sub => sub.checked == true); + } + + private getEntityName (entityType:string) { + return StringUtils.getEntityName(entityType, false); + } +} \ No newline at end of file diff --git a/sdg/sdg-selection/sdg-selection.module.ts b/sdg/sdg-selection/sdg-selection.module.ts new file mode 100644 index 00000000..822b307a --- /dev/null +++ b/sdg/sdg-selection/sdg-selection.module.ts @@ -0,0 +1,24 @@ +import {CommonModule} from "@angular/common"; +import {NgModule} from "@angular/core"; +import {FormsModule} from "@angular/forms"; +import {InputModule} from "../../sharedComponents/input/input.module"; +import {LoadingModule} from "../../utils/loading/loading.module"; +import {SdgSelectionComponent} from "./sdg-selection.component"; + +@NgModule({ + imports: [ + CommonModule, FormsModule, LoadingModule, InputModule + ], + declarations: [ + SdgSelectionComponent + ], + providers: [ + + ], + exports: [ + SdgSelectionComponent + ] +}) +export class SdgSelectionModule { + +} \ No newline at end of file