Implemented the ability to choose a folder for a library. Implemented an admin landing page that will showcase the different management items.
This commit is contained in:
parent
e06e34083c
commit
2679a52aec
20 changed files with 305 additions and 17 deletions
|
|
@ -99,7 +99,7 @@
|
|||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
"src/styles.scss",
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
|
|
|
|||
50
package-lock.json
generated
50
package-lock.json
generated
|
|
@ -353,6 +353,11 @@
|
|||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@angular/elements": {
|
||||
"version": "9.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@angular/elements/-/elements-9.1.12.tgz",
|
||||
"integrity": "sha512-0zjGlx2HxRWr4BiGfS9lY0i/MDfe2u3evn37svmTw72UmLsfaAR7XBa+5W2chgPCb5/1LyQAY/h02tsUEFlvYA=="
|
||||
},
|
||||
"@angular/forms": {
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-11.0.4.tgz",
|
||||
|
|
@ -1563,6 +1568,16 @@
|
|||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@circlon/angular-tree-component": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@circlon/angular-tree-component/-/angular-tree-component-10.0.1.tgz",
|
||||
"integrity": "sha512-yLXK01Z8TdtVnzcJbt8LVShtP2AFx8CA0Jwmj+ubHKyY5wOmO6pXebDdB1x0G9HEYYljl5wFhKoZLEZnG1tSAQ==",
|
||||
"requires": {
|
||||
"lodash-es": "^4.17.15",
|
||||
"mobx": "~4.14.1",
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@istanbuljs/schema": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
|
||||
|
|
@ -6562,6 +6577,31 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"jqwidgets-ng": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jqwidgets-ng/-/jqwidgets-ng-11.0.1.tgz",
|
||||
"integrity": "sha512-V4iFpZps7P9bdnXX8pn55tGjPCz1Wu7a5jEXzwlN9ICdR428Du4aldLyJPR1fClDNyqIMXpHsn+EADt16lsdgg==",
|
||||
"requires": {
|
||||
"@angular/cdk": "^9.1.1",
|
||||
"@angular/elements": "^9.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/cdk": {
|
||||
"version": "9.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-9.2.4.tgz",
|
||||
"integrity": "sha512-iw2+qHMXHYVC6K/fttHeNHIieSKiTEodVutZoOEcBu9rmRTGbLB26V/CRsfIRmA1RBk+uFYWc6UQZnMC3RdnJQ==",
|
||||
"requires": {
|
||||
"parse5": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"parse5": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
|
@ -7042,6 +7082,11 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
},
|
||||
"lodash-es": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz",
|
||||
"integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ=="
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
|
|
@ -7561,6 +7606,11 @@
|
|||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mobx": {
|
||||
"version": "4.14.1",
|
||||
"resolved": "https://registry.npmjs.org/mobx/-/mobx-4.14.1.tgz",
|
||||
"integrity": "sha512-Oyg7Sr7r78b+QPYLufJyUmxTWcqeQ96S1nmtyur3QL8SeI6e0TqcKKcxbG+sVJLWANhHQkBW/mDmgG5DDC4fdw=="
|
||||
},
|
||||
"move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
|
|
|
|||
23
src/app/_services/library.service.ts
Normal file
23
src/app/_services/library.service.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LibraryService {
|
||||
|
||||
baseUrl = environment.apiUrl;
|
||||
|
||||
constructor(private httpClient: HttpClient) { }
|
||||
|
||||
listDirectories(rootPath: string) {
|
||||
let query = '';
|
||||
if (rootPath !== undefined && rootPath.length > 0) {
|
||||
query = '?path=' + rootPath;
|
||||
}
|
||||
|
||||
return this.httpClient.get<string[]>(this.baseUrl + 'library/list' + query);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { AdminGuard } from '../_guards/admin.guard';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { UsersComponent } from './users/users.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '**', component: UsersComponent, pathMatch: 'full'},
|
||||
{path: '**', component: DashboardComponent, pathMatch: 'full'},
|
||||
{
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AdminGuard],
|
||||
children: [
|
||||
{path: '/dashboard', component: DashboardComponent},
|
||||
{path: '/users', component: UsersComponent}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,17 @@ import { CommonModule } from '@angular/common';
|
|||
import { AdminRoutingModule } from './admin-routing.module';
|
||||
import { UsersComponent } from './users/users.component';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [UsersComponent],
|
||||
declarations: [UsersComponent, DashboardComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AdminRoutingModule
|
||||
AdminRoutingModule,
|
||||
NgbNavModule
|
||||
],
|
||||
providers: []
|
||||
})
|
||||
|
|
|
|||
18
src/app/admin/dashboard/dashboard.component.html
Normal file
18
src/app/admin/dashboard/dashboard.component.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<h1>Admin Dashboard</h1>
|
||||
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
|
||||
<li *ngFor="let tab of tabs" [ngbNavItem]="tab">
|
||||
<a ngbNavLink>{{ tab | titlecase }}</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<ng-container *ngIf="tab === 'users'">
|
||||
<app-users></app-users>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab === 'libraries'">
|
||||
Library management here
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
0
src/app/admin/dashboard/dashboard.component.scss
Normal file
0
src/app/admin/dashboard/dashboard.component.scss
Normal file
19
src/app/admin/dashboard/dashboard.component.ts
Normal file
19
src/app/admin/dashboard/dashboard.component.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrls: ['./dashboard.component.scss']
|
||||
})
|
||||
export class DashboardComponent implements OnInit {
|
||||
|
||||
tabs = ['users', 'libraries'];
|
||||
counter = this.tabs.length + 1;
|
||||
active = this.tabs[0];
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,3 +1,11 @@
|
|||
|
||||
<button class="btn btn-primary" (click)="addFolder('')">Add Folder</button>
|
||||
|
||||
|
||||
<div class="container">
|
||||
<h2></h2>
|
||||
</div>
|
||||
|
||||
<h2>Members:</h2>
|
||||
<div class="container">
|
||||
<ul>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { ModalDismissReasons, NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { DirectoryPickerComponent, DirectoryPickerResult } from 'src/app/directory-picker/directory-picker.component';
|
||||
import { MemberService } from 'src/app/member.service';
|
||||
import { Member } from 'src/app/_models/member';
|
||||
|
||||
|
|
@ -10,8 +12,10 @@ import { Member } from 'src/app/_models/member';
|
|||
export class UsersComponent implements OnInit {
|
||||
|
||||
members: Member[] = [];
|
||||
closeResult = ''; // Debug code
|
||||
@ViewChild('content') content: any;
|
||||
|
||||
constructor(private memberService: MemberService) { }
|
||||
constructor(private memberService: MemberService, private modalService: NgbModal) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log('User Component');
|
||||
|
|
@ -20,4 +24,16 @@ export class UsersComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
addFolder(library: string) {
|
||||
|
||||
const modalRef = this.modalService.open(DirectoryPickerComponent);
|
||||
//modalRef.componentInstance.name = 'World';
|
||||
modalRef.closed.subscribe((closeResult: DirectoryPickerResult) => {
|
||||
console.log('Closed Result', closeResult);
|
||||
if (closeResult.success) {
|
||||
console.log('Add folder path to Library');
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ import { UserLoginComponent } from './user-login/user-login.component';
|
|||
import { ToastrModule } from 'ngx-toastr';
|
||||
import { ErrorInterceptor } from './_interceptors/error.interceptor';
|
||||
import { LibraryComponent } from './library/library.component';
|
||||
import { DirectoryPickerComponent } from './directory-picker/directory-picker.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
|
@ -21,7 +24,8 @@ import { LibraryComponent } from './library/library.component';
|
|||
HomeComponent,
|
||||
NavHeaderComponent,
|
||||
UserLoginComponent,
|
||||
LibraryComponent
|
||||
LibraryComponent,
|
||||
DirectoryPickerComponent
|
||||
],
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
|
|
@ -38,6 +42,7 @@ import { LibraryComponent } from './library/library.component';
|
|||
{provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true}
|
||||
],
|
||||
entryComponents: [DirectoryPickerComponent],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
|
|
|||
27
src/app/directory-picker/directory-picker.component.html
Normal file
27
src/app/directory-picker/directory-picker.component.html
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">Choose a Directory</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="close()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- <nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item {{'active' ? route === routeStack.peek() : ''}}" *ngFor="let route of routeStack.items">{{getStem(route)}}</li>
|
||||
</ol>
|
||||
</nav> -->
|
||||
<div class="list-group">
|
||||
<button *ngIf="routeStack.peek() !== undefined" (click)="goBack()" class="list-group-item list-group-item-action"><i class="fa fa-arrow-left mr-2"></i>Back</button>
|
||||
<button *ngFor="let folder of folders" class="list-group-item list-group-item-action" (click)="selectNode(folder)">
|
||||
<span>{{getStem(folder)}}</span>
|
||||
<button class="btn btn-primary pull-right" (click)="shareFolder(folder, $event)">Share</button>
|
||||
</button>
|
||||
<div class="text-center" *ngIf="folders.length === 0">
|
||||
There are no folders here
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" (click)="close()">Cancel</button>
|
||||
</div>
|
||||
0
src/app/directory-picker/directory-picker.component.scss
Normal file
0
src/app/directory-picker/directory-picker.component.scss
Normal file
103
src/app/directory-picker/directory-picker.component.ts
Normal file
103
src/app/directory-picker/directory-picker.component.ts
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { LibraryService } from '../_services/library.service';
|
||||
|
||||
class Stack {
|
||||
items: any[];
|
||||
|
||||
constructor() {
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return this.items.length === 0;
|
||||
}
|
||||
|
||||
peek() {
|
||||
if (!this.isEmpty()) {
|
||||
return this.items[this.items.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
pop() {
|
||||
if (this.isEmpty()) {
|
||||
return undefined;
|
||||
}
|
||||
return this.items.pop();
|
||||
}
|
||||
|
||||
push(item: any) {
|
||||
this.items.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
export interface DirectoryPickerResult {
|
||||
success: boolean;
|
||||
folderPath: string;
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-directory-picker',
|
||||
templateUrl: './directory-picker.component.html',
|
||||
styleUrls: ['./directory-picker.component.scss']
|
||||
})
|
||||
export class DirectoryPickerComponent implements OnInit {
|
||||
|
||||
currentRoot = '';
|
||||
folders: string[] = [];
|
||||
routeStack: Stack = new Stack();
|
||||
|
||||
constructor(public modal: NgbActiveModal, private libraryService: LibraryService) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadChildren(this.currentRoot);
|
||||
}
|
||||
|
||||
selectNode(folderName: string) {
|
||||
this.currentRoot = folderName;
|
||||
this.routeStack.push(folderName);
|
||||
this.loadChildren(folderName);
|
||||
}
|
||||
|
||||
goBack() {
|
||||
this.routeStack.pop();
|
||||
this.currentRoot = this.routeStack.peek();
|
||||
this.loadChildren(this.currentRoot);
|
||||
}
|
||||
|
||||
loadChildren(path: string) {
|
||||
this.libraryService.listDirectories(path).subscribe(folders => {
|
||||
this.folders = folders;
|
||||
});
|
||||
}
|
||||
|
||||
shareFolder(folderName: string, event: any) {
|
||||
console.log(`You selected ${folderName} as your folder to share!`);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.modal.close({success: true, folderPath: folderName});
|
||||
}
|
||||
|
||||
close() {
|
||||
this.modal.close({success: false, folderPath: undefined});
|
||||
}
|
||||
|
||||
getStem(path: string): string {
|
||||
|
||||
const lastPath = this.routeStack.peek();
|
||||
if (lastPath) {
|
||||
let replaced = path.replace(lastPath, '');
|
||||
if (replaced.startsWith('/') || replaced.startsWith('\\')) {
|
||||
replaced = replaced.substr(1, replaced.length);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,8 @@
|
|||
<app-user-login></app-user-login>
|
||||
</ng-container>
|
||||
|
||||
<app-directory-picker></app-directory-picker>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { User } from '../_models/user';
|
||||
import { AccountService } from '../_services/account.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-library',
|
||||
|
|
@ -7,9 +10,14 @@ import { Component, OnInit } from '@angular/core';
|
|||
})
|
||||
export class LibraryComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
user: User | undefined;
|
||||
|
||||
constructor(public accountService: AccountService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
this.user = user;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@
|
|||
|
||||
<!-- TODO: Put SignalR notification button dropdown here. -->
|
||||
|
||||
<div class=" nav-item dropdown" *ngIf="(accountService.currentUser$ | async) as user" dropdown>
|
||||
<a dropdownToggle class="dropdown-toggle text-light ml-2">{{user.username | titlecase}}</a>
|
||||
<div class="dropdown-menu mt-3" *dropdownMenu>
|
||||
<a *ngIf="user.isAdmin" class="dropdown-item" routerLink="/admin/users">Logout</a>
|
||||
<a (click)="logout()" class="dropdown-item">Logout</a>
|
||||
<div ngbDropdown class=" nav-item dropdown" *ngIf="(accountService.currentUser$ | async) as user" dropdown>
|
||||
<button class="btn btn-outline-primary" ngbDropdownToggle>{{user.username | titlecase}}</button>
|
||||
<div ngbDropdownMenu >
|
||||
<button ngbDropdownItem routerLink="/admin/users">Server Settings</button>
|
||||
<button ngbDropdownItem *ngIf="user.isAdmin"(click)="logout()">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@
|
|||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
<body class="mat-typography">
|
||||
<body class="mat-typography" theme="dark">
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -43,3 +43,5 @@
|
|||
// background-color: darken(#cc7b19, 10%) !important;
|
||||
// }
|
||||
// }
|
||||
html, body { height: 100%; }
|
||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue