aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/app/_elements/folder
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/app/_elements/folder')
-rw-r--r--frontend/src/app/_elements/folder/folder.component.css189
-rw-r--r--frontend/src/app/_elements/folder/folder.component.html99
-rw-r--r--frontend/src/app/_elements/folder/folder.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/folder/folder.component.ts276
4 files changed, 589 insertions, 0 deletions
diff --git a/frontend/src/app/_elements/folder/folder.component.css b/frontend/src/app/_elements/folder/folder.component.css
new file mode 100644
index 00000000..137a9643
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.css
@@ -0,0 +1,189 @@
+#folder {
+ /*position: absolute;
+ left: 50%;
+ transform: translateX(-50%);*/
+}
+
+#tabs {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ height: 3.1rem;
+}
+
+#tabs>.folder-tab:not(:first-child) {
+ margin-left: -5px;
+}
+
+.folder-tab {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ position: relative;
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-color: var(--ns-primary);
+ color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 0 1px;
+}
+
+.folder-tab:not(:first-child) {
+ margin-block-start: auto;
+}
+
+.selected-tab {
+ height: 3rem;
+ background-color: var(--ns-primary);
+ color: var(--offwhite);
+}
+
+.hover-tab {
+ height: 3.2rem;
+}
+
+.selected-tab,
+.hover-tab {
+ width: fit-content !important;
+}
+
+.tab-link {
+ color: var(--offwhite) !important;
+ text-decoration: none !important;
+ cursor: pointer;
+ padding: 0.5rem;
+}
+
+.tab-link:active {
+ text-decoration: underline !important;
+}
+
+.selected-tab {
+ background-color: var(--ns-primary);
+}
+
+#searchbar {
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-bottom: 1px solid var(--ns-primary);
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: flex-start;
+ width: 100%;
+}
+
+.collapse-horizontal {
+ white-space: nowrap;
+ height: 2.5rem;
+ overflow-x: hidden;
+}
+
+#search-options {
+ margin-left: auto;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ height: 100%;
+}
+
+#selected-content {
+ background-color: var(--ns-bg-dark-50);
+ width: 100%;
+ /*backdrop-filter: blur(2px);*/
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 1px 1px;
+ border-top-right-radius: 4px;
+}
+
+#footer {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+}
+
+.bottom-button {
+ font-size: large;
+ position: relative;
+ background-color: var(--ns-primary);
+ width: 10rem;
+ height: 2.3rem;
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 0px 1px 1px 1px;
+}
+
+.rounded-bottom {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.separator {
+ border-left-color: var(--ns-primary);
+ border-left-width: 1px;
+ border-left-style: solid;
+}
+
+.list-view {
+ height: 100%;
+ overflow-y: auto;
+}
+
+.list-item {
+ height: 3rem;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ border-bottom: 1px solid var(--ns-primary);
+}
+
+.list-item:hover {
+ background-color: var(--ns-bg-dark-100);
+ box-shadow: 0px 3px 3px var(--ns-primary);
+}
+
+.list-item:hover>.hover-hide {
+ display: none;
+}
+
+.folder-inside {
+ width: 100%;
+ height: 40rem;
+ overflow-y: auto;
+}
+
+.file-content {
+ width: 100%;
+ height: 92%;
+ position: relative;
+}
+
+.file-bottom-buttons {
+ position: absolute;
+ bottom: 15px;
+ right: 15px;
+ display: flex;
+ flex-direction: row-reverse;
+}
+
+.file-button {
+ position: relative;
+ color: var(--offwhite);
+ border-radius: 4px;
+ border: 1px solid var(--ns-primary);
+ margin: 5px;
+ padding: 5px;
+ cursor: pointer;
+ z-index: 1001;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+.file-button:hover {
+ background-color: var(--ns-primary);
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/folder/folder.component.html b/frontend/src/app/_elements/folder/folder.component.html
new file mode 100644
index 00000000..36f70c97
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.html
@@ -0,0 +1,99 @@
+<div id="folder">
+ <div id="tabs">
+ <div id="new-file-tab" class="folder-tab p-1 rounded-top" [style]="'z-index:' + (selectedTab == TabType.NewFile ? 11 : 10) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == TabType.NewFile, 'hover-tab' : hoverTab == TabType.NewFile}">
+ <mat-icon class="text-offwhite">add</mat-icon>
+ <a class="stretched-link tab-link" (click)="selectTab(TabType.NewFile)" (mouseenter)="hoverOverTab(TabType.NewFile)" (mouseleave)="hoverOverTab(TabType.None)">
+ {{tabTitles[TabType.NewFile]}}
+ </a>
+ </div>
+ <!--<div class="folder-tab p-1 rounded-top" *ngFor="let file of filteredFiles; let i = index" [style]="'z-index:' + calcZIndex(i) + ' ;'" [ngClass]="{'selected-tab' : selectedFileIndex == i, 'hover-tab' : hoveringOverFileIndex == i}">
+ <a class="m-1 stretched-link tab-link" (click)="selectFile(i)" (mouseenter)="hoverOverFile(i)" (mouseleave)="hoverOverFile(-1)">{{file.name}}</a>
+ </div>-->
+ <div class="folder-tab p-1 rounded-top" *ngFor="let tab of tabsToShow; let i = index" [style]="'z-index:' + (selectedTab == tab ? 11 : (tabsToShow.length - i)) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == tab, 'hover-tab' : hoverTab == tab}">
+ <a class="m-1 stretched-link tab-link" (click)="selectTab(tab)" (mouseenter)="hoverOverTab(tab)" (mouseleave)="hoverOverTab(TabType.None)">{{tabTitles[tab]}}</a>
+ </div>
+ </div>
+ <div id="selected-content" class="rounded-bottom text-offwhite">
+ <div id="searchbar" *ngIf="listView">
+ <!-- <div id="path" class="ps-2">{{folderName}}
+ </div>
+ <mat-icon>keyboard_arrow_right</mat-icon> -->
+ <div id="search" class="text-offwhite mx-1">
+ <mat-form-field>
+ <button matPrefix class="btn-clear input-icon"><mat-icon>search</mat-icon></button>
+ <input type="search" matInput name="search" [(ngModel)]="searchTerm" (input)="searchTermsChanged()">
+ <button matSuffix class="btn-clear input-icon" (click)="clearSearchTerm()"><mat-icon>clear</mat-icon></button>
+ </mat-form-field>
+ </div>
+ <div id="search-options">
+ <div id="collapseFilters" class="collapse collapse-horizontal">
+ <mat-icon class="text-offwhite ">timeline</mat-icon>
+ Regresioni
+ <mat-icon class="text-offwhite ">looks_two</mat-icon>
+ Binarni klasifikacioni
+ <mat-icon class="text-offwhite ">auto_awesome_motion</mat-icon>
+ Multiklasifikacioni
+ </div>
+ <button class="btn-clear icon-toggle" data-bs-toggle="collapse" data-bs-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters">
+ <mat-icon>filter_alt</mat-icon>
+ </button>
+ <div id="collapseSort" class="collapse collapse-horizontal">
+ [sort options here TODO]
+ </div>
+ <button class="btn-clear icon-toggle" data-bs-toggle="collapse" data-bs-target="#collapseSort" aria-expanded="false" aria-controls="collapseSort">
+ <mat-icon>sort</mat-icon>
+ </button>
+ <!-- <button class="btn-clear icon-toggle separator" [ngClass]="{'icon-toggle-on': listView}" (click)="toggleListView()">
+ <mat-icon>view_list</mat-icon>
+ </button> -->
+ </div>
+ </div>
+ <!--{{fileToDisplay ? fileToDisplay.name : 'No file selected.'}} {{selectedFileIndex}} {{hoveringOverFileIndex}}-->
+ <div [ngSwitch]="listView" class="folder-inside bg-blur">
+ <div class="file-content" [ngSwitch]="type" *ngSwitchCase="false">
+ <div class="file-bottom-buttons">
+ <button class="btn-clear file-button" (click)="deleteFile()">
+ <mat-icon>delete</mat-icon>
+ </button>
+ <button class="btn-clear file-button">
+ <mat-icon>link</mat-icon>
+ </button>
+ <button class="btn-clear file-button">
+ <mat-icon>zoom_out_map</mat-icon>
+ </button>
+ </div>
+ <app-form-model [forExperiment]="forExperiment" [model]="fileToDisplay" *ngSwitchCase="FolderType.Model"></app-form-model>
+ <app-form-dataset *ngSwitchCase="FolderType.Dataset" ></app-form-dataset>
+ </div>
+ <div *ngSwitchCase="true" class="list-view">
+ <div *ngFor="let file of filteredFiles; let i = index" class="list-item">
+ <div class="mx-2">
+ <a class="force-link" (click)="selectFile(i)">{{file.name}}</a>
+ </div>
+ <div class="mx-2 hover-hide">
+ {{file.lastUpdated | date}}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="footer" [ngSwitch]="newFileSelected">
+ <button mat-button (click)="saveNewFile()" class="bottom-button text-offwhite rounded-bottom" *ngSwitchCase="true">
+ <div class="f-row">
+ <div>Sačuvaj</div>
+ <div class="pt-1">
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ <button mat-button (click)="ok()" class="bottom-button text-offwhite rounded-bottom" *ngSwitchCase="false">
+ <div class="f-row">
+ <div>Ok</div>
+ <div class="icon-double pt-1">
+ <mat-icon>check</mat-icon>
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/folder/folder.component.spec.ts b/frontend/src/app/_elements/folder/folder.component.spec.ts
new file mode 100644
index 00000000..33a573a7
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FolderComponent } from './folder.component';
+
+describe('FolderComponent', () => {
+ let component: FolderComponent;
+ let fixture: ComponentFixture<FolderComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ FolderComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FolderComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts
new file mode 100644
index 00000000..06b4d893
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.ts
@@ -0,0 +1,276 @@
+import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
+import Dataset from 'src/app/_data/Dataset';
+import { FolderFile, FolderType } from 'src/app/_data/FolderFile';
+import Model from 'src/app/_data/Model';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+import shared from 'src/app/Shared';
+import { ModelsService } from 'src/app/_services/models.service';
+import { FormDatasetComponent } from '../form-dataset/form-dataset.component';
+import Experiment from 'src/app/_data/Experiment';
+import { ExperimentsService } from 'src/app/_services/experiments.service';
+import { PredictorsService } from 'src/app/_services/predictors.service';
+
+@Component({
+ selector: 'app-folder',
+ templateUrl: './folder.component.html',
+ styleUrls: ['./folder.component.css']
+})
+export class FolderComponent implements OnInit {
+
+ @ViewChild(FormDatasetComponent) formDataset?: FormDatasetComponent;
+
+ @Input() folderName: string = 'Moji podaci';
+
+ @Input() files!: FolderFile[]
+
+ newFile!: Dataset | Model;
+
+ @Input() type: FolderType = FolderType.Dataset;
+
+ @Input() forExperiment?: Experiment;
+
+ newFileSelected: boolean = true;
+
+ selectedFileIndex: number = -1;
+ selectedFile?: FolderFile;
+ hoveringOverFileIndex: number = -1;
+
+ fileToDisplay?: FolderFile;
+
+ @Output() selectedFileChanged: EventEmitter<FolderFile> = new EventEmitter();
+ @Output() okPressed: EventEmitter<string> = new EventEmitter();
+
+ searchTerm: string = '';
+
+ constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService) {
+ //PLACEHOLDER
+ this.forExperiment = new Experiment();
+ this.forExperiment.inputColumns = ['kolona1', 'kol2', '???', 'test'];
+
+ this.folders[TabType.File] = [];
+ this.folders[TabType.NewFile] = [];
+
+ this.refreshFiles();
+
+
+ }
+
+ ngOnInit(): void {
+ if (this.files.length > 0)
+ this.selectFile(0);
+ else {
+ this.selectNewFile();
+ }
+ }
+
+ displayFile(){
+ if(this.type == FolderType.Dataset)
+ this.formDataset!.dataset = <Dataset>this.fileToDisplay;
+ }
+
+ hoverOverFile(i: number) {
+ this.hoveringOverFileIndex = i;
+ if (i != -1) {
+ this.fileToDisplay = this.files[i];
+ } else {
+ if (this.newFileSelected) {
+ this.fileToDisplay = this.newFile;
+ } else {
+ this.fileToDisplay = this.files[this.selectedFileIndex];
+ }
+ }
+ this.displayFile();
+ }
+
+ selectNewFile() {
+ if (!this.newFile) {
+ this.createNewFile();
+ }
+ this.fileToDisplay = this.newFile;
+ this.selectedFile = this.newFile;
+ this.newFileSelected = true;
+ this.listView = false;
+ this.selectedFileChanged.emit(this.newFile);
+ this.displayFile();
+ }
+
+ selectFile(index: number) {
+ this.selectedFile = this.filteredFiles[index];
+ this.fileToDisplay = this.filteredFiles[index];
+ this.newFileSelected = false;
+ this.listView = false;
+ this.selectedFileChanged.emit(this.selectedFile);
+ this.displayFile();
+ }
+
+ createNewFile() {
+ if (this.type == FolderType.Dataset) {
+ this.newFile = new Dataset();
+ } else if (this.type == FolderType.Model) {
+ this.newFile = new Model();
+ }
+ }
+
+ ok() {
+ this.okPressed.emit();
+ }
+
+ refreshFiles(){
+ this.datasetsService.getMyDatasets().subscribe((datasets) => {
+ this.folders[TabType.MyDatasets] = datasets;
+ });
+
+ this.datasetsService.getPublicDatasets().subscribe((datasets) => {
+ this.folders[TabType.PublicDatasets] = datasets;
+ });
+
+ this.modelsService.getMyModels().subscribe((models) => {
+ this.folders[TabType.MyModels] = models;
+ });
+
+ /*this.modelsService.getMyModels().subscribe((models) => {
+ this.folders[TabType.PublicModels] = models;
+ });*/
+ this.folders[TabType.PublicModels] = [];
+
+ this.experimentsService.getMyExperiments().subscribe((experiments) => {
+ this.folders[TabType.MyExperiments] = experiments;
+ });
+
+ this.files = [];
+
+ this.filteredFiles.length = 0;
+ this.filteredFiles.push(...this.files);
+
+ this.searchTermsChanged();
+
+ }
+
+ saveNewFile() {
+ if(this.type == FolderType.Dataset)
+ this.formDataset!.uploadDataset();
+ }
+
+ /*calcZIndex(i: number) {
+ let zIndex = (this.files.length - i - 1)
+ if (this.selectedFileIndex == i)
+ zIndex = this.files.length + 2;
+ if (this.hoveringOverFileIndex == i)
+ zIndex = this.files.length + 3;
+ return zIndex;
+ }
+
+ newFileZIndex() {
+ return (this.files.length + 1);
+ }*/
+
+ clearSearchTerm() {
+ this.searchTerm = '';
+ this.searchTermsChanged();
+ }
+
+ filteredFiles: FolderFile[] = [];
+
+ searchTermsChanged() {
+ this.filteredFiles.length = 0;
+ this.filteredFiles.push(...this.files.filter((file) => file.name.toLowerCase().includes(this.searchTerm.toLowerCase())));
+ if (this.selectedFile) {
+ if (!this.filteredFiles.includes(this.selectedFile)) {
+ this.selectFile(-1);
+ } else {
+ this.selectedFileIndex = this.filteredFiles.indexOf(this.selectedFile);
+ }
+ }
+ }
+
+ listView: boolean = false;
+
+ toggleListView() {
+ this.listView = !this.listView;
+ }
+
+ deleteFile() {
+ console.log('delete');
+ }
+
+ folders: { [tab: number]: FolderFile[] } = {};
+
+ tabTitles: { [tab: number]: string } = {
+ [TabType.File]: 'Fajl',
+ [TabType.NewFile]: 'Novi fajl',
+ [TabType.MyDatasets]: 'Moji izvori podataka',
+ [TabType.PublicDatasets]: 'Javni izvori podataka',
+ [TabType.MyModels]: 'Moje konfiguracije neuronske mreže',
+ [TabType.PublicModels]: 'Javne konfiguracije neuronske mreže',
+ [TabType.MyExperiments]: 'Eksperimenti',
+ };
+
+ FolderType = FolderType;
+
+ TabType = TabType;
+
+ @Input() tabsToShow: TabType[] = [
+ TabType.MyDatasets,
+ TabType.PublicDatasets,
+ TabType.MyModels,
+ TabType.PublicModels,
+ TabType.MyExperiments,
+ TabType.File
+ ]
+
+ @Input() selectedTab: TabType = TabType.NewFile;
+ hoverTab: TabType = TabType.None;
+
+ selectTab(tab: TabType) {
+ this.checkListView(tab);
+ this.selectedTab = tab;
+ this.files = this.folders[tab];
+
+ this.searchTermsChanged();
+ }
+
+ checkListView(tab: TabType) {
+ switch (tab) {
+ case TabType.File:
+ case TabType.NewFile:
+ case TabType.None:
+ this.listView = false;
+ break;
+ case TabType.MyExperiments:
+ case TabType.MyDatasets:
+ case TabType.MyModels:
+ case TabType.PublicDatasets:
+ case TabType.PublicModels:
+ this.listView = true;
+ break;
+ }
+ }
+
+ hoverOverTab(tab: TabType) {
+ this.checkListView(tab);
+ this.hoverTab = tab;
+ if (tab == TabType.None) {
+ this.checkListView(this.selectedTab);
+ this.files = this.folders[this.selectedTab];
+ } else {
+ this.files = this.folders[tab];
+ }
+ this.searchTermsChanged();
+ }
+}
+
+export enum Privacy {
+ Private,
+ Public
+}
+
+export enum TabType {
+ NewFile,
+ File,
+ MyDatasets,
+ PublicDatasets,
+ MyModels,
+ PublicModels,
+ MyExperiments,
+ None
+} \ No newline at end of file