Fully dynamic config form

This commit is contained in:
shamoon 2023-12-22 09:10:02 -08:00
parent cf0dc73eb3
commit d7067dc355
4 changed files with 50 additions and 47 deletions

View File

@ -29,6 +29,7 @@
@case (ConfigOptionType.Number) { <pngx-input-number [formControlName]="option.key" [error]="errors[option.key]" [showAdd]="false"></pngx-input-number> }
@case (ConfigOptionType.Boolean) { <pngx-input-switch [formControlName]="option.key" [error]="errors[option.key]" title="Enable" i18n-title></pngx-input-switch> }
@case (ConfigOptionType.String) { <pngx-input-text [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-text> }
@case (ConfigOptionType.JSON) { <pngx-input-text [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-text> }
}
</div>
</div>

View File

@ -87,7 +87,7 @@ describe('ConfigComponent', () => {
it('should support discard changes', () => {
component.initialConfig = { output_type: OutputTypeConfig.PDF_A2 } as any
component.configForm.get('output_type').patchValue(OutputTypeConfig.PDF_A)
component.configForm.patchValue({ output_type: OutputTypeConfig.PDF_A })
component.discardChanges()
expect(component.configForm.get('output_type').value).toEqual(
OutputTypeConfig.PDF_A2
@ -95,9 +95,9 @@ describe('ConfigComponent', () => {
})
it('should support JSON validation for e.g. user_args', () => {
component.configForm.get('user_args').patchValue('{ foo bar }')
component.configForm.patchValue({ user_args: '{ foo bar }' })
expect(component.errors).toEqual({ user_args: 'Invalid JSON' })
component.configForm.get('user_args').patchValue('{ "foo": "bar" }')
component.configForm.patchValue({ user_args: '{ "foo": "bar" }' })
expect(component.errors).toEqual({ user_args: null })
})
})

View File

@ -31,22 +31,8 @@ export class ConfigComponent
{
public readonly ConfigOptionType = ConfigOptionType
public configForm = new FormGroup({
id: new FormControl(),
output_type: new FormControl(),
pages: new FormControl(),
language: new FormControl(),
mode: new FormControl(),
skip_archive_file: new FormControl(),
image_dpi: new FormControl(),
unpaper_clean: new FormControl(),
deskew: new FormControl(),
rotate_pages: new FormControl(),
rotate_pages_threshold: new FormControl(),
max_image_pixels: new FormControl(),
color_conversion_strategy: new FormControl(),
user_args: new FormControl(),
})
// generated dynamically
public configForm = new FormGroup({})
public errors = {}
@ -72,6 +58,10 @@ export class ConfigComponent
private toastService: ToastService
) {
super()
this.configForm.addControl('id', new FormControl())
PaperlessConfigOptions.forEach((option) => {
this.configForm.addControl(option.key, new FormControl())
})
}
ngOnInit(): void {
@ -90,27 +80,32 @@ export class ConfigComponent
},
})
// validate JSON input for user_args
this.configForm
.get('user_args')
.addValidators((control: AbstractControl) => {
if (!control.value || control.value.toString().length === 0) return null
try {
JSON.parse(control.value)
} catch (e) {
return [
{
user_args: e,
},
]
}
return null
// validate JSON inputs
PaperlessConfigOptions.filter(
(o) => o.type === ConfigOptionType.JSON
).forEach((option) => {
this.configForm
.get(option.key)
.addValidators((control: AbstractControl) => {
if (!control.value || control.value.toString().length === 0)
return null
try {
JSON.parse(control.value)
} catch (e) {
return [
{
user_args: e,
},
]
}
return null
})
this.configForm.get(option.key).statusChanges.subscribe((status) => {
this.errors[option.key] =
status === 'INVALID' ? $localize`Invalid JSON` : null
})
this.configForm.get('user_args').statusChanges.subscribe((status) => {
this.errors['user_args'] =
status === 'INVALID' ? $localize`Invalid JSON` : null
this.configForm.get(option.key).updateValueAndValidity()
})
this.configForm.get('user_args').updateValueAndValidity()
}
ngOnDestroy(): void {
@ -131,7 +126,6 @@ export class ConfigComponent
this.isDirty$ = dirtyCheck(this.configForm, this.store.asObservable())
}
this.configForm.patchValue(config)
this.initialConfig = config

View File

@ -42,6 +42,7 @@ export enum ConfigOptionType {
Number = 'number',
Select = 'select',
Boolean = 'boolean',
JSON = 'json',
}
export const ConfigCategory = {
@ -52,12 +53,12 @@ export interface ConfigOption {
key: string
title: string
type: ConfigOptionType
choices?: Array<string>
choices?: Array<{ id: string; name: string }>
config_key?: string
category: string
}
function mapToFlatChoices(enumObj: Object): Array<any> {
function mapToItems(enumObj: Object): Array<{ id: string; name: string }> {
return Object.keys(enumObj).map((key) => {
return {
id: enumObj[key],
@ -71,10 +72,17 @@ export const PaperlessConfigOptions: ConfigOption[] = [
key: 'output_type',
title: $localize`Output Type`,
type: ConfigOptionType.Select,
choices: mapToFlatChoices(OutputTypeConfig),
choices: mapToItems(OutputTypeConfig),
config_key: 'PAPERLESS_OCR_OUTPUT_TYPE',
category: ConfigCategory.OCR,
},
{
key: 'language',
title: $localize`Language`,
type: ConfigOptionType.String,
config_key: 'PAPERLESS_OCR_LANGUAGE',
category: ConfigCategory.OCR,
},
{
key: 'pages',
title: $localize`Pages`,
@ -86,7 +94,7 @@ export const PaperlessConfigOptions: ConfigOption[] = [
key: 'mode',
title: $localize`Mode`,
type: ConfigOptionType.Select,
choices: mapToFlatChoices(ModeConfig),
choices: mapToItems(ModeConfig),
config_key: 'PAPERLESS_OCR_MODE',
category: ConfigCategory.OCR,
},
@ -94,7 +102,7 @@ export const PaperlessConfigOptions: ConfigOption[] = [
key: 'skip_archive_file',
title: $localize`Skip Archive File`,
type: ConfigOptionType.Select,
choices: mapToFlatChoices(ArchiveFileConfig),
choices: mapToItems(ArchiveFileConfig),
config_key: 'PAPERLESS_OCR_SKIP_ARCHIVE_FILE',
category: ConfigCategory.OCR,
},
@ -109,7 +117,7 @@ export const PaperlessConfigOptions: ConfigOption[] = [
key: 'unpaper_clean',
title: $localize`Clean`,
type: ConfigOptionType.Select,
choices: mapToFlatChoices(CleanConfig),
choices: mapToItems(CleanConfig),
config_key: 'PAPERLESS_OCR_CLEAN',
category: ConfigCategory.OCR,
},
@ -145,14 +153,14 @@ export const PaperlessConfigOptions: ConfigOption[] = [
key: 'color_conversion_strategy',
title: $localize`Color Conversion Strategy`,
type: ConfigOptionType.Select,
choices: mapToFlatChoices(ColorConvertConfig),
choices: mapToItems(ColorConvertConfig),
config_key: 'PAPERLESS_OCR_COLOR_CONVERSION_STRATEGY',
category: ConfigCategory.OCR,
},
{
key: 'user_args',
title: $localize`OCR Arguments`,
type: ConfigOptionType.String,
type: ConfigOptionType.JSON,
config_key: 'PAPERLESS_OCR_USER_ARGS',
category: ConfigCategory.OCR,
},