1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-01-26 05:27:35 +02:00

Improving config UI #569

This commit is contained in:
Patrik J. Braun 2022-12-30 23:44:53 +01:00
parent 36d4641e9d
commit 875d120df8
14 changed files with 265 additions and 194 deletions

View File

@ -1,14 +1,10 @@
import {
ConfigTemplateEntry,
DefaultsJobs,
} from '../../../../common/entities/job/JobDTO';
import {ConfigTemplateEntry, DefaultsJobs,} from '../../../../common/entities/job/JobDTO';
import * as path from 'path';
import * as fs from 'fs';
import {Job} from './Job';
import {ProjectPath} from '../../../ProjectPath';
import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing';
import {VideoProcessing} from '../../fileprocessing/VideoProcessing';
import {DiskMangerWorker} from '../../threading/DiskMangerWorker';
import {GPXProcessing} from '../../fileprocessing/GPXProcessing';
export class TempFolderCleaningJob extends Job {

View File

@ -331,8 +331,9 @@ export class ServerGPXCompressingConfig extends ClientGPXCompressingConfig {
{
name: $localize`Min distance`,
priority: ConfigPriority.underTheHood,
unit: 'm',
uiDisabled: (sc: ServerGPXCompressingConfig, c: ServerConfig) => !c.Map.enabled || !sc.enabled || !c.MetaFile.gpx
},
} as TAGS,
description: $localize`Filters out entry that are closer than this in meters.`
})
minDistance: number = 5;
@ -356,8 +357,12 @@ export class ServerMetaFileConfig extends ClientMetaFileConfig {
tags:
{
name: $localize`GPX compression`,
priority: ConfigPriority.advanced
}
priority: ConfigPriority.advanced,
uiJob: [{
job: DefaultsJobs[DefaultsJobs['GPX Compression']],
relevant: (c) => c.MetaFile.GPXCompressing.enabled
}]
} as TAGS
})
GPXCompressing: ServerGPXCompressingConfig = new ServerGPXCompressingConfig();
}
@ -419,8 +424,8 @@ export class ServerIndexingConfig {
{
name: $localize`Exclude File List`,
priority: ConfigPriority.advanced,
uiOptional: true
uiOptional: true,
hint: $localize`.ignore;.pg2ignore`
} as TAGS,
description: $localize`Files that mark a folder to be excluded from indexing. Any folder that contains a file with this name will be excluded from indexing.`,
})
@ -610,7 +615,7 @@ export class ServerJobConfig {
DefaultsJobs[DefaultsJobs.Indexing],
DefaultsJobs[DefaultsJobs.Indexing],
new NeverJobTriggerConfig(),
{indexChangesOnly: true} // set config explicitly so it not undefined on the UI
{indexChangesOnly: true} // set config explicitly so it is not undefined on the UI
),
new JobScheduleConfig(
DefaultsJobs[DefaultsJobs['Preview Filling']],
@ -681,7 +686,7 @@ export class VideoTranscodingConfig {
tags:
{
name: $localize`FPS`,
priority: ConfigPriority.advanced,
priority: ConfigPriority.underTheHood,
uiOptions: [24, 25, 30, 48, 50, 60]
},
description: $localize`Target frame per second (fps) of the output video will be scaled down this this.`
@ -700,7 +705,7 @@ export class VideoTranscodingConfig {
tags:
{
name: $localize`MP4 codec`,
priority: ConfigPriority.advanced,
priority: ConfigPriority.underTheHood,
uiOptions: ['libx264', 'libx265'],
relevant: (c: any) => c.format === 'mp4'
}
@ -710,7 +715,7 @@ export class VideoTranscodingConfig {
tags:
{
name: $localize`Webm Codec`,
priority: ConfigPriority.advanced,
priority: ConfigPriority.underTheHood,
uiOptions: ['libvpx', 'libvpx-vp9'],
relevant: (c: any) => c.format === 'webm'
}
@ -782,6 +787,8 @@ export class PhotoConvertingConfig extends ClientPhotoConvertingConfig {
tags: {
name: $localize`Resolution`,
priority: ConfigPriority.advanced,
uiOptions: [720, 1080, 1440, 2160, 4320],
unit: 'px',
uiDisabled: (sc: PhotoConvertingConfig) =>
!sc.enabled
},
@ -867,23 +874,34 @@ export class ServerMediaConfig extends ClientMediaConfig {
@ConfigProperty({
tags: {
name: $localize`Video`,
priority: ConfigPriority.advanced
},
priority: ConfigPriority.advanced,
uiJob: [
{
job: DefaultsJobs[DefaultsJobs['Video Converting']],
relevant: (c) => c.Media.Video.enabled
}]
} as TAGS,
description: $localize`Video support uses ffmpeg. ffmpeg and ffprobe binaries need to be available in the PATH or the @ffmpeg-installer/ffmpeg and @ffprobe-installer/ffprobe optional node packages need to be installed.`
})
Video: ServerVideoConfig = new ServerVideoConfig();
@ConfigProperty({
tags: {
name: $localize`Photo`,
priority: ConfigPriority.advanced
}
priority: ConfigPriority.advanced,
uiJob: [
{
job: DefaultsJobs[DefaultsJobs['Photo Converting']],
relevant: (c) => c.Media.Photo.Converting.enabled
}]
} as TAGS
})
Photo: ServerPhotoConfig = new ServerPhotoConfig();
@ConfigProperty({
tags: {
name: $localize`Thumbnail`,
priority: ConfigPriority.advanced
}
priority: ConfigPriority.advanced,
uiJob: [{job: DefaultsJobs[DefaultsJobs['Thumbnail Generation']]}]
} as TAGS
})
Thumbnail: ServerThumbnailConfig = new ServerThumbnailConfig();
}
@ -965,34 +983,99 @@ export class ServerConfig extends ClientConfig {
@ConfigProperty({volatile: true})
Environment: ServerEnvironmentConfig = new ServerEnvironmentConfig();
@ConfigProperty()
@ConfigProperty({
tags: {
name: $localize`Server`,
uiIcon: 'cog'
} as TAGS,
})
Server: ServerServiceConfig = new ServerServiceConfig();
@ConfigProperty()
@ConfigProperty({
tags: {
name: $localize`Database`,
uiIcon: 'list'
} as TAGS
})
Database: ServerDataBaseConfig = new ServerDataBaseConfig();
@ConfigProperty()
@ConfigProperty({
tags: {
name: $localize`Users`,
uiIcon: 'person'
} as TAGS,
})
Users: ServerUserConfig = new ServerUserConfig();
@ConfigProperty()
@ConfigProperty({
tags: {
name: $localize`Indexing`,
uiIcon: 'pie-chart',
uiJob: [
{
job: DefaultsJobs[DefaultsJobs.Indexing],
description: $localize`If you add a new folder to your gallery, the site indexes it automatically. If you would like to trigger indexing manually, click index button. (Note: search only works among the indexed directories.)`
}, {
job: DefaultsJobs[DefaultsJobs['Database Reset']],
hideProgress: true
}]
} as TAGS
})
Indexing: ServerIndexingConfig = new ServerIndexingConfig();
@ConfigProperty()
@ConfigProperty({
tags: {
name: $localize`Media`,
uiIcon: 'camera-slr'
} as TAGS,
})
Media: ServerMediaConfig = new ServerMediaConfig();
@ConfigProperty()
@ConfigProperty({
tags: {
name: $localize`Meta file`,
uiIcon: 'file'
} as TAGS,
})
MetaFile: ServerMetaFileConfig = new ServerMetaFileConfig();
@ConfigProperty()
@ConfigProperty({
tags: {
name: $localize`Preview`,
uiIcon: 'image',
uiJob: [
{
job: DefaultsJobs[DefaultsJobs['Preview Filling']],
}, {
job: DefaultsJobs[DefaultsJobs['Preview Reset']],
hideProgress: true
}]
} as TAGS
})
Preview: ServerPreviewConfig = new ServerPreviewConfig();
@ConfigProperty()
@ConfigProperty({
tags: {
name: $localize`Sharing`,
uiIcon: 'share'
} as TAGS,
})
Sharing: ServerSharingConfig = new ServerSharingConfig();
@ConfigProperty()
@ConfigProperty({
tags: {
name: $localize`Duplicates`,
uiIcon: 'layers'
} as TAGS
})
Duplicates: ServerDuplicatesConfig = new ServerDuplicatesConfig();
@ConfigProperty()
@ConfigProperty({
tags: {
name: $localize`Jobs`,
uiIcon: 'project'
} as TAGS
})
Jobs: ServerJobConfig = new ServerJobConfig();
}

View File

@ -33,11 +33,18 @@ export type TAGS = {
secret?: boolean, // these config properties should never travel out of the server
experimental?: boolean, //is it a beta feature
unit?: string, // Unit info to display on UI
uiIcon?: string,
uiType?: 'SearchQuery', // Hint for the UI about the type
uiOptions?: (string | number)[], //Hint for the UI about the recommended options
uiAllowSpaces?: boolean
uiOptional?: boolean; //makes the tag not "required"
uiDisabled?: (subConfig: any, config: ClientConfig) => boolean
uiJob?: {
job: string,
hideProgress: boolean,
relevant?: (c: ClientConfig) => boolean,
description: string
}[]
};
@SubConfigClass<TAGS>({tags: {client: true}})
@ -278,7 +285,7 @@ export class ClientMapConfig {
tags: {
name: $localize`Max Preview Markers`,
priority: ConfigPriority.underTheHood
},
} as TAGS,
description: $localize`Maximum number of markers to be shown on the map preview on the gallery page.`,
})
maxPreviewMarkers: number = 50;
@ -289,7 +296,8 @@ export class ClientThumbnailConfig {
@ConfigProperty({
type: 'unsignedInt', max: 100,
tags: {
name: $localize`Max Preview Markers`,
name: $localize`Map Icon size`,
unit: 'px',
priority: ConfigPriority.underTheHood
},
description: $localize`Icon size (used on maps).`,
@ -298,6 +306,7 @@ export class ClientThumbnailConfig {
@ConfigProperty({
type: 'unsignedInt', tags: {
name: $localize`Person thumbnail size`,
unit: 'px',
priority: ConfigPriority.underTheHood
},
description: $localize`Person (face) thumbnail size.`,
@ -732,8 +741,9 @@ export class ClientServiceConfig {
description: $localize`If you access the page form local network its good to know the public url for creating sharing link.`,
tags: {
name: $localize`Page public url`,
hint: typeof window !== 'undefined' ? window?.origin : '',
uiOptional: true
}
} as TAGS
})
publicUrl: string = '';
@ -804,72 +814,57 @@ export class ClientUserConfig {
@SubConfigClass<TAGS>({tags: {client: true}})
export class ClientConfig {
@ConfigProperty({
tags: {
name: $localize`Server`
} as TAGS,
})
@ConfigProperty()
Server: ClientServiceConfig = new ClientServiceConfig();
@ConfigProperty({
tags: {
name: $localize`Users`
} as TAGS,
})
@ConfigProperty()
Users: ClientUserConfig = new ClientUserConfig();
@ConfigProperty({
tags: {
name: $localize`Gallery`
name: $localize`Gallery`,
uiIcon: 'browser'
} as TAGS,
})
Gallery: ClientGalleryConfig = new ClientGalleryConfig();
@ConfigProperty({
tags: {
name: $localize`Media`
} as TAGS,
})
@ConfigProperty()
Media: ClientMediaConfig = new ClientMediaConfig();
@ConfigProperty({
tags: {
name: $localize`Meta file`
} as TAGS,
})
@ConfigProperty()
MetaFile: ClientMetaFileConfig = new ClientMetaFileConfig();
@ConfigProperty({
tags: {
name: $localize`Album`
name: $localize`Album`,
uiIcon: 'grid-two-up'
} as TAGS,
})
Album: ClientAlbumConfig = new ClientAlbumConfig();
@ConfigProperty({
tags: {
name: $localize`Search`
name: $localize`Search`,
uiIcon: 'magnifying-glass'
} as TAGS,
})
Search: ClientSearchConfig = new ClientSearchConfig();
@ConfigProperty({
tags: {
name: $localize`Sharing`
} as TAGS,
})
@ConfigProperty()
Sharing: ClientSharingConfig = new ClientSharingConfig();
@ConfigProperty({
tags: {
name: $localize`Map`
name: $localize`Map`,
uiIcon: 'map-marker'
} as TAGS,
})
Map: ClientMapConfig = new ClientMapConfig();
@ConfigProperty({
tags: {
name: $localize`Faces`
name: $localize`Faces`,
uiIcon: 'people'
} as TAGS,
})
Faces: ClientFacesConfig = new ClientFacesConfig();
@ -877,6 +872,7 @@ export class ClientConfig {
@ConfigProperty({
tags: {
name: $localize`Random photo`,
uiIcon: 'random',
githubIssue: 392
} as TAGS,
description: $localize`This feature enables you to generate 'random photo' urls. That URL returns a photo random selected from your gallery. You can use the url with 3rd party application like random changing desktop background. Note: With the current implementation, random link also requires login.`

View File

@ -86,68 +86,68 @@
</div>
<div class="col-md-10">
<app-settings-template
*ngFor="let cp of configPaths"
#setting
#tmpl
icon="list"
[ConfigPath]="cp"
[hidden]="!tmpl.HasAvailableSettings"></app-settings-template>
<!-- <app-settings-template #setting #server
icon="list"
[ConfigPath]="'Server'"
[hidden]="!server.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #users
icon="person"
[ConfigPath]="'Users'"
[hidden]="!users.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #db
icon="list"
[ConfigPath]="'Database'"
[hidden]="!db.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #media
icon="camera-slr"
[ConfigPath]="'Media'"
[hidden]="!media.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #gallery
icon="browser"
[ConfigPath]="'Gallery'"
[hidden]="!gallery.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #preview
icon="image"
[ConfigPath]="'Preview'"
[hidden]="!preview.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #metafile
icon="file"
[ConfigPath]="'MetaFile'"
[hidden]="!metafile.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #map
icon="map-marker"
[ConfigPath]="'Map'"
[hidden]="!map.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #sharing
icon="share"
[ConfigPath]="'Sharing'"
[hidden]="!sharing.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #faces
icon="people"
[ConfigPath]="'Faces'"
[hidden]="!faces.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #album
icon="grid-two-up"
[ConfigPath]="'Album'"
[hidden]="!album.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #randomPhoto
icon="random"
[ConfigPath]="'RandomPhoto'"
[hidden]="!randomPhoto.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #duplicates
icon="layers"
[ConfigPath]="'Duplicates'"
[hidden]="!duplicates.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #indexing
icon="pie-chart"
[ConfigPath]="'Indexing'"
[hidden]="!indexing.HasAvailableSettings"></app-settings-template>-->
*ngFor="let cp of configPaths"
#setting
#tmpl
icon="list"
[ConfigPath]="cp"
[hidden]="!tmpl.HasAvailableSettings"></app-settings-template>
<!-- <app-settings-template #setting #server
icon="list"
[ConfigPath]="'Server'"
[hidden]="!server.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #users
icon="person"
[ConfigPath]="'Users'"
[hidden]="!users.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #db
icon="list"
[ConfigPath]="'Database'"
[hidden]="!db.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #media
icon="camera-slr"
[ConfigPath]="'Media'"
[hidden]="!media.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #gallery
icon="browser"
[ConfigPath]="'Gallery'"
[hidden]="!gallery.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #preview
icon="image"
[ConfigPath]="'Preview'"
[hidden]="!preview.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #metafile
icon="file"
[ConfigPath]="'MetaFile'"
[hidden]="!metafile.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #map
icon="map-marker"
[ConfigPath]="'Map'"
[hidden]="!map.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #sharing
icon="share"
[ConfigPath]="'Sharing'"
[hidden]="!sharing.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #faces
icon="people"
[ConfigPath]="'Faces'"
[hidden]="!faces.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #album
icon="grid-two-up"
[ConfigPath]="'Album'"
[hidden]="!album.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #randomPhoto
icon="random"
[ConfigPath]="'RandomPhoto'"
[hidden]="!randomPhoto.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #duplicates
icon="layers"
[ConfigPath]="'Duplicates'"
[hidden]="!duplicates.HasAvailableSettings"></app-settings-template>
<app-settings-template #setting #indexing
icon="pie-chart"
[ConfigPath]="'Indexing'"
[hidden]="!indexing.HasAvailableSettings"></app-settings-template>-->
</div>
</div>

View File

@ -49,8 +49,7 @@ export interface RecursiveState extends ConfigState {
@Directive()
export abstract class SettingsComponentDirective<
T extends RecursiveState> implements OnInit, OnDestroy, ISettingsComponent {
@Input() icon: string;
public icon: string;
@Input() ConfigPath: string;
@ViewChild('settingsForm', {static: true})
@ -72,7 +71,7 @@ export abstract class SettingsComponentDirective<
private navigation: NavigationService,
public settingsService: AbstractSettingsService,
protected notification: NotificationService,
protected globalSettingsService: SettingsService,
public globalSettingsService: SettingsService,
sliceFN?: (s: IWebConfigClassPrivate<TAGS> & WebConfig) => T
) {
this.setSliceFN(sliceFN);
@ -108,6 +107,7 @@ export abstract class SettingsComponentDirective<
if (state.volatile) {
return true;
}
if (state.tags &&
((state.tags.relevant && !state.tags.relevant(parent.value))
|| state.tags.secret)) {
@ -135,12 +135,11 @@ export abstract class SettingsComponentDirective<
return (
(state.tags?.priority > this.globalSettingsService.configPriority ||
(this.globalSettingsService.configPriority === ConfigPriority.basic &&
state.tags?.dockerSensitive && this.globalSettingsService.settings.value.Environment.isDocker)) && //if this value should not change in Docker, lets hide it
state.tags?.dockerSensitive && this.globalSettingsService.settings.value.Environment.isDocker)) && //if this value should not change in Docker, lets hide it
Utils.equalsFilter(state.value, state.default,
['__propPath', '__created', '__prototype', '__rootConfig']) &&
Utils.equalsFilter(state.original, state.default,
['__propPath', '__created', '__prototype', '__rootConfig'])
);
['__propPath', '__created', '__prototype', '__rootConfig']));
};
};
@ -164,6 +163,7 @@ export abstract class SettingsComponentDirective<
}
};
instrument(this.states, null);
this.icon = this.states.tags?.uiIcon;
};
onOptionChange = () => {

View File

@ -13,8 +13,6 @@
<div class="col-md-10">
<div class="input-group">
<app-gallery-search-field
*ngIf="Type === 'SearchQuery'"
[(ngModel)]="state.value"
@ -296,7 +294,9 @@
</div>
<small class="form-text text-muted" *ngIf="description">{{description}}
<span *ngIf="Type==='array' && (state.arrayType === 'string' || isNumberArray)" i18n>';' separated list.</span>
<a *ngIf="link" [href]="link">{{linkText || link}}</a>
<a *ngIf="state.tags?.githubIssue"
[href]="'https://github.com/bpatrik/pigallery2/issues/'+state.tags?.githubIssue">See
#{{state.tags?.githubIssue}}.</a>
</small>
<ng-content></ng-content>
</div>

View File

@ -1,22 +1,9 @@
import {Component, forwardRef, Input, OnChanges} from '@angular/core';
import {
ControlValueAccessor,
FormControl,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
} from '@angular/forms';
import {ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator,} from '@angular/forms';
import {Utils} from '../../../../../../common/Utils';
import {IConfigClass, propertyTypes} from 'typeconfig/common';
import {propertyTypes} from 'typeconfig/common';
import {SearchQueryParserService} from '../../../gallery/search/search-query-parser.service';
import {
ClientThumbnailConfig,
ConfigPriority,
MapLayers,
NavigationLinkConfig, NavigationLinkTypes,
TAGS
} from '../../../../../../common/config/public/ClientConfig';
import {MapLayers, NavigationLinkConfig, NavigationLinkTypes, TAGS} from '../../../../../../common/config/public/ClientConfig';
import {SettingsService} from '../../settings.service';
import {WebConfig} from '../../../../../../common/config/private/WebConfig';
import {JobScheduleConfig, UserConfig} from '../../../../../../common/config/private/PrivateConfig';
@ -61,16 +48,12 @@ interface IState {
})
export class SettingsEntryComponent
implements ControlValueAccessor, Validator, OnChanges {
@Input() defaultName: string;
name: string;
@Input() required: boolean;
@Input() dockerWarning: boolean;
@Input() placeholder: string;
@Input() allowSpaces = false;
@Input() description: string;
@Input() link: string;
@Input() linkText: string;
@Input() typeOverride: 'SearchQuery';
required: boolean;
dockerWarning: boolean;
placeholder: string;
allowSpaces = false;
description: string;
state: IState;
isNumberArray = false;
isNumber = false;
@ -89,13 +72,14 @@ export class SettingsEntryComponent
if (this.Disabled) {
return false;
}
if (this.state.isConfigArrayType) {
for (let i = 0; i < this.state.value?.length; ++i) {
for (const k of Object.keys(this.state.value[i].__state)) {
if (!Utils.equalsFilter(
this.state.value[i]?.__state[k]?.value,
this.state.default[i]?.__state[k]?.value,
['__propPath', '__created', '__prototype', '__rootConfig'])) {
['default', '__propPath', '__created', '__prototype', '__rootConfig'])) {
return true;
}
}
@ -128,7 +112,7 @@ export class SettingsEntryComponent
}
get Type(): string | object {
return this.typeOverride || this.state.tags?.uiType || this.state.type;
return this.state.tags?.uiType || this.state.type;
}
get ArrayType(): string {
@ -216,7 +200,7 @@ export class SettingsEntryComponent
this.title = $localize`readonly` + ', ';
}
this.title += $localize`default value` + ': ' + this.defaultStr;
this.name = this.state?.tags?.name || this.defaultName;
this.name = this.state?.tags?.name;
if (this.name) {
this.idName =
this.GUID + this.name.toLowerCase().replace(new RegExp(' ', 'gm'), '-');
@ -245,10 +229,6 @@ export class SettingsEntryComponent
if (typeof this.dockerWarning === 'undefined') {
this.dockerWarning = this.state.tags.dockerSensitive && this.settingsService.settings.value.Environment.isDocker;
}
if (this.state.tags.githubIssue) {
this.link = `https://github.com/bpatrik/pigallery2/issues/` + this.state.tags.githubIssue;
this.linkText = $localize`See` + ' ' + this.state.tags.githubIssue;
}
this.name = this.name || this.state.tags.name;
this.allowSpaces = this.allowSpaces || this.state.tags.uiAllowSpaces;
this.required = this.required || !this.state.tags.uiOptional;

View File

@ -1,3 +0,0 @@
:host {
display: inline-block;
}

View File

@ -1,3 +0,0 @@
.panel-info {
text-align: center;
}

View File

@ -24,9 +24,7 @@
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
<ng-container *ngIf="states.value.enabled !== false">
<div class="alert alert-secondary" role="alert" *ngIf="states.description">
{{states.description}}
</div>
<ng-container
*ngTemplateOutlet="Recursion; context:{ rStates: states,skipEnabled:true,confPath:ConfigPath }"
></ng-container>
@ -48,16 +46,18 @@
</div>
<ng-template #Recursion let-rStates="rStates" let-skipEnabled="skipEnabled" let-confPath="confPath">
<div class="alert alert-secondary" role="alert" *ngIf="rStates.description">
{{rStates.description}}
</div>
<ng-container *ngFor="let ck of getKeys(rStates)">
<ng-container *ngIf="!(rStates.value.__state[ck].shouldHide && rStates.value.__state[ck].shouldHide())">
<app-settings-entry
*ngIf="(ck!=='enabled' || !skipEnabled) && !rStates.value.__state[ck].isConfigType"
[name]="confPath+'_'+ck"
[defaultName]="ck"
[ngModel]="rStates?.value.__state[ck]">
</app-settings-entry>
<ng-container *ngIf="rStates.value.__state[ck].isConfigType">
<div class="row">
<div class="row mt-2">
<div class="col-auto">
<h5>{{rStates?.value.__state[ck].tags?.name || ck}}</h5>
</div>
@ -66,9 +66,6 @@
</div>
</div>
<div class="mt-2">
<div class="alert alert-secondary" role="alert" *ngIf="rStates.value.__state[ck]?.description">
{{rStates.value.__state[ck]?.description}}
</div>
<ng-container
*ngTemplateOutlet="Recursion; context:{ rStates: rStates.value.__state[ck], confPath:confPath+'.'+ck }"
></ng-container>
@ -76,6 +73,31 @@
</ng-container>
</ng-container>
</ng-container>
<div *ngIf="rStates.tags?.uiJob">
<ng-container *ngFor="let job of rStates.tags?.uiJob; let i = index">
<div class="alert alert-secondary" role="alert" *ngIf="job.description">
{{job.description}}
</div>
<app-settings-job-button
*ngIf="!job.relevant || job.relevant(globalSettingsService.settings | async)"
class="mt-2 mb-1 mb-md-0 mt-md-0 float-left me-2"
[soloRun]="true"
(jobError)="error=$event"
[allowParallelRun]="false"
[danger]="i>0"
[jobName]="job.job"></app-settings-job-button>
</ng-container>
<ng-container *ngFor="let job of rStates.tags?.uiJob">
<ng-container
*ngIf="getProgress(job.job) && !job.hideProgress && (!job.relevant || job.relevant(globalSettingsService.settings | async))">
<hr class="mt-1"/>
<app-settings-job-progress
class="d-block mb-2"
[progress]="getProgress(job.job)"></app-settings-job-progress>
</ng-container>
</ng-container>
</div>
</ng-template>
</form>

View File

@ -1,4 +1,4 @@
import {Component, Input, OnInit} from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {AuthenticationService} from '../../../model/network/authentication.service';
import {NavigationService} from '../../../model/navigation.service';
import {NotificationService} from '../../../model/notification.service';
@ -6,6 +6,9 @@ import {SettingsComponentDirective} from '../_abstract/abstract.settings.compone
import {SettingsService} from '../settings.service';
import {WebConfig} from '../../../../../common/config/private/WebConfig';
import {AbstractSettingsService} from '../_abstract/abstract.settings.service';
import {JobProgressDTO} from '../../../../../common/entities/job/JobProgressDTO';
import {JobDTOUtils} from '../../../../../common/entities/job/JobDTO';
import {ScheduledJobsService} from '../scheduled-jobs.service';
@Component({
@ -22,7 +25,8 @@ export class TemplateComponent extends SettingsComponentDirective<any> implement
navigation: NavigationService,
notification: NotificationService,
settingsService: AbstractSettingsService,
globalSettingsService: SettingsService
globalSettingsService: SettingsService,
public jobsService: ScheduledJobsService,
) {
super(
authService,
@ -66,4 +70,7 @@ export class TemplateComponent extends SettingsComponentDirective<any> implement
});
}
getProgress(jobName: string): JobProgressDTO {
return this.jobsService.progress.value[JobDTOUtils.getHashName(jobName)];
}
}

View File

@ -1,7 +0,0 @@
.form-control {
margin: 5px 0;
}
.panel-info {
text-align: center;
}

View File

@ -1,4 +0,0 @@
.buttons-row {
margin-top: 10px;
margin-bottom: 20px;
}

View File

@ -6,7 +6,6 @@ import {
JobScheduleDTO,
JobScheduleDTOUtils,
JobTriggerType,
PeriodicJobTrigger,
ScheduledJobTrigger
} from '../../../../../common/entities/job/JobScheduleDTO';
import {ScheduledJobsService} from '../scheduled-jobs.service';
@ -96,6 +95,9 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
}
atTimeLocal(atTime: number): Date {
if (!atTime) {
return null;
}
const d = new Date();
d.setUTCHours(Math.floor(atTime / 60));
d.setUTCMinutes(Math.floor(atTime % 60));
@ -156,6 +158,8 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
case JobTriggerType.periodic:
schedule.trigger = new PeriodicJobTriggerConfig();
schedule.trigger.periodicity = 7;
schedule.trigger.atTime = 0;
break;
case JobTriggerType.after: