1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-07-17 01:32:29 +02:00

Add basic extension UI #784

This commit is contained in:
Patrik J. Braun
2024-02-19 22:15:57 +01:00
parent 850fb7b48b
commit d33e29cd82
13 changed files with 175 additions and 127 deletions

11
package-lock.json generated
View File

@ -29,7 +29,7 @@
"sharp": "0.31.3", "sharp": "0.31.3",
"ts-exif-parser": "0.2.2", "ts-exif-parser": "0.2.2",
"ts-node-iptc": "1.0.11", "ts-node-iptc": "1.0.11",
"typeconfig": "2.1.2", "typeconfig": "2.2.7",
"typeorm": "0.3.12", "typeorm": "0.3.12",
"xml2js": "0.6.2" "xml2js": "0.6.2"
}, },
@ -20385,8 +20385,9 @@
} }
}, },
"node_modules/typeconfig": { "node_modules/typeconfig": {
"version": "2.1.2", "version": "2.2.7",
"license": "MIT", "resolved": "https://registry.npmjs.org/typeconfig/-/typeconfig-2.2.7.tgz",
"integrity": "sha512-xxMJky/XUsmWss8HM99uPeN+sZYF67AAht3Gajtnbp4k5bxBwplnahU+1N1GUKhmvFuqQoIQbiXsu9WpvznI1g==",
"dependencies": { "dependencies": {
"minimist": "1.2.8" "minimist": "1.2.8"
} }
@ -35319,7 +35320,9 @@
} }
}, },
"typeconfig": { "typeconfig": {
"version": "2.1.2", "version": "2.2.7",
"resolved": "https://registry.npmjs.org/typeconfig/-/typeconfig-2.2.7.tgz",
"integrity": "sha512-xxMJky/XUsmWss8HM99uPeN+sZYF67AAht3Gajtnbp4k5bxBwplnahU+1N1GUKhmvFuqQoIQbiXsu9WpvznI1g==",
"requires": { "requires": {
"minimist": "1.2.8" "minimist": "1.2.8"
} }

View File

@ -56,7 +56,7 @@
"sharp": "0.31.3", "sharp": "0.31.3",
"ts-exif-parser": "0.2.2", "ts-exif-parser": "0.2.2",
"ts-node-iptc": "1.0.11", "ts-node-iptc": "1.0.11",
"typeconfig": "2.1.2", "typeconfig": "2.2.7",
"typeorm": "0.3.12", "typeorm": "0.3.12",
"xml2js": "0.6.2" "xml2js": "0.6.2"
}, },

View File

@ -119,6 +119,7 @@ export class RenderingMWs {
skipTags: {secret: true} as TAGS skipTags: {secret: true} as TAGS
}) as PrivateConfigClass }) as PrivateConfigClass
); );
console.log(message.result.Extensions.extensions);
res.json(message); res.json(message);
} }

View File

@ -1,4 +1,4 @@
import {ConfigProperty, IConfigClass} from 'typeconfig/common'; import {IConfigClass} from 'typeconfig/common';
import {Config, PrivateConfigClass} from '../../../common/config/private/Config'; import {Config, PrivateConfigClass} from '../../../common/config/private/Config';
import {ConfigClassBuilder} from 'typeconfig/node'; import {ConfigClassBuilder} from 'typeconfig/node';
import {IExtensionConfig} from './IExtension'; import {IExtensionConfig} from './IExtension';
@ -32,22 +32,14 @@ export class ExtensionConfig<C> implements IExtensionConfig<C> {
constructor(private readonly extensionFolder: string) { constructor(private readonly extensionFolder: string) {
} }
private findConfig(config: PrivateConfigClass) { private findConfig(config: PrivateConfigClass): ServerExtensionsEntryConfig {
let c = (config.Extensions.extensions || []).find(e => e.path === this.extensionFolder); let c = (config.Extensions.extensions || []).find(e => e.path === this.extensionFolder);
if (!c) { if (!c) {
c = new ServerExtensionsEntryConfig(this.extensionFolder); c = new ServerExtensionsEntryConfig(this.extensionFolder);
config.Extensions.extensions.push(c); config.Extensions.extensions.push(c);
} }
return c;
if (!config.Extensions.extensions2[this.extensionFolder]) {
Object.defineProperty(config.Extensions.extensions2, this.extensionFolder,
ConfigProperty({type: ServerExtensionsEntryConfig})(config.Extensions.extensions2, this.extensionFolder));
// config.Extensions.extensions2[this.extensionFolder] = c as any;
config.Extensions.extensions2[this.extensionFolder] = c;
}
return config.Extensions.extensions2[this.extensionFolder];
} }
public getConfig(): C { public getConfig(): C {
@ -67,11 +59,12 @@ export class ExtensionConfig<C> implements IExtensionConfig<C> {
const confTemplate = ConfigClassBuilder.attachPrivateInterface(new this.template()); const confTemplate = ConfigClassBuilder.attachPrivateInterface(new this.template());
const extConf = this.findConfig(config); const extConf = this.findConfig(config);
// confTemplate.__loadJSONObject(Utils.clone(extConf.configs || {})); // confTemplate.__loadJSONObject(Utils.clone(extConf.configs || {}));
//extConf.configs = confTemplate; extConf.configs = confTemplate;
Object.defineProperty(config.Extensions.extensions2[this.extensionFolder].configs, this.extensionFolder, console.log(((config as any).toJSON({attachState: true})).Extensions.extensions);
/* Object.defineProperty(config.Extensions.extensions2[this.extensionFolder].configs, this.extensionFolder,
ConfigProperty({type: this.template})(config.Extensions.extensions2[this.extensionFolder], this.extensionFolder)); ConfigProperty({type: this.template})(config.Extensions.extensions2[this.extensionFolder], this.extensionFolder));
console.log(config.Extensions.extensions2[this.extensionFolder].configs); console.log(config.Extensions.extensions2[this.extensionFolder].configs);
config.Extensions.extensions2[this.extensionFolder].configs = confTemplate as any; config.Extensions.extensions2[this.extensionFolder].configs = confTemplate as any;
console.log(config.Extensions.extensions2[this.extensionFolder].configs); console.log(config.Extensions.extensions2[this.extensionFolder].configs);*/
} }
} }

View File

@ -118,10 +118,14 @@ export class ExtensionManager implements IObjectManager {
} }
if (fs.existsSync(packageJsonPath)) { if (fs.existsSync(packageJsonPath)) {
if (fs.existsSync(path.join(extPath, 'node_modules'))) {
Logger.debug(LOG_TAG, `node_modules folder exists. Skipping "npm install".`);
} else {
Logger.silly(LOG_TAG, `Running: "npm install --prefer-offline --no-audit --progress=false --omit=dev" in ${extPath}`); Logger.silly(LOG_TAG, `Running: "npm install --prefer-offline --no-audit --progress=false --omit=dev" in ${extPath}`);
await exec('npm install --no-audit --progress=false --omit=dev', { await exec('npm install --no-audit --progress=false --omit=dev', {
cwd: extPath cwd: extPath
}); });
}
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const pkg = require(packageJsonPath); const pkg = require(packageJsonPath);
if (pkg.name) { if (pkg.name) {

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-inferrable-types */ /* eslint-disable @typescript-eslint/no-inferrable-types */
import {ConfigProperty, SubConfigClass} from 'typeconfig/common'; import {ConfigProperty, SubConfigClass} from 'typeconfig/common';
import {ClientExtensionsConfig, ConfigPriority, TAGS} from '../../public/ClientConfig'; import {ClientExtensionsConfig, ConfigPriority, TAGS} from '../../public/ClientConfig';
import {IConfigClassPrivate} from '../../../../../node_modules/typeconfig/src/decorators/class/IConfigClass'; import {GenericConfigType} from 'typeconfig/src/GenericConfigType';
@SubConfigClass<TAGS>({softReadonly: true}) @SubConfigClass<TAGS>({softReadonly: true})
export class ServerExtensionsEntryConfig { export class ServerExtensionsEntryConfig {
@ -28,12 +28,13 @@ export class ServerExtensionsEntryConfig {
path: string = ''; path: string = '';
@ConfigProperty({ @ConfigProperty({
type: GenericConfigType,
tags: { tags: {
name: $localize`Config`, name: $localize`Config`,
priority: ConfigPriority.advanced priority: ConfigPriority.advanced
} }
}) })
configs: IConfigClassPrivate<unknown>; configs: GenericConfigType;
} }
@SubConfigClass<TAGS>({softReadonly: true}) @SubConfigClass<TAGS>({softReadonly: true})
@ -59,13 +60,6 @@ export class ServerExtensionsConfig extends ClientExtensionsConfig {
}) })
extensions: ServerExtensionsEntryConfig[] = []; extensions: ServerExtensionsEntryConfig[] = [];
@ConfigProperty({
tags: {
name: $localize`Installed extensions2`,
priority: ConfigPriority.advanced
}
})
extensions2: Record<string, ServerExtensionsEntryConfig> = {};
@ConfigProperty({ @ConfigProperty({
tags: { tags: {

View File

@ -11,7 +11,7 @@ declare let $localize: (s: TemplateStringsArray) => string;
if (typeof $localize === 'undefined') { if (typeof $localize === 'undefined') {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
global.$localize = (s) => s; global.$localize = (s) => s[0];
} }
@ -1034,7 +1034,6 @@ export class ThemesConfig {
name: $localize`Selected theme css`, //this is a 'hack' to the UI settings. UI will only show the selected setting's css name: $localize`Selected theme css`, //this is a 'hack' to the UI settings. UI will only show the selected setting's css
uiDisabled: (sb: ThemesConfig) => !sb.enabled, uiDisabled: (sb: ThemesConfig) => !sb.enabled,
relevant: (c: ThemesConfig) => c.selectedTheme !== 'default', relevant: (c: ThemesConfig) => c.selectedTheme !== 'default',
uiType: 'SelectedThemeSettings'
} as TAGS, } as TAGS,
description: $localize`Adds these css settings as it is to the end of the body tag of the page.` description: $localize`Adds these css settings as it is to the end of the body tag of the page.`
}) })

View File

@ -1,5 +1,15 @@
import {propertyTypes} from 'typeconfig/common'; import {propertyTypes} from 'typeconfig/common';
import {ClientGroupingConfig, ClientSortingConfig, SVGIconConfig} from '../../../../../common/config/public/ClientConfig'; import {
ClientGroupingConfig,
ClientSortingConfig,
MapLayers,
MapPathGroupConfig,
MapPathGroupThemeConfig,
NavigationLinkConfig,
SVGIconConfig,
ThemeConfig
} from '../../../../../common/config/public/ClientConfig';
import {JobScheduleConfig, UserConfig} from '../../../../../common/config/private/PrivateConfig';
/** /**
* Configuration in these class have a custom UI * Configuration in these class have a custom UI
@ -8,6 +18,13 @@ export class CustomSettingsEntries {
public static readonly entries = [ public static readonly entries = [
{c: ClientSortingConfig, name: 'ClientSortingConfig'}, {c: ClientSortingConfig, name: 'ClientSortingConfig'},
{c: ClientGroupingConfig, name: 'ClientGroupingConfig'}, {c: ClientGroupingConfig, name: 'ClientGroupingConfig'},
{c: MapLayers, name: 'MapLayers'},
{c: JobScheduleConfig, name: 'JobScheduleConfig'},
{c: UserConfig, name: 'UserConfig'},
{c: NavigationLinkConfig, name: 'NavigationLinkConfig'},
{c: MapPathGroupThemeConfig, name: 'MapPathGroupThemeConfig'},
{c: MapPathGroupConfig, name: 'MapPathGroupConfig'},
{c: ThemeConfig, name: 'ThemeConfig'},
{c: SVGIconConfig, name: 'SVGIconConfig'}, {c: SVGIconConfig, name: 'SVGIconConfig'},
]; ];
@ -46,7 +63,7 @@ export class CustomSettingsEntries {
return cN; return cN;
} }
public static iS(s: { tags?: { uiType?: string }, type?: propertyTypes }) { public static iS(s: { tags?: { uiType?: string }, type?: propertyTypes, arrayType?: propertyTypes }) {
const c = this.getConfigName(s); const c = this.getConfigName(s);
return this.entries.findIndex(e => e.name == c) !== -1; return this.entries.findIndex(e => e.name == c) !== -1;
} }

View File

@ -12,7 +12,6 @@
[hidden]="shouldHide"> [hidden]="shouldHide">
<label class="col-sm-3 col-md-2 control-label" [for]="idName">{{name}}</label> <label class="col-sm-3 col-md-2 control-label" [for]="idName">{{name}}</label>
<div class="col-12 col-sm"> <div class="col-12 col-sm">
<div class="input-group" [ngSwitch]="uiType"> <div class="input-group" [ngSwitch]="uiType">
<app-gallery-search-field <app-gallery-search-field
*ngSwitchCase="'SearchQuery'" *ngSwitchCase="'SearchQuery'"
@ -29,7 +28,9 @@
style="flex: 1" style="flex: 1"
*ngSwitchCase="'StringInput'"> *ngSwitchCase="'StringInput'">
<input <input
[type]="HTMLInputType" [min]="state.min" [max]="state.max" class="form-control" [type]="HTMLInputType"
[min]="state.min"
[max]="state.max" class="form-control"
[placeholder]="placeholder" [placeholder]="placeholder"
[title]="title" [title]="title"
[(ngModel)]="StringValue" [(ngModel)]="StringValue"
@ -131,7 +132,7 @@
<textarea <textarea
rows="5" rows="5"
*ngSwitchCase="'SelectedThemeSettings'" *ngSwitchCase="'ThemeConfig-Array'"
type="text" type="text"
class="form-control" class="form-control"
[title]="title" [title]="title"
@ -168,7 +169,7 @@
<app-settings-workflow <app-settings-workflow
class="w-100" class="w-100"
*ngSwitchCase="'JobScheduleConfig'" *ngSwitchCase="'JobScheduleConfig-Array'"
[(ngModel)]="state.value" [(ngModel)]="state.value"
[id]="idName" [id]="idName"
[name]="idName" [name]="idName"
@ -252,7 +253,7 @@
</ng-template> </ng-template>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'MapLayers'"> <ng-container *ngSwitchCase="'MapLayers-Array'">
<div class="container"> <div class="container">
<table class="table"> <table class="table">
<thead> <thead>
@ -295,7 +296,7 @@
</div> </div>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'MapPathGroupThemeConfig'"> <ng-container *ngSwitchCase="'MapPathGroupThemeConfig-Array'">
<div class="container"> <div class="container">
<div class="row mt-1 mb-1 bg-body-tertiary" *ngFor="let val of state.value; index as i; last as isLast"> <div class="row mt-1 mb-1 bg-body-tertiary" *ngFor="let val of state.value; index as i; last as isLast">
<div class="row mt-1 mb-1 bg-body-tertiary"> <div class="row mt-1 mb-1 bg-body-tertiary">
@ -357,7 +358,7 @@
</div> </div>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'MapPathGroupConfig'"> <ng-container *ngSwitchCase="'MapPathGroupConfig-Array'">
<div class="container"> <div class="container">
<div class="bg-body-tertiary row mt-1 mb-1" *ngFor="let arr of state.value; let i = index"> <div class="bg-body-tertiary row mt-1 mb-1" *ngFor="let arr of state.value; let i = index">
<div class="row mt-1 mb-1 me-0 ms-0 pe-0 ps-0"> <div class="row mt-1 mb-1 me-0 ms-0 pe-0 ps-0">
@ -403,7 +404,7 @@
</div> </div>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'NavigationLinkConfig'"> <ng-container *ngSwitchCase="'NavigationLinkConfig-Array'">
<div class="container"> <div class="container">
<div class="row mt-1 mb-1 bg-body-tertiary" *ngFor="let link of state.value; let i = index"> <div class="row mt-1 mb-1 bg-body-tertiary" *ngFor="let link of state.value; let i = index">
<div class="col ps-0"> <div class="col ps-0">
@ -468,7 +469,8 @@
</div> </div>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'UserConfig'">
<ng-container *ngSwitchCase="'UserConfig-Array'">
<div class="container ps-0 pe-0"> <div class="container ps-0 pe-0">
<div class="row ms-0 me-0 mt-1 mb-1 bg-body-tertiary" *ngFor="let item of state.value; let i = index"> <div class="row ms-0 me-0 mt-1 mb-1 bg-body-tertiary" *ngFor="let item of state.value; let i = index">

View File

@ -258,7 +258,7 @@ export class SettingsEntryComponent
this.arrayType !== 'MapPathGroupConfig' && this.arrayType !== 'MapPathGroupConfig' &&
this.arrayType !== 'ServerExtensionsEntryConfig' && this.arrayType !== 'ServerExtensionsEntryConfig' &&
this.arrayType !== 'MapPathGroupThemeConfig' && this.arrayType !== 'MapPathGroupThemeConfig' &&
this.arrayType !== 'JobScheduleConfig' && this.arrayType !== 'JobScheduleConfig-Array' &&
this.arrayType !== 'UserConfig') { this.arrayType !== 'UserConfig') {
this.uiType = 'StringInput'; this.uiType = 'StringInput';
} }

View File

@ -3,7 +3,7 @@
<div class="card-header"> <div class="card-header">
<h5 class="d-inline-block mb-0"> <h5 class="d-inline-block mb-0">
<ng-icon [name]="icon"></ng-icon> <ng-icon [name]="icon"></ng-icon>
{{Name}} {{ Name }}
</h5> </h5>
<div *ngIf="states.value.enabled !== undefined" class="float-end p-0"> <div *ngIf="states.value.enabled !== undefined" class="float-end p-0">
@ -22,7 +22,7 @@
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div> <div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{ error }}</div>
<ng-container *ngIf="states.value.enabled !== false"> <ng-container *ngIf="states.value.enabled !== false">
<ng-container <ng-container
*ngTemplateOutlet="Recursion; context:{ rStates: states,topLevel:true,confPath:ConfigPath,skipJobs:true }" *ngTemplateOutlet="Recursion; context:{ rStates: states,topLevel:true,confPath:ConfigPath,skipJobs:true }"
@ -31,7 +31,7 @@
<div class="panel-info" <div class="panel-info"
*ngIf="states.value.enabled === false"> *ngIf="states.value.enabled === false">
{{Name}} <span i18n>config is not supported with these settings.</span> {{ Name }} <span i18n>config is not supported with these settings.</span>
</div> </div>
<div class="row mt-2"> <div class="row mt-2">
<div class="col-auto" *ngIf="states.tags?.uiJob && !states.tags?.uiJob[0].description"> <div class="col-auto" *ngIf="states.tags?.uiJob && !states.tags?.uiJob[0].description">
@ -72,36 +72,60 @@
let-confPath="confPath"> let-confPath="confPath">
<div class="alert alert-secondary" role="alert" <div class="alert alert-secondary" role="alert"
*ngIf="rStates.description && settingsService.configStyle == ConfigStyle.full"> *ngIf="rStates.description && settingsService.configStyle == ConfigStyle.full">
<ng-icon size="1.3em" name="ionInformationCircleOutline"></ng-icon> {{rStates.description}} <ng-icon size="1.3em" name="ionInformationCircleOutline"></ng-icon>
{{ rStates.description }}
<a *ngIf="rStates.tags?.githubIssue" <a *ngIf="rStates.tags?.githubIssue"
[href]="'https://github.com/bpatrik/pigallery2/issues/'+rStates.tags?.githubIssue"> [href]="'https://github.com/bpatrik/pigallery2/issues/'+rStates.tags?.githubIssue">
<ng-container i18n>See</ng-container> <ng-container i18n>See</ng-container>
#{{rStates.tags?.githubIssue}}.</a> #{{ rStates.tags?.githubIssue }}.</a>
</div> </div>
<ng-container *ngFor="let ck of getKeys(rStates)"> <ng-container *ngFor="let ck of getKeys(rStates)">
<ng-container *ngIf="!(rStates.value.__state[ck].shouldHide && rStates.value.__state[ck].shouldHide())"> <ng-container *ngIf="!(rStates.value.__state[ck].shouldHide && rStates.value.__state[ck].shouldHide())">
<!-- is array -->
<ng-container *ngIf="rStates.value.__state[ck].isConfigArrayType && isExpandableArrayConfig(rStates.value.__state[ck])">
<div class="row">
<div class="col-auto">
<h5>{{ rStates?.value.__state[ck].tags?.name || ck }}</h5>
</div>
<div class="col mt-2">
<div class="row mt-2" *ngFor="let entry of rStates.value.__state[ck].value; let i = index">
<hr/>
<ng-container
*ngTemplateOutlet="Recursion; context:{ rStates: {value: rStates.value.__state[ck].value[i]}, confPath:confPath+'.'+ck+'.'+i }"
></ng-container>
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="!rStates.value.__state[ck].isConfigArrayType || !isExpandableArrayConfig(rStates.value.__state[ck])">
<!-- simple entries or complex once's but with custom UI--->
<app-settings-entry <app-settings-entry
*ngIf="(ck!=='enabled' || !topLevel) && !isExpandableConfig(rStates.value.__state[ck])" *ngIf="(ck!=='enabled' || !topLevel) && !isExpandableConfig(rStates.value.__state[ck])"
[name]="confPath+'_'+ck" [name]="confPath+'_'+ck"
[ngModel]="rStates?.value.__state[ck]"> [ngModel]="rStates?.value.__state[ck]">
</app-settings-entry> </app-settings-entry>
<!-- Config entries --->
<ng-container *ngIf="isExpandableConfig(rStates.value.__state[ck])"> <ng-container *ngIf="isExpandableConfig(rStates.value.__state[ck])">
<!-- Sub category with header and menu item -->
<div class="card mt-2 mb-2" *ngIf="topLevel && rStates?.value.__state[ck].tags?.uiIcon" <div class="card mt-2 mb-2" *ngIf="topLevel && rStates?.value.__state[ck].tags?.uiIcon"
[id]="ConfigPath+'.'+ck"> [id]="ConfigPath+'.'+ck">
<div class="card-body"> <div class="card-body">
<h5 class="card-title"> <h5 class="card-title">
<ng-icon [name]="rStates?.value.__state[ck].tags?.uiIcon"></ng-icon> <ng-icon [name]="rStates?.value.__state[ck].tags?.uiIcon"></ng-icon>
{{rStates?.value.__state[ck].tags?.name || ck}} {{ rStates?.value.__state[ck].tags?.name || ck }}
</h5> </h5>
<ng-container <ng-container
*ngTemplateOutlet="Recursion; context:{ rStates: rStates.value.__state[ck], confPath:confPath+'.'+ck }" *ngTemplateOutlet="Recursion; context:{ rStates: rStates.value.__state[ck], confPath:confPath+'.'+ck }"
></ng-container> ></ng-container>
</div> </div>
</div> </div>
<!-- Sub category without header and menu item -->
<ng-container *ngIf="!topLevel || !rStates?.value.__state[ck].tags?.uiIcon"> <ng-container *ngIf="!topLevel || !rStates?.value.__state[ck].tags?.uiIcon">
<div class="row mt-2"> <div class="row mt-2">
<div class="col-auto"> <div class="col-auto">
<h5>{{rStates?.value.__state[ck].tags?.name || ck}}</h5> <h5>{{ rStates?.value.__state[ck].tags?.name || ck }}</h5>
</div> </div>
<div class="col"> <div class="col">
<hr/> <hr/>
@ -116,6 +140,7 @@
</ng-container> </ng-container>
</ng-container> </ng-container>
</ng-container> </ng-container>
</ng-container>
<div *ngIf="rStates.tags?.uiJob && !skipJobs"> <div *ngIf="rStates.tags?.uiJob && !skipJobs">
<ng-container <ng-container
*ngTemplateOutlet="JobTemplate; context:{ uiJob: rStates.tags?.uiJob }" *ngTemplateOutlet="JobTemplate; context:{ uiJob: rStates.tags?.uiJob }"
@ -125,12 +150,14 @@
></ng-container> ></ng-container>
</div> </div>
</ng-template> </ng-template>
<ng-template #JobTemplate let-uiJob="uiJob"> <ng-template #JobTemplate let-uiJob="uiJob">
<div class="mb-2"> <div class="mb-2">
<ng-container *ngFor="let job of uiJob; let i = index"> <ng-container *ngFor="let job of uiJob; let i = index">
<div class="alert alert-secondary" role="alert" <div class="alert alert-secondary" role="alert"
*ngIf="job.description && settingsService.configStyle == ConfigStyle.full"> *ngIf="job.description && settingsService.configStyle == ConfigStyle.full">
<ng-icon size="1.3em" name="ionInformationCircleOutline"></ng-icon> {{job.description}} <ng-icon size="1.3em" name="ionInformationCircleOutline"></ng-icon>
{{ job.description }}
</div> </div>
<app-settings-job-button <app-settings-job-button
*ngIf="!job.relevant || job.relevant(settingsService.settings | async)" *ngIf="!job.relevant || job.relevant(settingsService.settings | async)"
@ -144,6 +171,7 @@
</div> </div>
</ng-template> </ng-template>
<ng-template #JobProcessTemplate let-uiJob="uiJob"> <ng-template #JobProcessTemplate let-uiJob="uiJob">
<div> <div>
<ng-container *ngFor="let job of uiJob"> <ng-container *ngFor="let job of uiJob">

View File

@ -271,10 +271,18 @@ export class TemplateComponent implements OnInit, OnChanges, OnDestroy, ISetting
this.getSettings(); this.getSettings();
} }
/**
* main template should list it
* @param c
*/
isExpandableConfig(c: ConfigState) { isExpandableConfig(c: ConfigState) {
return c.isConfigType && !CustomSettingsEntries.iS(c); return c.isConfigType && !CustomSettingsEntries.iS(c);
} }
isExpandableArrayConfig(c: ConfigState) {
return c.isConfigArrayType && !CustomSettingsEntries.iS(c);
}
public async save(): Promise<boolean> { public async save(): Promise<boolean> {
this.inProgress = true; this.inProgress = true;
@ -328,7 +336,6 @@ export class TemplateComponent implements OnInit, OnChanges, OnDestroy, ISetting
return 1; return 1;
} }
return (s[a].tags?.name as string || a).localeCompare(s[b].tags?.name || b); return (s[a].tags?.name as string || a).localeCompare(s[b].tags?.name || b);
}); });
states.keys = keys; states.keys = keys;
return states.keys; return states.keys;