mirror of
https://github.com/zoriya/Kyoo.Angular.git
synced 2026-08-15 18:43:37 +00:00
Starting the admin panel
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
import { PanelComponent } from "./panel/panel.component";
|
||||
import { ItemResolver } from "../services/item-resolver.service";
|
||||
|
||||
const routes: Routes = [
|
||||
{path: "admin", component: PanelComponent,
|
||||
resolve: {
|
||||
items: ItemResolver.forResource("config/panel"),
|
||||
config: ItemResolver.forResource("config")
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AdminRoutingModule { }
|
||||
@@ -0,0 +1,30 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { PanelComponent } from './panel/panel.component';
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { MatSidenavModule } from "@angular/material/sidenav";
|
||||
import { MatButtonModule } from "@angular/material/button";
|
||||
import { OptionComponent } from './option/option.component';
|
||||
import { SectionComponent } from './section/section.component';
|
||||
import { MatFormFieldModule } from "@angular/material/form-field";
|
||||
import { MatInputModule } from "@angular/material/input";
|
||||
import { MatIconModule } from "@angular/material/icon";
|
||||
import { MatToolbarModule } from "@angular/material/toolbar";
|
||||
import { MatTooltipModule } from "@angular/material/tooltip";
|
||||
|
||||
@NgModule({
|
||||
declarations: [PanelComponent, OptionComponent, SectionComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatSidenavModule,
|
||||
RouterModule,
|
||||
MatButtonModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatTooltipModule
|
||||
]
|
||||
})
|
||||
export class AdminModule
|
||||
{ }
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Option } from "./option";
|
||||
|
||||
export interface ConfigurationSection
|
||||
{
|
||||
name: string;
|
||||
options: Option[]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface Option
|
||||
{
|
||||
slug: string;
|
||||
name: string;
|
||||
description: string;
|
||||
helpMessage: string;
|
||||
type: number;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<form>
|
||||
<mat-form-field class="w-100 mb-2">
|
||||
<mat-label>{{option.name}}</mat-label>
|
||||
<input #input matInput value="{{value}}">
|
||||
<mat-hint align="start">{{option.description}}</mat-hint>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||
import { Option } from "../models/option";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
|
||||
@Component({
|
||||
selector: "app-option",
|
||||
templateUrl: "./option.component.html",
|
||||
styleUrls: ["./option.component.scss"]
|
||||
})
|
||||
export class OptionComponent implements OnInit, OnDestroy
|
||||
{
|
||||
value: string;
|
||||
@Input() option: Option;
|
||||
@Input() config;
|
||||
@ViewChild("input") input: ElementRef<HTMLInputElement>;
|
||||
|
||||
constructor(private http: HttpClient)
|
||||
{}
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.value = this.getConfig(this.option.slug);
|
||||
}
|
||||
|
||||
getConfig(slug: string): string
|
||||
{
|
||||
const sections = slug.split(':');
|
||||
let conf = this.config;
|
||||
|
||||
for (const child of sections)
|
||||
{
|
||||
if (conf == null)
|
||||
throw new Error(`Invalid configuration. No resource at ${slug}`);
|
||||
conf = conf[child];
|
||||
}
|
||||
return conf;
|
||||
}
|
||||
async ngOnDestroy()
|
||||
{
|
||||
if (this.value == this.input.nativeElement.value)
|
||||
return;
|
||||
await this.http.put(`/api/config/${this.option.slug}`, this.input.nativeElement.value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<mat-sidenav-container class="admin-panel" autosize>
|
||||
<mat-sidenav opened class="sidenav-container" mode="side" fixedInViewport fixedTopGap="64">
|
||||
<p *ngFor="let item of this.items">{{item.name}}</p>
|
||||
</mat-sidenav>
|
||||
|
||||
<mat-sidenav-content class="content">
|
||||
<mat-toolbar class="toolbar">
|
||||
<button mat-icon-button aria-label="Save changes" matTooltip="Save changes">
|
||||
<mat-icon>save</mat-icon>
|
||||
</button>
|
||||
<span>{{getChanges()}} changes</span>
|
||||
</mat-toolbar>
|
||||
<div>
|
||||
<app-section *ngFor="let item of this.items" [section]="item" [config]="config"></app-section>
|
||||
</div>
|
||||
</mat-sidenav-content>
|
||||
|
||||
</mat-sidenav-container>
|
||||
@@ -0,0 +1,36 @@
|
||||
@import "variables";
|
||||
|
||||
|
||||
.admin-panel
|
||||
{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidenav-container
|
||||
{
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content
|
||||
{
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
|
||||
> div
|
||||
{
|
||||
margin-top: $nav-bar-height;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar
|
||||
{
|
||||
position: fixed;
|
||||
top: $nav-bar-height;
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { ConfigurationSection } from "../models/configuration-section";
|
||||
|
||||
@Component({
|
||||
selector: "app-panel",
|
||||
templateUrl: "./panel.component.html",
|
||||
styleUrls: ["./panel.component.scss"]
|
||||
})
|
||||
export class PanelComponent
|
||||
{
|
||||
items: ConfigurationSection[];
|
||||
config: any;
|
||||
|
||||
constructor(private route: ActivatedRoute)
|
||||
{
|
||||
this.route.data.subscribe(x =>
|
||||
{
|
||||
this.items = x.items;
|
||||
this.config = x.config;
|
||||
});
|
||||
}
|
||||
|
||||
getChanges(): number
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<h3>{{section.name}}</h3>
|
||||
|
||||
<app-option *ngFor="let option of section.options" [option]="option" [config]="config"></app-option>
|
||||
<br/>
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { ConfigurationSection } from "../models/configuration-section";
|
||||
|
||||
@Component({
|
||||
selector: "app-section",
|
||||
templateUrl: "./section.component.html",
|
||||
styleUrls: ["./section.component.scss"]
|
||||
})
|
||||
export class SectionComponent
|
||||
{
|
||||
@Input() section: ConfigurationSection;
|
||||
@Input() config: any;
|
||||
}
|
||||
@@ -18,6 +18,10 @@
|
||||
<input placeholder="Search" id="search" type="search" (input)="onUpdateValue($any($event))"/>
|
||||
</li>
|
||||
|
||||
<li class="nav-item icon" routerLink="admin" routerLinkActive="active">
|
||||
<mat-icon matTooltipPosition="below" matTooltip="Configure Kyoo" >settings</mat-icon>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" *ngIf="!this.isAuthenticated else accountDrop">
|
||||
<a class="icon" (click)="this.authManager.login()" matTooltipPosition="below" matTooltip="Login">
|
||||
<mat-icon>account_circle</mat-icon>
|
||||
|
||||
@@ -111,7 +111,7 @@ input::-webkit-search-cancel-button
|
||||
opacity: 0.7;
|
||||
outline: none;
|
||||
|
||||
&:host-context(.hoverEnabled) &:hover, &:focus
|
||||
&:host-context(.hoverEnabled) &:hover, &:focus, &.active
|
||||
{
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
|
||||
@@ -48,6 +48,8 @@ import { ShowGridComponent } from "./components/show-grid/show-grid.component";
|
||||
import { MatBadgeModule } from "@angular/material/badge";
|
||||
import { StartupService } from "./services/startup.service";
|
||||
import { LongPressDirective } from "./misc/long-press.directive";
|
||||
import { AdminRoutingModule } from "./admin/admin-routing.module";
|
||||
import { AdminModule } from "./admin/admin.module";
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -77,6 +79,7 @@ import { LongPressDirective } from "./misc/long-press.directive";
|
||||
BrowserModule,
|
||||
HttpClientModule,
|
||||
AuthRoutingModule,
|
||||
AdminRoutingModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
MatSnackBarModule,
|
||||
@@ -97,6 +100,7 @@ import { LongPressDirective } from "./misc/long-press.directive";
|
||||
MatTabsModule,
|
||||
MatCheckboxModule,
|
||||
AuthModule,
|
||||
AdminModule,
|
||||
MatChipsModule,
|
||||
MatAutocompleteModule,
|
||||
MatExpansionModule,
|
||||
|
||||
Reference in New Issue
Block a user