mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 04:49:33 -07:00
feat: got the features page working
This commit is contained in:
parent
d6b5b85ba2
commit
669ac65d66
9 changed files with 92 additions and 28 deletions
|
@ -17,7 +17,11 @@ export class FeatureService extends ServiceHelpers {
|
||||||
return this.http.get<IFeatureEnablement[]>(this.url, {headers: this.headers});
|
return this.http.get<IFeatureEnablement[]>(this.url, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(feature: IFeatureEnablement): Observable<IFeatureEnablement[]> {
|
public enable(feature: IFeatureEnablement): Observable<IFeatureEnablement[]> {
|
||||||
return this.http.post<IFeatureEnablement[]>(this.url, JSON.stringify(feature), {headers: this.headers});
|
return this.http.post<IFeatureEnablement[]>(`${this.url}enable`, JSON.stringify(feature), {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
|
public disable(feature: IFeatureEnablement): Observable<IFeatureEnablement[]> {
|
||||||
|
return this.http.post<IFeatureEnablement[]>(`${this.url}disable`, JSON.stringify(feature), {headers: this.headers});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,21 +3,18 @@
|
||||||
<div class="small-middle-container">
|
<div class="small-middle-container">
|
||||||
<legend>Features</legend>
|
<legend>Features</legend>
|
||||||
|
|
||||||
|
<div class="row" style="margin-top:3em">
|
||||||
|
<div class="col">
|
||||||
|
<div *ngFor="let feature of features">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8">
|
<div class="col-1">
|
||||||
<div class="mat-table">
|
<mat-slide-toggle [checked]="feature.enabled" (change)="updateFeature($event, feature)" id="enable"></mat-slide-toggle>
|
||||||
<div class="mat-row" *ngFor="let feature of features">
|
</div>
|
||||||
<div class="mat-cell">{{feature.name}}</div>
|
<div class="col-2">
|
||||||
<div class="mat-cell">
|
<h3>{{feature.name}}</h3>
|
||||||
<span *ngIf="feature.enabled">
|
</div>
|
||||||
<i class="fas fa-check-circle" style="color:green;"></i>
|
|
||||||
</span>
|
|
||||||
<span *ngIf="!feature.enabled">
|
|
||||||
<i class="fas fa-times-circle" style="color:red;"></i>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
|
@ -0,0 +1,5 @@
|
||||||
|
.small-middle-container {
|
||||||
|
margin: auto;
|
||||||
|
width: 95%;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
import { FeaturesFacade } from "../../state/features";
|
import { FeaturesFacade } from "../../state/features";
|
||||||
import { IFeatureEnablement } from "../../interfaces";
|
import { IFeatureEnablement } from "../../interfaces";
|
||||||
|
import { MatSlideToggleChange } from "@angular/material/slide-toggle";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./features.component.html"
|
templateUrl: "./features.component.html",
|
||||||
|
styleUrls: ["./features.component.scss"]
|
||||||
})
|
})
|
||||||
export class FeaturesComponent implements OnInit {
|
export class FeaturesComponent implements OnInit {
|
||||||
|
|
||||||
|
@ -14,10 +16,17 @@ export class FeaturesComponent implements OnInit {
|
||||||
constructor(private readonly featuresFacade: FeaturesFacade) { }
|
constructor(private readonly featuresFacade: FeaturesFacade) { }
|
||||||
|
|
||||||
public async ngOnInit() {
|
public async ngOnInit() {
|
||||||
this.featuresFacade.features$().subscribe(x => this.features = x);
|
this.featuresFacade.features$().subscribe(x => {
|
||||||
|
this.features = x;
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enableFeature(feature: IFeatureEnablement) {
|
public updateFeature(change: MatSlideToggleChange, feature: IFeatureEnablement) {
|
||||||
firstValueFrom(this.featuresFacade.update(feature));
|
if (change.checked) {
|
||||||
|
firstValueFrom(this.featuresFacade.enable(feature));
|
||||||
|
} else {
|
||||||
|
firstValueFrom(this.featuresFacade.disable(feature));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<button mat-button [matMenuTriggerFor]="configurationmenu"><i class="fas fa-wrench" aria-hidden="true"></i> Configuration</button>
|
<button mat-button [matMenuTriggerFor]="configurationmenu"><i class="fas fa-wrench" aria-hidden="true"></i> Configuration</button>
|
||||||
<mat-menu #configurationmenu="matMenu">
|
<mat-menu #configurationmenu="matMenu">
|
||||||
<button mat-menu-item [routerLink]="['/Settings/Ombi']"><i class="far fa-grin-stars icon-spacing"></i> General</button>
|
<button mat-menu-item [routerLink]="['/Settings/Ombi']"><i class="far fa-grin-stars icon-spacing"></i> General</button>
|
||||||
|
<button mat-menu-item [routerLink]="['/Settings/Features']"><i class="fas fa-rocket icon-spacing"></i> Control Features</button>
|
||||||
<button mat-menu-item [routerLink]="['/Settings/Customization']"><i class="fas fa-paint-brush icon-spacing"></i> Customization</button>
|
<button mat-menu-item [routerLink]="['/Settings/Customization']"><i class="fas fa-paint-brush icon-spacing"></i> Customization</button>
|
||||||
<button mat-menu-item [routerLink]="['/Settings/LandingPage']"><i class="far fa-file icon-spacing"></i> Landing Page</button>
|
<button mat-menu-item [routerLink]="['/Settings/LandingPage']"><i class="far fa-file icon-spacing"></i> Landing Page</button>
|
||||||
<button mat-menu-item [routerLink]="['/Settings/Issues']"><i class="fas fa-exclamation-triangle icon-spacing"></i> Issues</button>
|
<button mat-menu-item [routerLink]="['/Settings/Issues']"><i class="fas fa-exclamation-triangle icon-spacing"></i> Issues</button>
|
||||||
|
|
|
@ -3,8 +3,13 @@ import { IFeatureEnablement } from "../../interfaces";
|
||||||
export class LoadFeatures {
|
export class LoadFeatures {
|
||||||
public static readonly type = '[Features] LoadAll';
|
public static readonly type = '[Features] LoadAll';
|
||||||
}
|
}
|
||||||
export class UpdateFeature {
|
export class EnableFeature {
|
||||||
public static readonly type = '[Features] Update';
|
public static readonly type = '[Features] Enable';
|
||||||
|
|
||||||
|
constructor(public feature: IFeatureEnablement) { }
|
||||||
|
}
|
||||||
|
export class DisableFeature {
|
||||||
|
public static readonly type = '[Features] Disable';
|
||||||
|
|
||||||
constructor(public feature: IFeatureEnablement) { }
|
constructor(public feature: IFeatureEnablement) { }
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { LoadFeatures, UpdateFeature } from "./features.actions";
|
import { DisableFeature, EnableFeature, LoadFeatures } from "./features.actions";
|
||||||
|
|
||||||
import { FeaturesSelectors } from "./features.selectors";
|
import { FeaturesSelectors } from "./features.selectors";
|
||||||
import { IFeatureEnablement } from "../../interfaces";
|
import { IFeatureEnablement } from "../../interfaces";
|
||||||
|
@ -15,7 +15,9 @@ export class FeaturesFacade {
|
||||||
|
|
||||||
public features$ = (): Observable<IFeatureEnablement[]> => this.store.select(FeaturesSelectors.features);
|
public features$ = (): Observable<IFeatureEnablement[]> => this.store.select(FeaturesSelectors.features);
|
||||||
|
|
||||||
public update = (feature: IFeatureEnablement): Observable<unknown> => this.store.dispatch(new UpdateFeature(feature));
|
public enable = (feature: IFeatureEnablement): Observable<unknown> => this.store.dispatch(new EnableFeature(feature));
|
||||||
|
|
||||||
|
public disable = (feature: IFeatureEnablement): Observable<unknown> => this.store.dispatch(new DisableFeature(feature));
|
||||||
|
|
||||||
public loadFeatures = (): Observable<unknown> => this.store.dispatch(new LoadFeatures());
|
public loadFeatures = (): Observable<unknown> => this.store.dispatch(new LoadFeatures());
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Action, State, StateContext } from "@ngxs/store";
|
import { Action, State, StateContext } from "@ngxs/store";
|
||||||
import { LoadFeatures, UpdateFeature } from "./features.actions";
|
import { DisableFeature, EnableFeature, LoadFeatures } from "./features.actions";
|
||||||
|
|
||||||
import { FEATURES_STATE_TOKEN } from "./types";
|
import { FEATURES_STATE_TOKEN } from "./types";
|
||||||
import { FeatureService } from "../../services/feature.service";
|
import { FeatureService } from "../../services/feature.service";
|
||||||
|
@ -24,9 +24,16 @@ export class FeatureState {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Action(UpdateFeature)
|
@Action(EnableFeature)
|
||||||
public update({ setState }: StateContext<IFeatureEnablement[]>, { feature }: UpdateFeature): Observable<IFeatureEnablement[]> {
|
public enable({ setState }: StateContext<IFeatureEnablement[]>, { feature }: EnableFeature): Observable<IFeatureEnablement[]> {
|
||||||
return this.featuresService.update(feature).pipe(
|
return this.featuresService.enable(feature).pipe(
|
||||||
|
tap((result) => setState(result))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Action(DisableFeature)
|
||||||
|
public disable({ setState }: StateContext<IFeatureEnablement[]>, { feature }: DisableFeature): Observable<IFeatureEnablement[]> {
|
||||||
|
return this.featuresService.disable(feature).pipe(
|
||||||
tap((result) => setState(result))
|
tap((result) => setState(result))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Ombi.Attributes;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Settings.Settings.Models;
|
using Ombi.Settings.Settings.Models;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
@ -24,6 +25,39 @@ namespace Ombi.Controllers.V2
|
||||||
return PopulateFeatures(features?.Features ?? new List<FeatureEnablement>());
|
return PopulateFeatures(features?.Features ?? new List<FeatureEnablement>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("enable")]
|
||||||
|
[Admin]
|
||||||
|
public async Task<List<FeatureEnablement>> Enable([FromBody] FeatureEnablement feature)
|
||||||
|
{
|
||||||
|
var featureSettings = await _features.GetSettingsAsync();
|
||||||
|
var features = PopulateFeatures(featureSettings?.Features ?? new List<FeatureEnablement>());
|
||||||
|
var featureToUpdate = features.First(x => x.Name.Equals(feature.Name));
|
||||||
|
featureToUpdate.Enabled = true;
|
||||||
|
|
||||||
|
featureSettings.Features = features;
|
||||||
|
|
||||||
|
await _features.SaveSettingsAsync(featureSettings);
|
||||||
|
|
||||||
|
return PopulateFeatures(featureSettings?.Features ?? new List<FeatureEnablement>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("disable")]
|
||||||
|
[Admin]
|
||||||
|
public async Task<List<FeatureEnablement>> Disable([FromBody] FeatureEnablement feature)
|
||||||
|
{
|
||||||
|
var featureSettings = await _features.GetSettingsAsync();
|
||||||
|
var features = PopulateFeatures(featureSettings?.Features ?? new List<FeatureEnablement>());
|
||||||
|
var featureToUpdate = features.First(x => x.Name.Equals(feature.Name));
|
||||||
|
featureToUpdate.Enabled = false;
|
||||||
|
|
||||||
|
featureSettings.Features = features;
|
||||||
|
|
||||||
|
await _features.SaveSettingsAsync(featureSettings);
|
||||||
|
|
||||||
|
return PopulateFeatures(featureSettings?.Features ?? new List<FeatureEnablement>());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<FeatureEnablement> PopulateFeatures(List<FeatureEnablement> existingFeatures)
|
private List<FeatureEnablement> PopulateFeatures(List<FeatureEnablement> existingFeatures)
|
||||||
{
|
{
|
||||||
var supported = GetSupportedFeatures().ToList();
|
var supported = GetSupportedFeatures().ToList();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue