From 99a055cf098fcd6644e51a26004f7cbaeddbffd5 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 10 Aug 2026 07:44:23 -0700 Subject: [PATCH] Frontend stuff, with sections --- .../admin/config/config.component.html | 71 ++++++++++--------- .../admin/config/config.component.spec.ts | 25 ++++++- .../admin/config/config.component.ts | 16 ++++- src-ui/src/app/data/paperless-config.ts | 39 ++++++++++ 4 files changed, 115 insertions(+), 36 deletions(-) diff --git a/src-ui/src/app/components/admin/config/config.component.html b/src-ui/src/app/components/admin/config/config.component.html index 7c8a65628..af0fe9e6f 100644 --- a/src-ui/src/app/components/admin/config/config.component.html +++ b/src-ui/src/app/components/admin/config/config.component.html @@ -14,43 +14,48 @@ {{category}}
-
- @for (option of getCategoryOptions(category); track option.key) { -
-
-
-
-
- {{option.title}} -
- - - - @if (isSet(option.key)) { - + @for (section of getCategorySections(category); track section) { + @if (section) { +
{{section}}
+ } +
+ @for (option of getCategoryOptions(category, section); track option.key) { +
+
+
+
+
+ {{option.title}} +
+ + + + @if (isSet(option.key)) { + + } +
+
+ @switch (option.type) { + @case (ConfigOptionType.Select) { } + @case (ConfigOptionType.Number) { } + @case (ConfigOptionType.Boolean) { } + @case (ConfigOptionType.String) { } + @case (ConfigOptionType.JSON) { } + @case (ConfigOptionType.File) { } + @case (ConfigOptionType.Password) { } + } +
+ @if (option.note) { +
{{option.note}}
}
-
- @switch (option.type) { - @case (ConfigOptionType.Select) { } - @case (ConfigOptionType.Number) { } - @case (ConfigOptionType.Boolean) { } - @case (ConfigOptionType.String) { } - @case (ConfigOptionType.JSON) { } - @case (ConfigOptionType.File) { } - @case (ConfigOptionType.Password) { } - } -
- @if (option.note) { -
{{option.note}}
- }
-
- } -
+ } +
+ }
diff --git a/src-ui/src/app/components/admin/config/config.component.spec.ts b/src-ui/src/app/components/admin/config/config.component.spec.ts index f4f4799a6..7925b6ad1 100644 --- a/src-ui/src/app/components/admin/config/config.component.spec.ts +++ b/src-ui/src/app/components/admin/config/config.component.spec.ts @@ -8,7 +8,11 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap' import { NgSelectModule } from '@ng-select/ng-select' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { of, throwError } from 'rxjs' -import { OutputTypeConfig } from 'src/app/data/paperless-config' +import { + ConfigCategory, + ConfigSection, + OutputTypeConfig, +} from 'src/app/data/paperless-config' import { ConfigService } from 'src/app/services/config.service' import { SettingsService } from 'src/app/services/settings.service' import { ToastService } from 'src/app/services/toast.service' @@ -158,4 +162,23 @@ describe('ConfigComponent', () => { component.resetOption('barcodes_enabled') expect(component.configForm.get('barcodes_enabled').value).toBeNull() }) + + it('should group options into sections within a category, or not', () => { + const sections = component.getCategorySections(ConfigCategory.OCR) + expect(sections).toEqual([null, ConfigSection.RemoteOCR]) + expect( + component + .getCategoryOptions(ConfigCategory.OCR) + .map((option) => option.key) + ).toContain('output_type') + expect( + component + .getCategoryOptions(ConfigCategory.OCR, ConfigSection.RemoteOCR) + .map((option) => option.key) + ).toEqual([ + 'remote_ocr_engine', + 'remote_ocr_api_key', + 'remote_ocr_endpoint', + ]) + }) }) diff --git a/src-ui/src/app/components/admin/config/config.component.ts b/src-ui/src/app/components/admin/config/config.component.ts index 9c2946438..0e3f432e2 100644 --- a/src-ui/src/app/components/admin/config/config.component.ts +++ b/src-ui/src/app/components/admin/config/config.component.ts @@ -74,8 +74,20 @@ export class ConfigComponent return Object.values(ConfigCategory) } - getCategoryOptions(category: string): ConfigOption[] { - return PaperlessConfigOptions.filter((o) => o.category === category) + getCategorySections(category: string): string[] { + return [ + ...new Set( + PaperlessConfigOptions.filter((o) => o.category === category).map( + (o) => o.section ?? null // null means no section + ) + ), + ] + } + + getCategoryOptions(category: string, section: string = null): ConfigOption[] { + return PaperlessConfigOptions.filter( + (o) => o.category === category && (o.section ?? null) === section + ) } initialConfig: PaperlessConfig diff --git a/src-ui/src/app/data/paperless-config.ts b/src-ui/src/app/data/paperless-config.ts index ad3f4c0c9..575525f1b 100644 --- a/src-ui/src/app/data/paperless-config.ts +++ b/src-ui/src/app/data/paperless-config.ts @@ -54,6 +54,10 @@ export const ConfigCategory = { AI: $localize`AI Settings`, } +export const ConfigSection = { + RemoteOCR: $localize`Remote OCR`, +} + export const LLMEmbeddingBackendConfig = { OPENAI_LIKE: 'openai-like', HUGGINGFACE: 'huggingface', @@ -65,6 +69,10 @@ export const LLMBackendConfig = { OLLAMA: 'ollama', } +export const RemoteOCREngineConfig = { + AZURE_AI: 'azureai', +} + export interface ConfigOption { key: string title: string @@ -72,6 +80,7 @@ export interface ConfigOption { choices?: Array<{ id: string; name: string }> config_key?: string category: string + section?: string note?: string } @@ -181,6 +190,33 @@ export const PaperlessConfigOptions: ConfigOption[] = [ config_key: 'PAPERLESS_OCR_USER_ARGS', category: ConfigCategory.OCR, }, + { + key: 'remote_ocr_engine', + title: $localize`Remote OCR Engine`, + type: ConfigOptionType.Select, + choices: mapToItems(RemoteOCREngineConfig), + config_key: 'PAPERLESS_REMOTE_OCR_ENGINE', + category: ConfigCategory.OCR, + section: ConfigSection.RemoteOCR, + note: $localize`Enabling remote OCR sends documents to a third-party service for processing. Consider the privacy implications as well as potential costs before enabling.`, + }, + { + key: 'remote_ocr_api_key', + title: $localize`Remote OCR API Key`, + type: ConfigOptionType.Password, + config_key: 'PAPERLESS_REMOTE_OCR_API_KEY', + category: ConfigCategory.OCR, + section: ConfigSection.RemoteOCR, + }, + { + key: 'remote_ocr_endpoint', + title: $localize`Remote OCR Endpoint`, + type: ConfigOptionType.String, + config_key: 'PAPERLESS_REMOTE_OCR_ENDPOINT', + category: ConfigCategory.OCR, + section: ConfigSection.RemoteOCR, + note: $localize`Required when using the Azure AI engine.`, + }, { key: 'app_logo', title: $localize`Application Logo`, @@ -398,6 +434,9 @@ export interface PaperlessConfig extends ObjectWithId { barcode_enable_tag: boolean barcode_tag_mapping: object barcode_tag_split: boolean + remote_ocr_engine: string + remote_ocr_api_key: string + remote_ocr_endpoint: string ai_enabled: boolean llm_embedding_backend: string llm_embedding_model: string