Saving some work on frontend config
This commit is contained in:
parent
3e1a3aef4c
commit
6e03d9848c
@ -25,6 +25,7 @@ import { ConsumptionTemplatesComponent } from './components/manage/consumption-t
|
|||||||
import { MailComponent } from './components/manage/mail/mail.component'
|
import { MailComponent } from './components/manage/mail/mail.component'
|
||||||
import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component'
|
import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component'
|
||||||
import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component'
|
import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component'
|
||||||
|
import { ConfigComponent } from './components/admin/config/config.component'
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
|
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
|
||||||
@ -179,6 +180,17 @@ export const routes: Routes = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'config',
|
||||||
|
component: ConfigComponent,
|
||||||
|
canActivate: [PermissionsGuard],
|
||||||
|
data: {
|
||||||
|
requiredPermission: {
|
||||||
|
action: PermissionAction.View,
|
||||||
|
type: PermissionType.Admin,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'tasks',
|
path: 'tasks',
|
||||||
component: TasksComponent,
|
component: TasksComponent,
|
||||||
|
@ -108,6 +108,7 @@ import { ProfileEditDialogComponent } from './components/common/profile-edit-dia
|
|||||||
import { PdfViewerComponent } from './components/common/pdf-viewer/pdf-viewer.component'
|
import { PdfViewerComponent } from './components/common/pdf-viewer/pdf-viewer.component'
|
||||||
import { DocumentLinkComponent } from './components/common/input/document-link/document-link.component'
|
import { DocumentLinkComponent } from './components/common/input/document-link/document-link.component'
|
||||||
import { PreviewPopupComponent } from './components/common/preview-popup/preview-popup.component'
|
import { PreviewPopupComponent } from './components/common/preview-popup/preview-popup.component'
|
||||||
|
import { ConfigComponent } from './components/admin/config/config.component'
|
||||||
|
|
||||||
import localeAf from '@angular/common/locales/af'
|
import localeAf from '@angular/common/locales/af'
|
||||||
import localeAr from '@angular/common/locales/ar'
|
import localeAr from '@angular/common/locales/ar'
|
||||||
@ -263,6 +264,7 @@ function initializeApp(settings: SettingsService) {
|
|||||||
PdfViewerComponent,
|
PdfViewerComponent,
|
||||||
DocumentLinkComponent,
|
DocumentLinkComponent,
|
||||||
PreviewPopupComponent,
|
PreviewPopupComponent,
|
||||||
|
ConfigComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
116
src-ui/src/app/components/admin/config/config.component.html
Normal file
116
src-ui/src/app/components/admin/config/config.component.html
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<pngx-page-header title="Configuration" i18n-title></pngx-page-header>
|
||||||
|
|
||||||
|
<form [formGroup]="configForm" (ngSubmit)="saveConfig()" class="pb-4">
|
||||||
|
|
||||||
|
<h4 i18n>OCR Settings</h4>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Output Type</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-select [items]="ConfigChoices.output_type" formControlName="output_type" [allowNull]="true"></pngx-input-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Pages</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-number formControlName="pages" [showAdd]="false"></pngx-input-number>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Mode</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-select [items]="ConfigChoices.mode" formControlName="mode" [allowNull]="true"></pngx-input-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Skip Archive File</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-select [items]="ConfigChoices.skip_archive_file" formControlName="skip_archive_file" [allowNull]="true"></pngx-input-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Image DPI</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-number formControlName="image_dpi" [showAdd]="false"></pngx-input-number>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Clean</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-select [items]="ConfigChoices.unpaper_clean" formControlName="unpaper_clean" [allowNull]="true"></pngx-input-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Deskew</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-check formControlName="deskew"></pngx-input-check>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Rotate Pages</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-check formControlName="rotate_pages"></pngx-input-check>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Rotate Pages Threshold</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-number formControlName="rotate_pages_threshold" [showAdd]="false"></pngx-input-number>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Max Image Pixels</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-number formControlName="max_image_pixels" [showAdd]="false"></pngx-input-number>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Color Conversion Strategy</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-select [items]="ConfigChoices.color_conversion_strategy" formControlName="color_conversion_strategy" [allowNull]="true"></pngx-input-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>OCR Arguments</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-text formControlName="user_args"></pngx-input-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary mb-2" [disabled]="loading ||(isDirty$ | async) === false" i18n>Save</button>
|
||||||
|
</form>
|
@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { ConfigComponent } from './config.component'
|
||||||
|
|
||||||
|
describe('ConfigComponent', () => {
|
||||||
|
let component: ConfigComponent
|
||||||
|
let fixture: ComponentFixture<ConfigComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ConfigComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ConfigComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
108
src-ui/src/app/components/admin/config/config.component.ts
Normal file
108
src-ui/src/app/components/admin/config/config.component.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||||
|
import { FormControl, FormGroup } from '@angular/forms'
|
||||||
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
|
Observable,
|
||||||
|
Subject,
|
||||||
|
Subscription,
|
||||||
|
takeUntil,
|
||||||
|
} from 'rxjs'
|
||||||
|
import {
|
||||||
|
ArchiveFileConfig,
|
||||||
|
CleanConfig,
|
||||||
|
ColorConvertConfig,
|
||||||
|
ModeConfig,
|
||||||
|
OutputTypeConfig,
|
||||||
|
PaperlessConfig,
|
||||||
|
} from 'src/app/data/paperless-config'
|
||||||
|
import { ConfigService } from 'src/app/services/config.service'
|
||||||
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
|
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
||||||
|
import { DirtyComponent, dirtyCheck } from '@ngneat/dirty-check-forms'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'pngx-config',
|
||||||
|
templateUrl: './config.component.html',
|
||||||
|
styleUrl: './config.component.scss',
|
||||||
|
})
|
||||||
|
export class ConfigComponent
|
||||||
|
extends ComponentWithPermissions
|
||||||
|
implements OnInit, OnDestroy, DirtyComponent
|
||||||
|
{
|
||||||
|
public ConfigChoices = {
|
||||||
|
output_type: Object.values(OutputTypeConfig),
|
||||||
|
mode: Object.values(ModeConfig),
|
||||||
|
skip_archive_file: Object.values(ArchiveFileConfig),
|
||||||
|
unpaper_clean: Object.values(CleanConfig),
|
||||||
|
color_conversion_strategy: Object.values(ColorConvertConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
public configForm = new FormGroup({
|
||||||
|
output_type: new FormControl(null),
|
||||||
|
pages: new FormControl(null),
|
||||||
|
language: new FormControl(null),
|
||||||
|
mode: new FormControl(null),
|
||||||
|
skip_archive_file: new FormControl(null),
|
||||||
|
image_dpi: new FormControl(null),
|
||||||
|
unpaper_clean: new FormControl(null),
|
||||||
|
deskew: new FormControl(null),
|
||||||
|
rotate_pages: new FormControl(null),
|
||||||
|
rotate_pages_threshold: new FormControl(null),
|
||||||
|
max_image_pixels: new FormControl(null),
|
||||||
|
color_conversion_strategy: new FormControl(null),
|
||||||
|
user_args: new FormControl(null),
|
||||||
|
})
|
||||||
|
|
||||||
|
public loading: boolean = false
|
||||||
|
|
||||||
|
store: BehaviorSubject<any>
|
||||||
|
storeSub: Subscription
|
||||||
|
isDirty$: Observable<boolean>
|
||||||
|
|
||||||
|
private unsubscribeNotifier: Subject<any> = new Subject()
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private configService: ConfigService,
|
||||||
|
private toastService: ToastService
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.configService
|
||||||
|
.getConfig()
|
||||||
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
|
.subscribe({
|
||||||
|
next: (config) => {
|
||||||
|
this.initialize(config)
|
||||||
|
},
|
||||||
|
error: (e) => {
|
||||||
|
this.toastService.showError($localize`Error retrieving config`, e)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.unsubscribeNotifier.next(true)
|
||||||
|
this.unsubscribeNotifier.complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
private initialize(config: PaperlessConfig) {
|
||||||
|
this.store = new BehaviorSubject(config)
|
||||||
|
|
||||||
|
this.store
|
||||||
|
.asObservable()
|
||||||
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
|
.subscribe((state) => {
|
||||||
|
this.configForm.patchValue(state, { emitEvent: false })
|
||||||
|
})
|
||||||
|
|
||||||
|
this.isDirty$ = dirtyCheck(this.configForm, this.store.asObservable())
|
||||||
|
|
||||||
|
this.configForm.patchValue(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveConfig() {
|
||||||
|
throw Error('Not Implemented')
|
||||||
|
}
|
||||||
|
}
|
@ -271,6 +271,15 @@
|
|||||||
</svg><span> <ng-container i18n>Settings</ng-container></span>
|
</svg><span> <ng-container i18n>Settings</ng-container></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }">
|
||||||
|
<a class="nav-link" routerLink="config" routerLinkActive="active" (click)="closeMenu()"
|
||||||
|
ngbPopover="Configuration" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
|
||||||
|
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
|
||||||
|
<svg class="sidebaricon" fill="currentColor">
|
||||||
|
<use xlink:href="assets/bootstrap-icons.svg#sliders2-vertical" />
|
||||||
|
</svg><span> <ng-container i18n>Configuration</ng-container></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.User }">
|
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.User }">
|
||||||
<a class="nav-link" routerLink="usersgroups" routerLinkActive="active" (click)="closeMenu()"
|
<a class="nav-link" routerLink="usersgroups" routerLinkActive="active" (click)="closeMenu()"
|
||||||
ngbPopover="Users & Groups" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
|
ngbPopover="Users & Groups" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
|
||||||
|
54
src-ui/src/app/data/paperless-config.ts
Normal file
54
src-ui/src/app/data/paperless-config.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { ObjectWithId } from './object-with-id'
|
||||||
|
|
||||||
|
// see /src/paperless/models.py
|
||||||
|
|
||||||
|
export enum OutputTypeConfig {
|
||||||
|
PDF = 'pdf',
|
||||||
|
PDF_A = 'pdfa',
|
||||||
|
PDF_A1 = 'pdfa-1',
|
||||||
|
PDF_A2 = 'pdfa-2',
|
||||||
|
PDF_A3 = 'pdfa-3',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ModeConfig {
|
||||||
|
SKIP = 'skip',
|
||||||
|
REDO = 'redo',
|
||||||
|
FORCE = 'force',
|
||||||
|
SKIP_NO_ARCHIVE = 'skip_noarchive',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ArchiveFileConfig {
|
||||||
|
NEVER = 'never',
|
||||||
|
WITH_TEXT = 'with_text',
|
||||||
|
ALWAYS = 'always',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CleanConfig {
|
||||||
|
CLEAN = 'clean',
|
||||||
|
FINAL = 'clean-final',
|
||||||
|
NONE = 'none',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ColorConvertConfig {
|
||||||
|
UNCHANGED = 'LeaveColorUnchanged',
|
||||||
|
RGB = 'RGB',
|
||||||
|
INDEPENDENT = 'UseDeviceIndependentColor',
|
||||||
|
GRAY = 'Gray',
|
||||||
|
CMYK = 'CMYK',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PaperlessConfig extends ObjectWithId {
|
||||||
|
output_type: OutputTypeConfig
|
||||||
|
pages: number
|
||||||
|
language: string
|
||||||
|
mode: ModeConfig
|
||||||
|
skip_archive_file: ArchiveFileConfig
|
||||||
|
image_dpi: number
|
||||||
|
unpaper_clean: CleanConfig
|
||||||
|
deskew: boolean
|
||||||
|
rotate_pages: boolean
|
||||||
|
rotate_pages_threshold: number
|
||||||
|
max_image_pixels: number
|
||||||
|
color_conversion_strategy: ColorConvertConfig
|
||||||
|
user_args: object
|
||||||
|
}
|
16
src-ui/src/app/services/config.service.spec.ts
Normal file
16
src-ui/src/app/services/config.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { ConfigService } from './config.service'
|
||||||
|
|
||||||
|
describe('ConfigService', () => {
|
||||||
|
let service: ConfigService
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({})
|
||||||
|
service = TestBed.inject(ConfigService)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
25
src-ui/src/app/services/config.service.ts
Normal file
25
src-ui/src/app/services/config.service.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { Observable, first, map } from 'rxjs'
|
||||||
|
import { environment } from 'src/environments/environment'
|
||||||
|
import { PaperlessConfig } from '../data/paperless-config'
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class ConfigService {
|
||||||
|
protected baseUrl: string = environment.apiBaseUrl + 'config/'
|
||||||
|
|
||||||
|
constructor(protected http: HttpClient) {}
|
||||||
|
|
||||||
|
getConfig(): Observable<PaperlessConfig> {
|
||||||
|
return this.http.get<[PaperlessConfig]>(this.baseUrl).pipe(
|
||||||
|
first(),
|
||||||
|
map((configs) => configs[0])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
saveConfig(config: PaperlessConfig): Observable<PaperlessConfig> {
|
||||||
|
return this.http.patch<PaperlessConfig>(this.baseUrl, config).pipe(first())
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user