Prettier code cleanup for .ts files

See #182
This commit is contained in:
Michael Shamoon 2022-03-11 10:53:32 -08:00
parent f8c8161a3e
commit f34202a82a
159 changed files with 3882 additions and 2716 deletions

View File

@ -1,23 +1,25 @@
import { AppPage } from './app.po'; import { AppPage } from './app.po'
import { browser, logging } from 'protractor'; import { browser, logging } from 'protractor'
describe('workspace-project App', () => { describe('workspace-project App', () => {
let page: AppPage; let page: AppPage
beforeEach(() => { beforeEach(() => {
page = new AppPage(); page = new AppPage()
}); })
it('should display welcome message', () => { it('should display welcome message', () => {
page.navigateTo(); page.navigateTo()
expect(page.getTitleText()).toEqual('paperless-ui app is running!'); expect(page.getTitleText()).toEqual('paperless-ui app is running!')
}); })
afterEach(async () => { afterEach(async () => {
// Assert that there are no errors emitted from the browser // Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER); const logs = await browser.manage().logs().get(logging.Type.BROWSER)
expect(logs).not.toContain(jasmine.objectContaining({ expect(logs).not.toContain(
jasmine.objectContaining({
level: logging.Level.SEVERE, level: logging.Level.SEVERE,
} as logging.Entry)); } as logging.Entry)
}); )
}); })
})

View File

@ -1,11 +1,13 @@
import { browser, by, element } from 'protractor'; import { browser, by, element } from 'protractor'
export class AppPage { export class AppPage {
navigateTo(): Promise<unknown> { navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>; return browser.get(browser.baseUrl) as Promise<unknown>
} }
getTitleText(): Promise<string> { getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText() as Promise<string>; return element(
by.css('app-root .content span')
).getText() as Promise<string>
} }
} }

View File

@ -1,39 +1,47 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core'
import { Routes, RouterModule } from '@angular/router'; import { Routes, RouterModule } from '@angular/router'
import { AppFrameComponent } from './components/app-frame/app-frame.component'; import { AppFrameComponent } from './components/app-frame/app-frame.component'
import { DashboardComponent } from './components/dashboard/dashboard.component'; import { DashboardComponent } from './components/dashboard/dashboard.component'
import { DocumentDetailComponent } from './components/document-detail/document-detail.component'; import { DocumentDetailComponent } from './components/document-detail/document-detail.component'
import { DocumentListComponent } from './components/document-list/document-list.component'; import { DocumentListComponent } from './components/document-list/document-list.component'
import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component'; import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component'
import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component'; import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component'
import { LogsComponent } from './components/manage/logs/logs.component'; import { LogsComponent } from './components/manage/logs/logs.component'
import { SettingsComponent } from './components/manage/settings/settings.component'; import { SettingsComponent } from './components/manage/settings/settings.component'
import { TagListComponent } from './components/manage/tag-list/tag-list.component'; import { TagListComponent } from './components/manage/tag-list/tag-list.component'
import { NotFoundComponent } from './components/not-found/not-found.component'; import { NotFoundComponent } from './components/not-found/not-found.component'
import {DocumentAsnComponent} from "./components/document-asn/document-asn.component"; import { DocumentAsnComponent } from './components/document-asn/document-asn.component'
import { DirtyFormGuard } from './guards/dirty-form.guard'; import { DirtyFormGuard } from './guards/dirty-form.guard'
const routes: Routes = [ const routes: Routes = [
{path: '', redirectTo: 'dashboard', pathMatch: 'full'}, { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{path: '', component: AppFrameComponent, children: [ {
{path: 'dashboard', component: DashboardComponent }, path: '',
{path: 'documents', component: DocumentListComponent }, component: AppFrameComponent,
{path: 'view/:id', component: DocumentListComponent }, children: [
{path: 'documents/:id', component: DocumentDetailComponent }, { path: 'dashboard', component: DashboardComponent },
{path: 'asn/:id', component: DocumentAsnComponent }, { path: 'documents', component: DocumentListComponent },
{path: 'tags', component: TagListComponent }, { path: 'view/:id', component: DocumentListComponent },
{path: 'documenttypes', component: DocumentTypeListComponent }, { path: 'documents/:id', component: DocumentDetailComponent },
{path: 'correspondents', component: CorrespondentListComponent }, { path: 'asn/:id', component: DocumentAsnComponent },
{path: 'logs', component: LogsComponent }, { path: 'tags', component: TagListComponent },
{path: 'settings', component: SettingsComponent, canDeactivate: [DirtyFormGuard] }, { path: 'documenttypes', component: DocumentTypeListComponent },
]}, { path: 'correspondents', component: CorrespondentListComponent },
{ path: 'logs', component: LogsComponent },
{
path: 'settings',
component: SettingsComponent,
canDeactivate: [DirtyFormGuard],
},
],
},
{path: '404', component: NotFoundComponent}, { path: '404', component: NotFoundComponent },
{path: '**', redirectTo: '/404', pathMatch: 'full'} { path: '**', redirectTo: '/404', pathMatch: 'full' },
]; ]
@NgModule({ @NgModule({
imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })], imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
exports: [RouterModule] exports: [RouterModule],
}) })
export class AppRoutingModule { } export class AppRoutingModule {}

View File

@ -1,35 +1,33 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing'
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing'
import { AppComponent } from './app.component'; import { AppComponent } from './app.component'
describe('AppComponent', () => { describe('AppComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [ imports: [RouterTestingModule],
RouterTestingModule declarations: [AppComponent],
], }).compileComponents()
declarations: [ })
AppComponent
],
}).compileComponents();
});
it('should create the app', () => { it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent)
const app = fixture.componentInstance; const app = fixture.componentInstance
expect(app).toBeTruthy(); expect(app).toBeTruthy()
}); })
it(`should have as title 'paperless-ui'`, () => { it(`should have as title 'paperless-ui'`, () => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent)
const app = fixture.componentInstance; const app = fixture.componentInstance
expect(app.title).toEqual('paperless-ui'); expect(app.title).toEqual('paperless-ui')
}); })
it('should render title', () => { it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent)
fixture.detectChanges(); fixture.detectChanges()
const compiled = fixture.nativeElement; const compiled = fixture.nativeElement
expect(compiled.querySelector('.content span').textContent).toContain('paperless-ui app is running!'); expect(compiled.querySelector('.content span').textContent).toContain(
}); 'paperless-ui app is running!'
}); )
})
})

View File

@ -1,24 +1,28 @@
import { SettingsService, SETTINGS_KEYS } from './services/settings.service'; import { SettingsService, SETTINGS_KEYS } from './services/settings.service'
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core'
import { Router } from '@angular/router'; import { Router } from '@angular/router'
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs'
import { ConsumerStatusService } from './services/consumer-status.service'; import { ConsumerStatusService } from './services/consumer-status.service'
import { ToastService } from './services/toast.service'; import { ToastService } from './services/toast.service'
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss'],
}) })
export class AppComponent implements OnInit, OnDestroy { export class AppComponent implements OnInit, OnDestroy {
newDocumentSubscription: Subscription
successSubscription: Subscription
failedSubscription: Subscription
newDocumentSubscription: Subscription; constructor(
successSubscription: Subscription; private settings: SettingsService,
failedSubscription: Subscription; private consumerStatusService: ConsumerStatusService,
private toastService: ToastService,
constructor (private settings: SettingsService, private consumerStatusService: ConsumerStatusService, private toastService: ToastService, private router: Router) { private router: Router
let anyWindow = (window as any) ) {
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.js'; let anyWindow = window as any
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.js'
this.settings.updateAppearanceSettings() this.settings.updateAppearanceSettings()
} }
@ -36,7 +40,12 @@ export class AppComponent implements OnInit, OnDestroy {
} }
private showNotification(key) { private showNotification(key) {
if (this.router.url == '/dashboard' && this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD)) { if (
this.router.url == '/dashboard' &&
this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD
)
) {
return false return false
} }
return this.settings.get(key) return this.settings.get(key)
@ -45,26 +54,50 @@ export class AppComponent implements OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
this.consumerStatusService.connect() this.consumerStatusService.connect()
this.successSubscription = this.consumerStatusService
this.successSubscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => { .onDocumentConsumptionFinished()
if (this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS)) { .subscribe((status) => {
this.toastService.show({title: $localize`Document added`, delay: 10000, content: $localize`Document ${status.filename} was added to paperless.`, actionName: $localize`Open document`, action: () => { if (
this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS)
) {
this.toastService.show({
title: $localize`Document added`,
delay: 10000,
content: $localize`Document ${status.filename} was added to paperless.`,
actionName: $localize`Open document`,
action: () => {
this.router.navigate(['documents', status.documentId]) this.router.navigate(['documents', status.documentId])
}}) },
})
} }
}) })
this.failedSubscription = this.consumerStatusService.onDocumentConsumptionFailed().subscribe(status => { this.failedSubscription = this.consumerStatusService
if (this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED)) { .onDocumentConsumptionFailed()
this.toastService.showError($localize`Could not add ${status.filename}\: ${status.message}`) .subscribe((status) => {
if (
this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED)
) {
this.toastService.showError(
$localize`Could not add ${status.filename}\: ${status.message}`
)
} }
}) })
this.newDocumentSubscription = this.consumerStatusService.onDocumentDetected().subscribe(status => { this.newDocumentSubscription = this.consumerStatusService
if (this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT)) { .onDocumentDetected()
this.toastService.show({title: $localize`New document detected`, delay: 5000, content: $localize`Document ${status.filename} is being processed by paperless.`}) .subscribe((status) => {
if (
this.showNotification(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT
)
) {
this.toastService.show({
title: $localize`New document detected`,
delay: 5000,
content: $localize`Document ${status.filename} is being processed by paperless.`,
})
} }
}) })
} }
} }

View File

@ -1,85 +1,88 @@
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core'
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module'
import { AppComponent } from './app.component'; import { AppComponent } from './app.component'
import { NgbDateAdapter, NgbDateParserFormatter, NgbModule } from '@ng-bootstrap/ng-bootstrap'; import {
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; NgbDateAdapter,
import { DocumentListComponent } from './components/document-list/document-list.component'; NgbDateParserFormatter,
import { DocumentDetailComponent } from './components/document-detail/document-detail.component'; NgbModule,
import { DashboardComponent } from './components/dashboard/dashboard.component'; } from '@ng-bootstrap/ng-bootstrap'
import { TagListComponent } from './components/manage/tag-list/tag-list.component'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'
import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component'; import { DocumentListComponent } from './components/document-list/document-list.component'
import { LogsComponent } from './components/manage/logs/logs.component'; import { DocumentDetailComponent } from './components/document-detail/document-detail.component'
import { SettingsComponent } from './components/manage/settings/settings.component'; import { DashboardComponent } from './components/dashboard/dashboard.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { TagListComponent } from './components/manage/tag-list/tag-list.component'
import { DatePipe, registerLocaleData } from '@angular/common'; import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component'
import { NotFoundComponent } from './components/not-found/not-found.component'; import { LogsComponent } from './components/manage/logs/logs.component'
import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component'; import { SettingsComponent } from './components/manage/settings/settings.component'
import { ConfirmDialogComponent } from './components/common/confirm-dialog/confirm-dialog.component'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { CorrespondentEditDialogComponent } from './components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'; import { DatePipe, registerLocaleData } from '@angular/common'
import { TagEditDialogComponent } from './components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component'; import { NotFoundComponent } from './components/not-found/not-found.component'
import { DocumentTypeEditDialogComponent } from './components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'; import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component'
import { TagComponent } from './components/common/tag/tag.component'; import { ConfirmDialogComponent } from './components/common/confirm-dialog/confirm-dialog.component'
import { PageHeaderComponent } from './components/common/page-header/page-header.component'; import { CorrespondentEditDialogComponent } from './components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'
import { AppFrameComponent } from './components/app-frame/app-frame.component'; import { TagEditDialogComponent } from './components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component'
import { ToastsComponent } from './components/common/toasts/toasts.component'; import { DocumentTypeEditDialogComponent } from './components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'
import { FilterEditorComponent } from './components/document-list/filter-editor/filter-editor.component'; import { TagComponent } from './components/common/tag/tag.component'
import { FilterableDropdownComponent } from './components/common/filterable-dropdown/filterable-dropdown.component'; import { PageHeaderComponent } from './components/common/page-header/page-header.component'
import { ToggleableDropdownButtonComponent } from './components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'; import { AppFrameComponent } from './components/app-frame/app-frame.component'
import { DateDropdownComponent } from './components/common/date-dropdown/date-dropdown.component'; import { ToastsComponent } from './components/common/toasts/toasts.component'
import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component'; import { FilterEditorComponent } from './components/document-list/filter-editor/filter-editor.component'
import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component'; import { FilterableDropdownComponent } from './components/common/filterable-dropdown/filterable-dropdown.component'
import { BulkEditorComponent } from './components/document-list/bulk-editor/bulk-editor.component'; import { ToggleableDropdownButtonComponent } from './components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
import { NgxFileDropModule } from 'ngx-file-drop'; import { DateDropdownComponent } from './components/common/date-dropdown/date-dropdown.component'
import { TextComponent } from './components/common/input/text/text.component'; import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component'
import { SelectComponent } from './components/common/input/select/select.component'; import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component'
import { CheckComponent } from './components/common/input/check/check.component'; import { BulkEditorComponent } from './components/document-list/bulk-editor/bulk-editor.component'
import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component'; import { NgxFileDropModule } from 'ngx-file-drop'
import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { TextComponent } from './components/common/input/text/text.component'
import { TagsComponent } from './components/common/input/tags/tags.component'; import { SelectComponent } from './components/common/input/select/select.component'
import { SortableDirective } from './directives/sortable.directive'; import { CheckComponent } from './components/common/input/check/check.component'
import { CookieService } from 'ngx-cookie-service'; import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component'
import { CsrfInterceptor } from './interceptors/csrf.interceptor'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'
import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-view-widget/saved-view-widget.component'; import { TagsComponent } from './components/common/input/tags/tags.component'
import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component'; import { SortableDirective } from './directives/sortable.directive'
import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component'; import { CookieService } from 'ngx-cookie-service'
import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component'; import { CsrfInterceptor } from './interceptors/csrf.interceptor'
import { PdfViewerModule } from 'ng2-pdf-viewer'; import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-view-widget/saved-view-widget.component'
import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component'; import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component'
import { YesNoPipe } from './pipes/yes-no.pipe'; import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component'
import { FileSizePipe } from './pipes/file-size.pipe'; import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component'
import { FilterPipe } from './pipes/filter.pipe'; import { PdfViewerModule } from 'ng2-pdf-viewer'
import { DocumentTitlePipe } from './pipes/document-title.pipe'; import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component'
import { MetadataCollapseComponent } from './components/document-detail/metadata-collapse/metadata-collapse.component'; import { YesNoPipe } from './pipes/yes-no.pipe'
import { SelectDialogComponent } from './components/common/select-dialog/select-dialog.component'; import { FileSizePipe } from './pipes/file-size.pipe'
import { NgSelectModule } from '@ng-select/ng-select'; import { FilterPipe } from './pipes/filter.pipe'
import { NumberComponent } from './components/common/input/number/number.component'; import { DocumentTitlePipe } from './pipes/document-title.pipe'
import { SafePipe } from './pipes/safe.pipe'; import { MetadataCollapseComponent } from './components/document-detail/metadata-collapse/metadata-collapse.component'
import { CustomDatePipe } from './pipes/custom-date.pipe'; import { SelectDialogComponent } from './components/common/select-dialog/select-dialog.component'
import { DateComponent } from './components/common/input/date/date.component'; import { NgSelectModule } from '@ng-select/ng-select'
import { ISODateTimeAdapter } from './utils/ngb-iso-date-time-adapter'; import { NumberComponent } from './components/common/input/number/number.component'
import { LocalizedDateParserFormatter } from './utils/ngb-date-parser-formatter'; import { SafePipe } from './pipes/safe.pipe'
import { ApiVersionInterceptor } from './interceptors/api-version.interceptor'; import { CustomDatePipe } from './pipes/custom-date.pipe'
import { ColorSliderModule } from 'ngx-color/slider'; import { DateComponent } from './components/common/input/date/date.component'
import { ColorComponent } from './components/common/input/color/color.component'; import { ISODateTimeAdapter } from './utils/ngb-iso-date-time-adapter'
import { DocumentAsnComponent } from './components/document-asn/document-asn.component'; import { LocalizedDateParserFormatter } from './utils/ngb-date-parser-formatter'
import { ApiVersionInterceptor } from './interceptors/api-version.interceptor'
import localeCs from '@angular/common/locales/cs'; import { ColorSliderModule } from 'ngx-color/slider'
import localeDa from '@angular/common/locales/da'; import { ColorComponent } from './components/common/input/color/color.component'
import localeDe from '@angular/common/locales/de'; import { DocumentAsnComponent } from './components/document-asn/document-asn.component'
import localeEnGb from '@angular/common/locales/en-GB';
import localeEs from '@angular/common/locales/es';
import localeFr from '@angular/common/locales/fr';
import localeIt from '@angular/common/locales/it';
import localeLb from '@angular/common/locales/lb';
import localeNl from '@angular/common/locales/nl';
import localePl from '@angular/common/locales/pl';
import localePt from '@angular/common/locales/pt';
import localeSv from '@angular/common/locales/sv';
import localeRo from '@angular/common/locales/ro';
import localeRu from '@angular/common/locales/ru';
import localeCs from '@angular/common/locales/cs'
import localeDa from '@angular/common/locales/da'
import localeDe from '@angular/common/locales/de'
import localeEnGb from '@angular/common/locales/en-GB'
import localeEs from '@angular/common/locales/es'
import localeFr from '@angular/common/locales/fr'
import localeIt from '@angular/common/locales/it'
import localeLb from '@angular/common/locales/lb'
import localeNl from '@angular/common/locales/nl'
import localePl from '@angular/common/locales/pl'
import localePt from '@angular/common/locales/pt'
import localeSv from '@angular/common/locales/sv'
import localeRo from '@angular/common/locales/ro'
import localeRu from '@angular/common/locales/ru'
registerLocaleData(localeCs) registerLocaleData(localeCs)
registerLocaleData(localeDa) registerLocaleData(localeDa)
@ -91,8 +94,8 @@ registerLocaleData(localeIt)
registerLocaleData(localeLb) registerLocaleData(localeLb)
registerLocaleData(localeNl) registerLocaleData(localeNl)
registerLocaleData(localePl) registerLocaleData(localePl)
registerLocaleData(localePt, "pt-BR") registerLocaleData(localePt, 'pt-BR')
registerLocaleData(localePt, "pt-PT") registerLocaleData(localePt, 'pt-PT')
registerLocaleData(localeRo) registerLocaleData(localeRo)
registerLocaleData(localeRu) registerLocaleData(localeRu)
registerLocaleData(localeSv) registerLocaleData(localeSv)
@ -146,7 +149,7 @@ registerLocaleData(localeSv)
CustomDatePipe, CustomDatePipe,
DateComponent, DateComponent,
ColorComponent, ColorComponent,
DocumentAsnComponent DocumentAsnComponent,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -159,24 +162,26 @@ registerLocaleData(localeSv)
InfiniteScrollModule, InfiniteScrollModule,
PdfViewerModule, PdfViewerModule,
NgSelectModule, NgSelectModule,
ColorSliderModule ColorSliderModule,
], ],
providers: [ providers: [
DatePipe, DatePipe,
CookieService, { CookieService,
{
provide: HTTP_INTERCEPTORS, provide: HTTP_INTERCEPTORS,
useClass: CsrfInterceptor, useClass: CsrfInterceptor,
multi: true multi: true,
},{ },
{
provide: HTTP_INTERCEPTORS, provide: HTTP_INTERCEPTORS,
useClass: ApiVersionInterceptor, useClass: ApiVersionInterceptor,
multi: true multi: true,
}, },
FilterPipe, FilterPipe,
DocumentTitlePipe, DocumentTitlePipe,
{provide: NgbDateAdapter, useClass: ISODateTimeAdapter}, { provide: NgbDateAdapter, useClass: ISODateTimeAdapter },
{provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter} { provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter },
], ],
bootstrap: [AppComponent] bootstrap: [AppComponent],
}) })
export class AppModule { } export class AppModule {}

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { AppFrameComponent } from './app-frame.component'; import { AppFrameComponent } from './app-frame.component'
describe('AppFrameComponent', () => { describe('AppFrameComponent', () => {
let component: AppFrameComponent; let component: AppFrameComponent
let fixture: ComponentFixture<AppFrameComponent>; let fixture: ComponentFixture<AppFrameComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ AppFrameComponent ] declarations: [AppFrameComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(AppFrameComponent); fixture = TestBed.createComponent(AppFrameComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,26 +1,31 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms'
import { ActivatedRoute, Router, Params } from '@angular/router'; import { ActivatedRoute, Router, Params } from '@angular/router'
import { from, Observable, Subscription, BehaviorSubject } from 'rxjs'; import { from, Observable, Subscription, BehaviorSubject } from 'rxjs'
import { debounceTime, distinctUntilChanged, map, switchMap, first } from 'rxjs/operators'; import {
import { PaperlessDocument } from 'src/app/data/paperless-document'; debounceTime,
import { OpenDocumentsService } from 'src/app/services/open-documents.service'; distinctUntilChanged,
import { SavedViewService } from 'src/app/services/rest/saved-view.service'; map,
import { SearchService } from 'src/app/services/rest/search.service'; switchMap,
import { environment } from 'src/environments/environment'; first,
import { DocumentDetailComponent } from '../document-detail/document-detail.component'; } from 'rxjs/operators'
import { Meta } from '@angular/platform-browser'; import { PaperlessDocument } from 'src/app/data/paperless-document'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'; import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { SearchService } from 'src/app/services/rest/search.service'
import { environment } from 'src/environments/environment'
import { DocumentDetailComponent } from '../document-detail/document-detail.component'
import { Meta } from '@angular/platform-browser'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'
@Component({ @Component({
selector: 'app-app-frame', selector: 'app-app-frame',
templateUrl: './app-frame.component.html', templateUrl: './app-frame.component.html',
styleUrls: ['./app-frame.component.scss'] styleUrls: ['./app-frame.component.scss'],
}) })
export class AppFrameComponent { export class AppFrameComponent {
constructor(
constructor (
public router: Router, public router: Router,
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private openDocumentsService: OpenDocumentsService, private openDocumentsService: OpenDocumentsService,
@ -28,7 +33,7 @@ export class AppFrameComponent {
public savedViewService: SavedViewService, public savedViewService: SavedViewService,
private list: DocumentListViewService, private list: DocumentListViewService,
private meta: Meta private meta: Meta
) { } ) {}
versionString = `${environment.appTitle} ${environment.version}` versionString = `${environment.appTitle} ${environment.version}`
@ -48,14 +53,14 @@ export class AppFrameComponent {
text$.pipe( text$.pipe(
debounceTime(200), debounceTime(200),
distinctUntilChanged(), distinctUntilChanged(),
map(term => { map((term) => {
if (term.lastIndexOf(' ') != -1) { if (term.lastIndexOf(' ') != -1) {
return term.substring(term.lastIndexOf(' ') + 1) return term.substring(term.lastIndexOf(' ') + 1)
} else { } else {
return term return term
} }
}), }),
switchMap(term => switchMap((term) =>
term.length < 2 ? from([[]]) : this.searchService.autocomplete(term) term.length < 2 ? from([[]]) : this.searchService.autocomplete(term)
) )
) )
@ -66,28 +71,36 @@ export class AppFrameComponent {
let lastSpaceIndex = currentSearch.lastIndexOf(' ') let lastSpaceIndex = currentSearch.lastIndexOf(' ')
if (lastSpaceIndex != -1) { if (lastSpaceIndex != -1) {
currentSearch = currentSearch.substring(0, lastSpaceIndex + 1) currentSearch = currentSearch.substring(0, lastSpaceIndex + 1)
currentSearch += event.item + " " currentSearch += event.item + ' '
} else { } else {
currentSearch = event.item + " " currentSearch = event.item + ' '
} }
this.searchField.patchValue(currentSearch) this.searchField.patchValue(currentSearch)
} }
search() { search() {
this.closeMenu() this.closeMenu()
this.list.quickFilter([{rule_type: FILTER_FULLTEXT_QUERY, value: this.searchField.value}]) this.list.quickFilter([
{ rule_type: FILTER_FULLTEXT_QUERY, value: this.searchField.value },
])
} }
closeDocument(d: PaperlessDocument) { closeDocument(d: PaperlessDocument) {
this.openDocumentsService.closeDocument(d).pipe(first()).subscribe(confirmed => { this.openDocumentsService
.closeDocument(d)
.pipe(first())
.subscribe((confirmed) => {
if (confirmed) { if (confirmed) {
this.closeMenu() this.closeMenu()
let route = this.activatedRoute.snapshot let route = this.activatedRoute.snapshot
while (route.firstChild) { while (route.firstChild) {
route = route.firstChild route = route.firstChild
} }
if (route.component == DocumentDetailComponent && route.params['id'] == d.id) { if (
this.router.navigate([""]) route.component == DocumentDetailComponent &&
route.params['id'] == d.id
) {
this.router.navigate([''])
} }
} }
}) })
@ -95,7 +108,10 @@ export class AppFrameComponent {
closeAll() { closeAll() {
// user may need to confirm losing unsaved changes // user may need to confirm losing unsaved changes
this.openDocumentsService.closeAll().pipe(first()).subscribe(confirmed => { this.openDocumentsService
.closeAll()
.pipe(first())
.subscribe((confirmed) => {
if (confirmed) { if (confirmed) {
this.closeMenu() this.closeMenu()
@ -105,7 +121,7 @@ export class AppFrameComponent {
route = route.firstChild route = route.firstChild
} }
if (route.component === DocumentDetailComponent) { if (route.component === DocumentDetailComponent) {
this.router.navigate([""]) this.router.navigate([''])
} }
} }
}) })
@ -123,5 +139,4 @@ export class AppFrameComponent {
return null return null
} }
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ConfirmDialogComponent } from './confirm-dialog.component'; import { ConfirmDialogComponent } from './confirm-dialog.component'
describe('ConfirmDialogComponent', () => { describe('ConfirmDialogComponent', () => {
let component: ConfirmDialogComponent; let component: ConfirmDialogComponent
let fixture: ComponentFixture<ConfirmDialogComponent>; let fixture: ComponentFixture<ConfirmDialogComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ ConfirmDialogComponent ] declarations: [ConfirmDialogComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ConfirmDialogComponent); fixture = TestBed.createComponent(ConfirmDialogComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,15 +1,14 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { Subject } from 'rxjs'; import { Subject } from 'rxjs'
@Component({ @Component({
selector: 'app-confirm-dialog', selector: 'app-confirm-dialog',
templateUrl: './confirm-dialog.component.html', templateUrl: './confirm-dialog.component.html',
styleUrls: ['./confirm-dialog.component.scss'] styleUrls: ['./confirm-dialog.component.scss'],
}) })
export class ConfirmDialogComponent { export class ConfirmDialogComponent {
constructor(public activeModal: NgbActiveModal) {}
constructor(public activeModal: NgbActiveModal) { }
@Output() @Output()
public confirmClicked = new EventEmitter() public confirmClicked = new EventEmitter()
@ -24,7 +23,7 @@ export class ConfirmDialogComponent {
message message
@Input() @Input()
btnClass = "btn-primary" btnClass = 'btn-primary'
@Input() @Input()
btnCaption = $localize`Confirm` btnCaption = $localize`Confirm`

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DateDropdownComponent } from './date-dropdown.component'; import { DateDropdownComponent } from './date-dropdown.component'
describe('DateDropdownComponent', () => { describe('DateDropdownComponent', () => {
let component: DateDropdownComponent; let component: DateDropdownComponent
let fixture: ComponentFixture<DateDropdownComponent>; let fixture: ComponentFixture<DateDropdownComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DateDropdownComponent ] declarations: [DateDropdownComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DateDropdownComponent); fixture = TestBed.createComponent(DateDropdownComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,10 +1,17 @@
import { formatDate } from '@angular/common'; import { formatDate } from '@angular/common'
import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core'; import {
import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; Component,
import { Subject, Subscription } from 'rxjs'; EventEmitter,
import { debounceTime } from 'rxjs/operators'; Input,
import { SettingsService } from 'src/app/services/settings.service'; Output,
import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter'; OnInit,
OnDestroy,
} from '@angular/core'
import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'
import { Subject, Subscription } from 'rxjs'
import { debounceTime } from 'rxjs/operators'
import { SettingsService } from 'src/app/services/settings.service'
import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter'
export interface DateSelection { export interface DateSelection {
before?: string before?: string
@ -20,21 +27,18 @@ const LAST_YEAR = 3
selector: 'app-date-dropdown', selector: 'app-date-dropdown',
templateUrl: './date-dropdown.component.html', templateUrl: './date-dropdown.component.html',
styleUrls: ['./date-dropdown.component.scss'], styleUrls: ['./date-dropdown.component.scss'],
providers: [ providers: [{ provide: NgbDateAdapter, useClass: ISODateAdapter }],
{provide: NgbDateAdapter, useClass: ISODateAdapter},
]
}) })
export class DateDropdownComponent implements OnInit, OnDestroy { export class DateDropdownComponent implements OnInit, OnDestroy {
constructor(settings: SettingsService) { constructor(settings: SettingsService) {
this.datePlaceHolder = settings.getLocalizedDateInputFormat() this.datePlaceHolder = settings.getLocalizedDateInputFormat()
} }
quickFilters = [ quickFilters = [
{id: LAST_7_DAYS, name: $localize`Last 7 days`}, { id: LAST_7_DAYS, name: $localize`Last 7 days` },
{id: LAST_MONTH, name: $localize`Last month`}, { id: LAST_MONTH, name: $localize`Last month` },
{id: LAST_3_MONTHS, name: $localize`Last 3 months`}, { id: LAST_3_MONTHS, name: $localize`Last 3 months` },
{id: LAST_YEAR, name: $localize`Last year`} { id: LAST_YEAR, name: $localize`Last year` },
] ]
datePlaceHolder: string datePlaceHolder: string
@ -62,9 +66,7 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
private sub: Subscription private sub: Subscription
ngOnInit() { ngOnInit() {
this.sub = this.datesSetDebounce$.pipe( this.sub = this.datesSetDebounce$.pipe(debounceTime(400)).subscribe(() => {
debounceTime(400)
).subscribe(() => {
this.onChange() this.onChange()
}) })
} }
@ -81,11 +83,11 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
switch (qf) { switch (qf) {
case LAST_7_DAYS: case LAST_7_DAYS:
date.setDate(date.getDate() - 7) date.setDate(date.getDate() - 7)
break; break
case LAST_MONTH: case LAST_MONTH:
date.setMonth(date.getMonth() - 1) date.setMonth(date.getMonth() - 1)
break; break
case LAST_3_MONTHS: case LAST_3_MONTHS:
date.setMonth(date.getMonth() - 3) date.setMonth(date.getMonth() - 3)
@ -94,20 +96,22 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
case LAST_YEAR: case LAST_YEAR:
date.setFullYear(date.getFullYear() - 1) date.setFullYear(date.getFullYear() - 1)
break break
} }
this.dateAfter = formatDate(date, 'yyyy-MM-dd', "en-us", "UTC") this.dateAfter = formatDate(date, 'yyyy-MM-dd', 'en-us', 'UTC')
this.onChange() this.onChange()
} }
onChange() { onChange() {
this.dateAfterChange.emit(this.dateAfter) this.dateAfterChange.emit(this.dateAfter)
this.dateBeforeChange.emit(this.dateBefore) this.dateBeforeChange.emit(this.dateBefore)
this.datesSet.emit({after: this.dateAfter, before: this.dateBefore}) this.datesSet.emit({ after: this.dateAfter, before: this.dateBefore })
} }
onChangeDebounce() { onChangeDebounce() {
this.datesSetDebounce$.next({after: this.dateAfter, before: this.dateBefore}) this.datesSetDebounce$.next({
after: this.dateAfter,
before: this.dateBefore,
})
} }
clearBefore() { clearBefore() {

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { EditDialogComponent } from './edit-dialog.component'; import { EditDialogComponent } from './edit-dialog.component'
describe('EditDialogComponent', () => { describe('EditDialogComponent', () => {
let component: EditDialogComponent; let component: EditDialogComponent
let fixture: ComponentFixture<EditDialogComponent>; let fixture: ComponentFixture<EditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ EditDialogComponent ] declarations: [EditDialogComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(EditDialogComponent); fixture = TestBed.createComponent(EditDialogComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,20 +1,22 @@
import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { Observable } from 'rxjs'; import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators'
import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model'; import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model'
import { ObjectWithId } from 'src/app/data/object-with-id'; import { ObjectWithId } from 'src/app/data/object-with-id'
import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service'; import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service'
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service'
@Directive() @Directive()
export abstract class EditDialogComponent<T extends ObjectWithId> implements OnInit { export abstract class EditDialogComponent<T extends ObjectWithId>
implements OnInit
{
constructor( constructor(
private service: AbstractPaperlessService<T>, private service: AbstractPaperlessService<T>,
private activeModal: NgbActiveModal, private activeModal: NgbActiveModal,
private toastService: ToastService) { } private toastService: ToastService
) {}
@Input() @Input()
dialogMode: string = 'create' dialogMode: string = 'create'
@ -43,7 +45,7 @@ export abstract class EditDialogComponent<T extends ObjectWithId> implements OnI
// wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM // wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM
setTimeout(() => { setTimeout(() => {
this.closeEnabled = true this.closeEnabled = true
}); })
} }
getCreateTitle() { getCreateTitle() {
@ -65,7 +67,7 @@ export abstract class EditDialogComponent<T extends ObjectWithId> implements OnI
case 'edit': case 'edit':
return this.getEditTitle() return this.getEditTitle()
default: default:
break; break
} }
} }
@ -78,25 +80,31 @@ export abstract class EditDialogComponent<T extends ObjectWithId> implements OnI
} }
save() { save() {
var newObject = Object.assign(Object.assign({}, this.object), this.objectForm.value) var newObject = Object.assign(
Object.assign({}, this.object),
this.objectForm.value
)
var serverResponse: Observable<T> var serverResponse: Observable<T>
switch (this.dialogMode) { switch (this.dialogMode) {
case 'create': case 'create':
serverResponse = this.service.create(newObject) serverResponse = this.service.create(newObject)
break; break
case 'edit': case 'edit':
serverResponse = this.service.update(newObject) serverResponse = this.service.update(newObject)
default: default:
break; break
} }
this.networkActive = true this.networkActive = true
serverResponse.subscribe(result => { serverResponse.subscribe(
(result) => {
this.activeModal.close() this.activeModal.close()
this.success.emit(result) this.success.emit(result)
}, error => { },
(error) => {
this.error = error.error this.error = error.error
this.networkActive = false this.networkActive = false
}) }
)
} }
cancel() { cancel() {

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FilterableDropodownComponent } from './filterable-dropdown.component'; import { FilterableDropodownComponent } from './filterable-dropdown.component'
describe('FilterableDropodownComponent', () => { describe('FilterableDropodownComponent', () => {
let component: FilterableDropodownComponent; let component: FilterableDropodownComponent
let fixture: ComponentFixture<FilterableDropodownComponent>; let fixture: ComponentFixture<FilterableDropodownComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ FilterableDropodownComponent ] declarations: [FilterableDropodownComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(FilterableDropodownComponent); fixture = TestBed.createComponent(FilterableDropodownComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,17 +1,23 @@
import { Component, EventEmitter, Input, Output, ElementRef, ViewChild } from '@angular/core'; import {
import { FilterPipe } from 'src/app/pipes/filter.pipe'; Component,
EventEmitter,
Input,
Output,
ElementRef,
ViewChild,
} from '@angular/core'
import { FilterPipe } from 'src/app/pipes/filter.pipe'
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
import { ToggleableItemState } from './toggleable-dropdown-button/toggleable-dropdown-button.component'; import { ToggleableItemState } from './toggleable-dropdown-button/toggleable-dropdown-button.component'
import { MatchingModel } from 'src/app/data/matching-model'; import { MatchingModel } from 'src/app/data/matching-model'
import { Subject } from 'rxjs'; import { Subject } from 'rxjs'
export interface ChangedItems { export interface ChangedItems {
itemsToAdd: MatchingModel[], itemsToAdd: MatchingModel[]
itemsToRemove: MatchingModel[] itemsToRemove: MatchingModel[]
} }
export class FilterableDropdownSelectionModel { export class FilterableDropdownSelectionModel {
changed = new Subject<FilterableDropdownSelectionModel>() changed = new Subject<FilterableDropdownSelectionModel>()
multiple = false multiple = false
@ -22,14 +28,20 @@ export class FilterableDropdownSelectionModel {
get itemsSorted(): MatchingModel[] { get itemsSorted(): MatchingModel[] {
// TODO: this is getting called very often // TODO: this is getting called very often
return this.items.sort((a,b) => { return this.items.sort((a, b) => {
if (a.id == null && b.id != null) { if (a.id == null && b.id != null) {
return -1 return -1
} else if (a.id != null && b.id == null) { } else if (a.id != null && b.id == null) {
return 1 return 1
} else if (this.getNonTemporary(a.id) == ToggleableItemState.NotSelected && this.getNonTemporary(b.id) != ToggleableItemState.NotSelected) { } else if (
this.getNonTemporary(a.id) == ToggleableItemState.NotSelected &&
this.getNonTemporary(b.id) != ToggleableItemState.NotSelected
) {
return 1 return 1
} else if (this.getNonTemporary(a.id) != ToggleableItemState.NotSelected && this.getNonTemporary(b.id) == ToggleableItemState.NotSelected) { } else if (
this.getNonTemporary(a.id) != ToggleableItemState.NotSelected &&
this.getNonTemporary(b.id) == ToggleableItemState.NotSelected
) {
return -1 return -1
} else { } else {
return a.name.localeCompare(b.name) return a.name.localeCompare(b.name)
@ -42,11 +54,17 @@ export class FilterableDropdownSelectionModel {
private temporarySelectionStates = new Map<number, ToggleableItemState>() private temporarySelectionStates = new Map<number, ToggleableItemState>()
getSelectedItems() { getSelectedItems() {
return this.items.filter(i => this.temporarySelectionStates.get(i.id) == ToggleableItemState.Selected) return this.items.filter(
(i) =>
this.temporarySelectionStates.get(i.id) == ToggleableItemState.Selected
)
} }
getExcludedItems() { getExcludedItems() {
return this.items.filter(i => this.temporarySelectionStates.get(i.id) == ToggleableItemState.Excluded) return this.items.filter(
(i) =>
this.temporarySelectionStates.get(i.id) == ToggleableItemState.Excluded
)
} }
set(id: number, state: ToggleableItemState, fireEvent = true) { set(id: number, state: ToggleableItemState, fireEvent = true) {
@ -62,9 +80,16 @@ export class FilterableDropdownSelectionModel {
toggle(id: number, fireEvent = true) { toggle(id: number, fireEvent = true) {
let state = this.temporarySelectionStates.get(id) let state = this.temporarySelectionStates.get(id)
if (state == null || (state != ToggleableItemState.Selected && state != ToggleableItemState.Excluded)) { if (
state == null ||
(state != ToggleableItemState.Selected &&
state != ToggleableItemState.Excluded)
) {
this.temporarySelectionStates.set(id, ToggleableItemState.Selected) this.temporarySelectionStates.set(id, ToggleableItemState.Selected)
} else if (state == ToggleableItemState.Selected || state == ToggleableItemState.Excluded) { } else if (
state == ToggleableItemState.Selected ||
state == ToggleableItemState.Excluded
) {
this.temporarySelectionStates.delete(id) this.temporarySelectionStates.delete(id)
} }
@ -91,7 +116,7 @@ export class FilterableDropdownSelectionModel {
} }
} }
exclude(id: number, fireEvent:boolean = true) { exclude(id: number, fireEvent: boolean = true) {
let state = this.temporarySelectionStates.get(id) let state = this.temporarySelectionStates.get(id)
if (state == null || state != ToggleableItemState.Excluded) { if (state == null || state != ToggleableItemState.Excluded) {
this.temporarySelectionStates.set(id, ToggleableItemState.Excluded) this.temporarySelectionStates.set(id, ToggleableItemState.Excluded)
@ -130,7 +155,9 @@ export class FilterableDropdownSelectionModel {
} }
get(id: number) { get(id: number) {
return this.temporarySelectionStates.get(id) || ToggleableItemState.NotSelected return (
this.temporarySelectionStates.get(id) || ToggleableItemState.NotSelected
)
} }
selectionSize() { selectionSize() {
@ -150,9 +177,19 @@ export class FilterableDropdownSelectionModel {
} }
isDirty() { isDirty() {
if (!Array.from(this.temporarySelectionStates.keys()).every(id => this.temporarySelectionStates.get(id) == this.selectionStates.get(id))) { if (
!Array.from(this.temporarySelectionStates.keys()).every(
(id) =>
this.temporarySelectionStates.get(id) == this.selectionStates.get(id)
)
) {
return true return true
} else if (!Array.from(this.selectionStates.keys()).every(id => this.selectionStates.get(id) == this.temporarySelectionStates.get(id))) { } else if (
!Array.from(this.selectionStates.keys()).every(
(id) =>
this.selectionStates.get(id) == this.temporarySelectionStates.get(id)
)
) {
return true return true
} else if (this.temporaryLogicalOperator !== this._logicalOperator) { } else if (this.temporaryLogicalOperator !== this._logicalOperator) {
return true return true
@ -162,7 +199,10 @@ export class FilterableDropdownSelectionModel {
} }
isNoneSelected() { isNoneSelected() {
return this.selectionSize() == 1 && this.get(null) == ToggleableItemState.Selected return (
this.selectionSize() == 1 &&
this.get(null) == ToggleableItemState.Selected
)
} }
init(map) { init(map) {
@ -187,8 +227,17 @@ export class FilterableDropdownSelectionModel {
diff(): ChangedItems { diff(): ChangedItems {
return { return {
itemsToAdd: this.items.filter(item => this.temporarySelectionStates.get(item.id) == ToggleableItemState.Selected && this.selectionStates.get(item.id) != ToggleableItemState.Selected), itemsToAdd: this.items.filter(
itemsToRemove: this.items.filter(item => !this.temporarySelectionStates.has(item.id) && this.selectionStates.has(item.id)), (item) =>
this.temporarySelectionStates.get(item.id) ==
ToggleableItemState.Selected &&
this.selectionStates.get(item.id) != ToggleableItemState.Selected
),
itemsToRemove: this.items.filter(
(item) =>
!this.temporarySelectionStates.has(item.id) &&
this.selectionStates.has(item.id)
),
} }
} }
} }
@ -196,10 +245,9 @@ export class FilterableDropdownSelectionModel {
@Component({ @Component({
selector: 'app-filterable-dropdown', selector: 'app-filterable-dropdown',
templateUrl: './filterable-dropdown.component.html', templateUrl: './filterable-dropdown.component.html',
styleUrls: ['./filterable-dropdown.component.scss'] styleUrls: ['./filterable-dropdown.component.scss'],
}) })
export class FilterableDropdownComponent { export class FilterableDropdownComponent {
@ViewChild('listFilterTextInput') listFilterTextInput: ElementRef @ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
@ViewChild('dropdown') dropdown: NgbDropdown @ViewChild('dropdown') dropdown: NgbDropdown
@ -211,7 +259,7 @@ export class FilterableDropdownComponent {
this._selectionModel.items = Array.from(items) this._selectionModel.items = Array.from(items)
this._selectionModel.items.unshift({ this._selectionModel.items.unshift({
name: $localize`:Filter drop down element to filter for documents with no correspondent/type/tag assigned:Not assigned`, name: $localize`:Filter drop down element to filter for documents with no correspondent/type/tag assigned:Not assigned`,
id: null id: null,
}) })
} }
} }
@ -229,7 +277,7 @@ export class FilterableDropdownComponent {
model.items = this.selectionModel.items model.items = this.selectionModel.items
model.multiple = this.selectionModel.multiple model.multiple = this.selectionModel.multiple
} }
model.changed.subscribe(updatedModel => { model.changed.subscribe((updatedModel) => {
this.selectionModelChange.next(updatedModel) this.selectionModelChange.next(updatedModel)
}) })
this._selectionModel = model this._selectionModel = model
@ -255,7 +303,7 @@ export class FilterableDropdownComponent {
title: string title: string
@Input() @Input()
filterPlaceholder: string = "" filterPlaceholder: string = ''
@Input() @Input()
icon: string icon: string
@ -276,14 +324,17 @@ export class FilterableDropdownComponent {
open = new EventEmitter() open = new EventEmitter()
get operatorToggleEnabled(): boolean { get operatorToggleEnabled(): boolean {
return this.selectionModel.selectionSize() > 1 && this.selectionModel.getExcludedItems().length == 0 return (
this.selectionModel.selectionSize() > 1 &&
this.selectionModel.getExcludedItems().length == 0
)
} }
modelIsDirty: boolean = false modelIsDirty: boolean = false
constructor(private filterPipe: FilterPipe) { constructor(private filterPipe: FilterPipe) {
this.selectionModel = new FilterableDropdownSelectionModel() this.selectionModel = new FilterableDropdownSelectionModel()
this.selectionModelChange.subscribe(updatedModel => { this.selectionModelChange.subscribe((updatedModel) => {
this.modelIsDirty = updatedModel.isDirty() this.modelIsDirty = updatedModel.isDirty()
}) })
} }
@ -300,7 +351,7 @@ export class FilterableDropdownComponent {
dropdownOpenChange(open: boolean): void { dropdownOpenChange(open: boolean): void {
if (open) { if (open) {
setTimeout(() => { setTimeout(() => {
this.listFilterTextInput.nativeElement.focus(); this.listFilterTextInput.nativeElement.focus()
}, 0) }, 0)
if (this.editing) { if (this.editing) {
this.selectionModel.reset() this.selectionModel.reset()

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ToggleableDropdownButtonComponent } from './toggleable-dropdown-button.component'; import { ToggleableDropdownButtonComponent } from './toggleable-dropdown-button.component'
describe('ToggleableDropdownButtonComponent', () => { describe('ToggleableDropdownButtonComponent', () => {
let component: ToggleableDropdownButtonComponent; let component: ToggleableDropdownButtonComponent
let fixture: ComponentFixture<ToggleableDropdownButtonComponent>; let fixture: ComponentFixture<ToggleableDropdownButtonComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ ToggleableDropdownButtonComponent ] declarations: [ToggleableDropdownButtonComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ToggleableDropdownButtonComponent); fixture = TestBed.createComponent(ToggleableDropdownButtonComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,20 +1,19 @@
import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core'; import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core'
import { MatchingModel } from 'src/app/data/matching-model'; import { MatchingModel } from 'src/app/data/matching-model'
export enum ToggleableItemState { export enum ToggleableItemState {
NotSelected = 0, NotSelected = 0,
Selected = 1, Selected = 1,
PartiallySelected = 2, PartiallySelected = 2,
Excluded = 3 Excluded = 3,
} }
@Component({ @Component({
selector: 'app-toggleable-dropdown-button', selector: 'app-toggleable-dropdown-button',
templateUrl: './toggleable-dropdown-button.component.html', templateUrl: './toggleable-dropdown-button.component.html',
styleUrls: ['./toggleable-dropdown-button.component.scss'] styleUrls: ['./toggleable-dropdown-button.component.scss'],
}) })
export class ToggleableDropdownButtonComponent { export class ToggleableDropdownButtonComponent {
@Input() @Input()
item: MatchingModel item: MatchingModel

View File

@ -1,30 +1,29 @@
import { Directive, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { Directive, ElementRef, Input, OnInit, ViewChild } from '@angular/core'
import { ControlValueAccessor } from '@angular/forms'; import { ControlValueAccessor } from '@angular/forms'
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid'
@Directive() @Directive()
export class AbstractInputComponent<T> implements OnInit, ControlValueAccessor { export class AbstractInputComponent<T> implements OnInit, ControlValueAccessor {
@ViewChild('inputField')
@ViewChild("inputField")
inputField: ElementRef inputField: ElementRef
constructor() { } constructor() {}
onChange = (newValue: T) => {}; onChange = (newValue: T) => {}
onTouched = () => {}; onTouched = () => {}
writeValue(newValue: any): void { writeValue(newValue: any): void {
this.value = newValue this.value = newValue
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
this.onChange = fn; this.onChange = fn
} }
registerOnTouched(fn: any): void { registerOnTouched(fn: any): void {
this.onTouched = fn; this.onTouched = fn
} }
setDisabledState?(isDisabled: boolean): void { setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled; this.disabled = isDisabled
} }
focus() { focus() {
@ -37,7 +36,7 @@ export class AbstractInputComponent<T> implements OnInit, ControlValueAccessor {
title: string title: string
@Input() @Input()
disabled = false; disabled = false
@Input() @Input()
error: string error: string

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { CheckComponent } from './check.component'; import { CheckComponent } from './check.component'
describe('CheckComponent', () => { describe('CheckComponent', () => {
let component: CheckComponent; let component: CheckComponent
let fixture: ComponentFixture<CheckComponent>; let fixture: ComponentFixture<CheckComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ CheckComponent ] declarations: [CheckComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(CheckComponent); fixture = TestBed.createComponent(CheckComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,22 +1,22 @@
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnInit } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid'
import { AbstractInputComponent } from '../abstract-input'; import { AbstractInputComponent } from '../abstract-input'
@Component({ @Component({
providers: [{ providers: [
{
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckComponent), useExisting: forwardRef(() => CheckComponent),
multi: true multi: true,
}], },
],
selector: 'app-input-check', selector: 'app-input-check',
templateUrl: './check.component.html', templateUrl: './check.component.html',
styleUrls: ['./check.component.scss'] styleUrls: ['./check.component.scss'],
}) })
export class CheckComponent extends AbstractInputComponent<boolean> { export class CheckComponent extends AbstractInputComponent<boolean> {
constructor() { constructor() {
super() super()
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ColorComponent } from './color.component'; import { ColorComponent } from './color.component'
describe('ColorComponent', () => { describe('ColorComponent', () => {
let component: ColorComponent; let component: ColorComponent
let fixture: ComponentFixture<ColorComponent>; let fixture: ComponentFixture<ColorComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ ColorComponent ] declarations: [ColorComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ColorComponent); fixture = TestBed.createComponent(ColorComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,20 +1,21 @@
import { Component, forwardRef } from '@angular/core'; import { Component, forwardRef } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { randomColor } from 'src/app/utils/color'; import { randomColor } from 'src/app/utils/color'
import { AbstractInputComponent } from '../abstract-input'; import { AbstractInputComponent } from '../abstract-input'
@Component({ @Component({
providers: [{ providers: [
{
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ColorComponent), useExisting: forwardRef(() => ColorComponent),
multi: true multi: true,
}], },
],
selector: 'app-input-color', selector: 'app-input-color',
templateUrl: './color.component.html', templateUrl: './color.component.html',
styleUrls: ['./color.component.scss'] styleUrls: ['./color.component.scss'],
}) })
export class ColorComponent extends AbstractInputComponent<string> { export class ColorComponent extends AbstractInputComponent<string> {
constructor() { constructor() {
super() super()
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DateComponent } from './date.component'; import { DateComponent } from './date.component'
describe('DateComponent', () => { describe('DateComponent', () => {
let component: DateComponent; let component: DateComponent
let fixture: ComponentFixture<DateComponent>; let fixture: ComponentFixture<DateComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DateComponent ] declarations: [DateComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DateComponent); fixture = TestBed.createComponent(DateComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,21 +1,24 @@
import { Component, forwardRef, OnInit } from '@angular/core'; import { Component, forwardRef, OnInit } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { SettingsService } from 'src/app/services/settings.service'; import { SettingsService } from 'src/app/services/settings.service'
import { AbstractInputComponent } from '../abstract-input'; import { AbstractInputComponent } from '../abstract-input'
@Component({ @Component({
providers: [{ providers: [
{
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateComponent), useExisting: forwardRef(() => DateComponent),
multi: true multi: true,
}], },
],
selector: 'app-input-date', selector: 'app-input-date',
templateUrl: './date.component.html', templateUrl: './date.component.html',
styleUrls: ['./date.component.scss'] styleUrls: ['./date.component.scss'],
}) })
export class DateComponent extends AbstractInputComponent<string> implements OnInit { export class DateComponent
extends AbstractInputComponent<string>
implements OnInit
{
constructor(private settings: SettingsService) { constructor(private settings: SettingsService) {
super() super()
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NumberComponent } from './number.component'; import { NumberComponent } from './number.component'
describe('NumberComponent', () => { describe('NumberComponent', () => {
let component: NumberComponent; let component: NumberComponent
let fixture: ComponentFixture<NumberComponent>; let fixture: ComponentFixture<NumberComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ NumberComponent ] declarations: [NumberComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(NumberComponent); fixture = TestBed.createComponent(NumberComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,21 +1,22 @@
import { Component, forwardRef } from '@angular/core'; import { Component, forwardRef } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { FILTER_ASN_ISNULL } from 'src/app/data/filter-rule-type'; import { FILTER_ASN_ISNULL } from 'src/app/data/filter-rule-type'
import { DocumentService } from 'src/app/services/rest/document.service'; import { DocumentService } from 'src/app/services/rest/document.service'
import { AbstractInputComponent } from '../abstract-input'; import { AbstractInputComponent } from '../abstract-input'
@Component({ @Component({
providers: [{ providers: [
{
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NumberComponent), useExisting: forwardRef(() => NumberComponent),
multi: true multi: true,
}], },
],
selector: 'app-input-number', selector: 'app-input-number',
templateUrl: './number.component.html', templateUrl: './number.component.html',
styleUrls: ['./number.component.scss'] styleUrls: ['./number.component.scss'],
}) })
export class NumberComponent extends AbstractInputComponent<number> { export class NumberComponent extends AbstractInputComponent<number> {
constructor(private documentService: DocumentService) { constructor(private documentService: DocumentService) {
super() super()
} }
@ -24,16 +25,17 @@ export class NumberComponent extends AbstractInputComponent<number> {
if (this.value) { if (this.value) {
return return
} }
this.documentService.listFiltered(1, 1, "archive_serial_number", true, [{rule_type: FILTER_ASN_ISNULL, value: "false"}]).subscribe( this.documentService
results => { .listFiltered(1, 1, 'archive_serial_number', true, [
{ rule_type: FILTER_ASN_ISNULL, value: 'false' },
])
.subscribe((results) => {
if (results.count > 0) { if (results.count > 0) {
this.value = results.results[0].archive_serial_number + 1 this.value = results.results[0].archive_serial_number + 1
} else { } else {
this.value = 1 this.value = 1
} }
this.onChange(this.value) this.onChange(this.value)
})
} }
)
}
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { SelectComponent } from './select.component'; import { SelectComponent } from './select.component'
describe('SelectComponent', () => { describe('SelectComponent', () => {
let component: SelectComponent; let component: SelectComponent
let fixture: ComponentFixture<SelectComponent>; let fixture: ComponentFixture<SelectComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ SelectComponent ] declarations: [SelectComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(SelectComponent); fixture = TestBed.createComponent(SelectComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,19 +1,26 @@
import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core'; import {
import { NG_VALUE_ACCESSOR } from '@angular/forms'; Component,
import { AbstractInputComponent } from '../abstract-input'; EventEmitter,
forwardRef,
Input,
Output,
} from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { AbstractInputComponent } from '../abstract-input'
@Component({ @Component({
providers: [{ providers: [
{
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectComponent), useExisting: forwardRef(() => SelectComponent),
multi: true multi: true,
}], },
],
selector: 'app-input-select', selector: 'app-input-select',
templateUrl: './select.component.html', templateUrl: './select.component.html',
styleUrls: ['./select.component.scss'] styleUrls: ['./select.component.scss'],
}) })
export class SelectComponent extends AbstractInputComponent<number> { export class SelectComponent extends AbstractInputComponent<number> {
constructor() { constructor() {
super() super()
this.addItemRef = this.addItem.bind(this) this.addItemRef = this.addItem.bind(this)
@ -47,7 +54,9 @@ export class SelectComponent extends AbstractInputComponent<number> {
getSuggestions() { getSuggestions() {
if (this.suggestions && this.items) { if (this.suggestions && this.items) {
return this.suggestions.filter(id => id != this.value).map(id => this.items.find(item => item.id == id)) return this.suggestions
.filter((id) => id != this.value)
.map((id) => this.items.find((item) => item.id == id))
} else { } else {
return [] return []
} }
@ -75,7 +84,6 @@ export class SelectComponent extends AbstractInputComponent<number> {
onBlur() { onBlur() {
setTimeout(() => { setTimeout(() => {
this.clearLastSearchTerm() this.clearLastSearchTerm()
}, 3000); }, 3000)
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { TagsComponent } from './tags.component'; import { TagsComponent } from './tags.component'
describe('TagsComponent', () => { describe('TagsComponent', () => {
let component: TagsComponent; let component: TagsComponent
let fixture: ComponentFixture<TagsComponent>; let fixture: ComponentFixture<TagsComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ TagsComponent ] declarations: [TagsComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(TagsComponent); fixture = TestBed.createComponent(TagsComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,45 +1,46 @@
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnInit } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { TagEditDialogComponent } from 'src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component'; import { TagEditDialogComponent } from 'src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component'
import { PaperlessTag } from 'src/app/data/paperless-tag'; import { PaperlessTag } from 'src/app/data/paperless-tag'
import { TagService } from 'src/app/services/rest/tag.service'; import { TagService } from 'src/app/services/rest/tag.service'
@Component({ @Component({
providers: [{ providers: [
{
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TagsComponent), useExisting: forwardRef(() => TagsComponent),
multi: true multi: true,
}], },
],
selector: 'app-input-tags', selector: 'app-input-tags',
templateUrl: './tags.component.html', templateUrl: './tags.component.html',
styleUrls: ['./tags.component.scss'] styleUrls: ['./tags.component.scss'],
}) })
export class TagsComponent implements OnInit, ControlValueAccessor { export class TagsComponent implements OnInit, ControlValueAccessor {
constructor(private tagService: TagService, private modalService: NgbModal) { constructor(private tagService: TagService, private modalService: NgbModal) {
this.createTagRef = this.createTag.bind(this) this.createTagRef = this.createTag.bind(this)
} }
onChange = (newValue: number[]) => {}; onChange = (newValue: number[]) => {}
onTouched = () => {}; onTouched = () => {}
writeValue(newValue: number[]): void { writeValue(newValue: number[]): void {
this.value = newValue this.value = newValue
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
this.onChange = fn; this.onChange = fn
} }
registerOnTouched(fn: any): void { registerOnTouched(fn: any): void {
this.onTouched = fn; this.onTouched = fn
} }
setDisabledState?(isDisabled: boolean): void { setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled; this.disabled = isDisabled
} }
ngOnInit(): void { ngOnInit(): void {
this.tagService.listAll().subscribe(result => { this.tagService.listAll().subscribe((result) => {
this.tags = result.results this.tags = result.results
}) })
} }
@ -63,7 +64,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
getTag(id) { getTag(id) {
if (this.tags) { if (this.tags) {
return this.tags.find(tag => tag.id == id) return this.tags.find((tag) => tag.id == id)
} else { } else {
return null return null
} }
@ -80,12 +81,15 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
} }
createTag(name: string = null) { createTag(name: string = null) {
var modal = this.modalService.open(TagEditDialogComponent, {backdrop: 'static'}) var modal = this.modalService.open(TagEditDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.dialogMode = 'create' modal.componentInstance.dialogMode = 'create'
if (name) modal.componentInstance.object = { name: name } if (name) modal.componentInstance.object = { name: name }
else if (this._lastSearchTerm) modal.componentInstance.object = { name: this._lastSearchTerm } else if (this._lastSearchTerm)
modal.componentInstance.success.subscribe(newTag => { modal.componentInstance.object = { name: this._lastSearchTerm }
this.tagService.listAll().subscribe(tags => { modal.componentInstance.success.subscribe((newTag) => {
this.tagService.listAll().subscribe((tags) => {
this.tags = tags.results this.tags = tags.results
this.value = [...this.value, newTag.id] this.value = [...this.value, newTag.id]
this.onChange(this.value) this.onChange(this.value)
@ -95,7 +99,9 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
getSuggestions() { getSuggestions() {
if (this.suggestions && this.tags) { if (this.suggestions && this.tags) {
return this.suggestions.filter(id => !this.value.includes(id)).map(id => this.tags.find(tag => tag.id == id)) return this.suggestions
.filter((id) => !this.value.includes(id))
.map((id) => this.tags.find((tag) => tag.id == id))
} else { } else {
return [] return []
} }
@ -117,7 +123,6 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
onBlur() { onBlur() {
setTimeout(() => { setTimeout(() => {
this.clearLastSearchTerm() this.clearLastSearchTerm()
}, 3000); }, 3000)
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { TextComponent } from './text.component'; import { TextComponent } from './text.component'
describe('TextComponent', () => { describe('TextComponent', () => {
let component: TextComponent; let component: TextComponent
let fixture: ComponentFixture<TextComponent>; let fixture: ComponentFixture<TextComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ TextComponent ] declarations: [TextComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(TextComponent); fixture = TestBed.createComponent(TextComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,21 +1,21 @@
import { Component, forwardRef } from '@angular/core'; import { Component, forwardRef } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { AbstractInputComponent } from '../abstract-input'; import { AbstractInputComponent } from '../abstract-input'
@Component({ @Component({
providers: [{ providers: [
{
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TextComponent), useExisting: forwardRef(() => TextComponent),
multi: true multi: true,
}], },
],
selector: 'app-input-text', selector: 'app-input-text',
templateUrl: './text.component.html', templateUrl: './text.component.html',
styleUrls: ['./text.component.scss'] styleUrls: ['./text.component.scss'],
}) })
export class TextComponent extends AbstractInputComponent<string> { export class TextComponent extends AbstractInputComponent<string> {
constructor() { constructor() {
super() super()
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { PageHeaderComponent } from './page-header.component'; import { PageHeaderComponent } from './page-header.component'
describe('PageHeaderComponent', () => { describe('PageHeaderComponent', () => {
let component: PageHeaderComponent; let component: PageHeaderComponent
let fixture: ComponentFixture<PageHeaderComponent>; let fixture: ComponentFixture<PageHeaderComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ PageHeaderComponent ] declarations: [PageHeaderComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(PageHeaderComponent); fixture = TestBed.createComponent(PageHeaderComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,17 +1,16 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core'
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser'
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment'
@Component({ @Component({
selector: 'app-page-header', selector: 'app-page-header',
templateUrl: './page-header.component.html', templateUrl: './page-header.component.html',
styleUrls: ['./page-header.component.scss'] styleUrls: ['./page-header.component.scss'],
}) })
export class PageHeaderComponent { export class PageHeaderComponent {
constructor(private titleService: Title) {}
constructor(private titleService: Title) { } _title = ''
_title = ""
@Input() @Input()
set title(title: string) { set title(title: string) {
@ -24,6 +23,5 @@ export class PageHeaderComponent {
} }
@Input() @Input()
subTitle: string = "" subTitle: string = ''
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { SelectDialogComponent } from './select-dialog.component'; import { SelectDialogComponent } from './select-dialog.component'
describe('SelectDialogComponent', () => { describe('SelectDialogComponent', () => {
let component: SelectDialogComponent; let component: SelectDialogComponent
let fixture: ComponentFixture<SelectDialogComponent>; let fixture: ComponentFixture<SelectDialogComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ SelectDialogComponent ] declarations: [SelectDialogComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(SelectDialogComponent); fixture = TestBed.createComponent(SelectDialogComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,15 +1,14 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { ObjectWithId } from 'src/app/data/object-with-id'; import { ObjectWithId } from 'src/app/data/object-with-id'
@Component({ @Component({
selector: 'app-select-dialog', selector: 'app-select-dialog',
templateUrl: './select-dialog.component.html', templateUrl: './select-dialog.component.html',
styleUrls: ['./select-dialog.component.scss'] styleUrls: ['./select-dialog.component.scss'],
}) })
export class SelectDialogComponent implements OnInit { export class SelectDialogComponent implements OnInit {
constructor(public activeModal: NgbActiveModal) { } constructor(public activeModal: NgbActiveModal) {}
@Output() @Output()
public selectClicked = new EventEmitter() public selectClicked = new EventEmitter()
@ -25,8 +24,7 @@ export class SelectDialogComponent implements OnInit {
selected: number selected: number
ngOnInit(): void { ngOnInit(): void {}
}
cancelClicked() { cancelClicked() {
this.activeModal.close() this.activeModal.close()

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { TagComponent } from './tag.component'; import { TagComponent } from './tag.component'
describe('TagComponent', () => { describe('TagComponent', () => {
let component: TagComponent; let component: TagComponent
let fixture: ComponentFixture<TagComponent>; let fixture: ComponentFixture<TagComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ TagComponent ] declarations: [TagComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(TagComponent); fixture = TestBed.createComponent(TagComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,25 +1,22 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core'
import { PaperlessTag } from 'src/app/data/paperless-tag'; import { PaperlessTag } from 'src/app/data/paperless-tag'
@Component({ @Component({
selector: 'app-tag', selector: 'app-tag',
templateUrl: './tag.component.html', templateUrl: './tag.component.html',
styleUrls: ['./tag.component.scss'] styleUrls: ['./tag.component.scss'],
}) })
export class TagComponent implements OnInit { export class TagComponent implements OnInit {
constructor() {}
constructor() { }
@Input() @Input()
tag: PaperlessTag tag: PaperlessTag
@Input() @Input()
linkTitle: string = "" linkTitle: string = ''
@Input() @Input()
clickable: boolean = false clickable: boolean = false
ngOnInit(): void { ngOnInit(): void {}
}
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ToastsComponent } from './toasts.component'; import { ToastsComponent } from './toasts.component'
describe('ToastsComponent', () => { describe('ToastsComponent', () => {
let component: ToastsComponent; let component: ToastsComponent
let fixture: ComponentFixture<ToastsComponent>; let fixture: ComponentFixture<ToastsComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ ToastsComponent ] declarations: [ToastsComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ToastsComponent); fixture = TestBed.createComponent(ToastsComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,15 +1,14 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core'
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs'
import { Toast, ToastService } from 'src/app/services/toast.service'; import { Toast, ToastService } from 'src/app/services/toast.service'
@Component({ @Component({
selector: 'app-toasts', selector: 'app-toasts',
templateUrl: './toasts.component.html', templateUrl: './toasts.component.html',
styleUrls: ['./toasts.component.scss'] styleUrls: ['./toasts.component.scss'],
}) })
export class ToastsComponent implements OnInit, OnDestroy { export class ToastsComponent implements OnInit, OnDestroy {
constructor(private toastService: ToastService) {}
constructor(private toastService: ToastService) { }
subscription: Subscription subscription: Subscription
@ -20,7 +19,8 @@ export class ToastsComponent implements OnInit, OnDestroy {
} }
ngOnInit(): void { ngOnInit(): void {
this.subscription = this.toastService.getToasts().subscribe(toasts => this.toasts = toasts) this.subscription = this.toastService
.getToasts()
.subscribe((toasts) => (this.toasts = toasts))
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DashboardComponent } from './dashboard.component'; import { DashboardComponent } from './dashboard.component'
describe('DashboardComponent', () => { describe('DashboardComponent', () => {
let component: DashboardComponent; let component: DashboardComponent
let fixture: ComponentFixture<DashboardComponent>; let fixture: ComponentFixture<DashboardComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DashboardComponent ] declarations: [DashboardComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DashboardComponent); fixture = TestBed.createComponent(DashboardComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,20 +1,15 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { Meta } from '@angular/platform-browser'; import { Meta } from '@angular/platform-browser'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'; import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'; import { SavedViewService } from 'src/app/services/rest/saved-view.service'
@Component({ @Component({
selector: 'app-dashboard', selector: 'app-dashboard',
templateUrl: './dashboard.component.html', templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'] styleUrls: ['./dashboard.component.scss'],
}) })
export class DashboardComponent implements OnInit { export class DashboardComponent implements OnInit {
constructor(private savedViewService: SavedViewService, private meta: Meta) {}
constructor(
private savedViewService: SavedViewService,
private meta: Meta
) { }
get displayName() { get displayName() {
let tagFullName = this.meta.getTag('name=full_name') let tagFullName = this.meta.getTag('name=full_name')
@ -39,9 +34,10 @@ export class DashboardComponent implements OnInit {
savedViews: PaperlessSavedView[] = [] savedViews: PaperlessSavedView[] = []
ngOnInit(): void { ngOnInit(): void {
this.savedViewService.listAll().subscribe(results => { this.savedViewService.listAll().subscribe((results) => {
this.savedViews = results.results.filter(savedView => savedView.show_on_dashboard) this.savedViews = results.results.filter(
(savedView) => savedView.show_on_dashboard
)
}) })
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { SavedViewWidgetComponent } from './saved-view-widget.component'; import { SavedViewWidgetComponent } from './saved-view-widget.component'
describe('SavedViewWidgetComponent', () => { describe('SavedViewWidgetComponent', () => {
let component: SavedViewWidgetComponent; let component: SavedViewWidgetComponent
let fixture: ComponentFixture<SavedViewWidgetComponent>; let fixture: ComponentFixture<SavedViewWidgetComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ SavedViewWidgetComponent ] declarations: [SavedViewWidgetComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(SavedViewWidgetComponent); fixture = TestBed.createComponent(SavedViewWidgetComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,24 +1,24 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core'
import { Router } from '@angular/router'; import { Router } from '@angular/router'
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs'
import { PaperlessDocument } from 'src/app/data/paperless-document'; import { PaperlessDocument } from 'src/app/data/paperless-document'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'; import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'; import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { DocumentService } from 'src/app/services/rest/document.service'; import { DocumentService } from 'src/app/services/rest/document.service'
@Component({ @Component({
selector: 'app-saved-view-widget', selector: 'app-saved-view-widget',
templateUrl: './saved-view-widget.component.html', templateUrl: './saved-view-widget.component.html',
styleUrls: ['./saved-view-widget.component.scss'] styleUrls: ['./saved-view-widget.component.scss'],
}) })
export class SavedViewWidgetComponent implements OnInit, OnDestroy { export class SavedViewWidgetComponent implements OnInit, OnDestroy {
constructor( constructor(
private documentService: DocumentService, private documentService: DocumentService,
private router: Router, private router: Router,
private list: DocumentListViewService, private list: DocumentListViewService,
private consumerStatusService: ConsumerStatusService) { } private consumerStatusService: ConsumerStatusService
) {}
@Input() @Input()
savedView: PaperlessSavedView savedView: PaperlessSavedView
@ -29,7 +29,9 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
this.reload() this.reload()
this.subscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => { this.subscription = this.consumerStatusService
.onDocumentConsumptionFinished()
.subscribe((status) => {
this.reload() this.reload()
}) })
} }
@ -39,7 +41,15 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
} }
reload() { reload() {
this.documentService.listFiltered(1,10,this.savedView.sort_field, this.savedView.sort_reverse, this.savedView.filter_rules).subscribe(result => { this.documentService
.listFiltered(
1,
10,
this.savedView.sort_field,
this.savedView.sort_reverse,
this.savedView.filter_rules
)
.subscribe((result) => {
this.documents = result.results this.documents = result.results
}) })
} }
@ -49,8 +59,7 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
this.router.navigate(['view', this.savedView.id]) this.router.navigate(['view', this.savedView.id])
} else { } else {
this.list.loadSavedView(this.savedView, true) this.list.loadSavedView(this.savedView, true)
this.router.navigate(["documents"]) this.router.navigate(['documents'])
} }
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { StatisticsWidgetComponent } from './statistics-widget.component'; import { StatisticsWidgetComponent } from './statistics-widget.component'
describe('StatisticsWidgetComponent', () => { describe('StatisticsWidgetComponent', () => {
let component: StatisticsWidgetComponent; let component: StatisticsWidgetComponent
let fixture: ComponentFixture<StatisticsWidgetComponent>; let fixture: ComponentFixture<StatisticsWidgetComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ StatisticsWidgetComponent ] declarations: [StatisticsWidgetComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(StatisticsWidgetComponent); fixture = TestBed.createComponent(StatisticsWidgetComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,24 +1,24 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http'
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core'
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs'
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'; import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment'
export interface Statistics { export interface Statistics {
documents_total?: number documents_total?: number
documents_inbox?: number documents_inbox?: number
} }
@Component({ @Component({
selector: 'app-statistics-widget', selector: 'app-statistics-widget',
templateUrl: './statistics-widget.component.html', templateUrl: './statistics-widget.component.html',
styleUrls: ['./statistics-widget.component.scss'] styleUrls: ['./statistics-widget.component.scss'],
}) })
export class StatisticsWidgetComponent implements OnInit, OnDestroy { export class StatisticsWidgetComponent implements OnInit, OnDestroy {
constructor(
constructor(private http: HttpClient, private http: HttpClient,
private consumerStatusService: ConsumerStatusService) { } private consumerStatusService: ConsumerStatusService
) {}
statistics: Statistics = {} statistics: Statistics = {}
@ -29,14 +29,16 @@ export class StatisticsWidgetComponent implements OnInit, OnDestroy {
} }
reload() { reload() {
this.getStatistics().subscribe(statistics => { this.getStatistics().subscribe((statistics) => {
this.statistics = statistics this.statistics = statistics
}) })
} }
ngOnInit(): void { ngOnInit(): void {
this.reload() this.reload()
this.subscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => { this.subscription = this.consumerStatusService
.onDocumentConsumptionFinished()
.subscribe((status) => {
this.reload() this.reload()
}) })
} }
@ -44,5 +46,4 @@ export class StatisticsWidgetComponent implements OnInit, OnDestroy {
ngOnDestroy(): void { ngOnDestroy(): void {
this.subscription.unsubscribe() this.subscription.unsubscribe()
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { UploadFileWidgetComponent } from './upload-file-widget.component'; import { UploadFileWidgetComponent } from './upload-file-widget.component'
describe('UploadFileWidgetComponent', () => { describe('UploadFileWidgetComponent', () => {
let component: UploadFileWidgetComponent; let component: UploadFileWidgetComponent
let fixture: ComponentFixture<UploadFileWidgetComponent>; let fixture: ComponentFixture<UploadFileWidgetComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ UploadFileWidgetComponent ] declarations: [UploadFileWidgetComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(UploadFileWidgetComponent); fixture = TestBed.createComponent(UploadFileWidgetComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,15 +1,19 @@
import { HttpEventType } from '@angular/common/http'; import { HttpEventType } from '@angular/common/http'
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'; import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'
import { ConsumerStatusService, FileStatus, FileStatusPhase } from 'src/app/services/consumer-status.service'; import {
import { DocumentService } from 'src/app/services/rest/document.service'; ConsumerStatusService,
FileStatus,
FileStatusPhase,
} from 'src/app/services/consumer-status.service'
import { DocumentService } from 'src/app/services/rest/document.service'
const MAX_ALERTS = 5 const MAX_ALERTS = 5
@Component({ @Component({
selector: 'app-upload-file-widget', selector: 'app-upload-file-widget',
templateUrl: './upload-file-widget.component.html', templateUrl: './upload-file-widget.component.html',
styleUrls: ['./upload-file-widget.component.scss'] styleUrls: ['./upload-file-widget.component.scss'],
}) })
export class UploadFileWidgetComponent implements OnInit { export class UploadFileWidgetComponent implements OnInit {
alertsExpanded = false alertsExpanded = false
@ -17,7 +21,7 @@ export class UploadFileWidgetComponent implements OnInit {
constructor( constructor(
private documentService: DocumentService, private documentService: DocumentService,
private consumerStatusService: ConsumerStatusService private consumerStatusService: ConsumerStatusService
) { } ) {}
getStatus() { getStatus() {
return this.consumerStatusService.getConsumerStatus().slice(0, MAX_ALERTS) return this.consumerStatusService.getConsumerStatus().slice(0, MAX_ALERTS)
@ -25,7 +29,8 @@ export class UploadFileWidgetComponent implements OnInit {
getStatusSummary() { getStatusSummary() {
let strings = [] let strings = []
let countUploadingAndProcessing = this.consumerStatusService.getConsumerStatusNotCompleted().length let countUploadingAndProcessing =
this.consumerStatusService.getConsumerStatusNotCompleted().length
let countFailed = this.getStatusFailed().length let countFailed = this.getStatusFailed().length
let countSuccess = this.getStatusSuccess().length let countSuccess = this.getStatusSuccess().length
if (countUploadingAndProcessing > 0) { if (countUploadingAndProcessing > 0) {
@ -37,16 +42,21 @@ export class UploadFileWidgetComponent implements OnInit {
if (countSuccess > 0) { if (countSuccess > 0) {
strings.push($localize`Added: ${countSuccess}`) strings.push($localize`Added: ${countSuccess}`)
} }
return strings.join($localize`:this string is used to separate processing, failed and added on the file upload widget:, `) return strings.join(
$localize`:this string is used to separate processing, failed and added on the file upload widget:, `
)
} }
getStatusHidden() { getStatusHidden() {
if (this.consumerStatusService.getConsumerStatus().length < MAX_ALERTS) return [] if (this.consumerStatusService.getConsumerStatus().length < MAX_ALERTS)
return []
else return this.consumerStatusService.getConsumerStatus().slice(MAX_ALERTS) else return this.consumerStatusService.getConsumerStatus().slice(MAX_ALERTS)
} }
getStatusUploading() { getStatusUploading() {
return this.consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING) return this.consumerStatusService.getConsumerStatus(
FileStatusPhase.UPLOADING
)
} }
getStatusFailed() { getStatusFailed() {
@ -64,7 +74,7 @@ export class UploadFileWidgetComponent implements OnInit {
let current = 0 let current = 0
let max = 0 let max = 0
this.getStatusUploading().forEach(status => { this.getStatusUploading().forEach((status) => {
current += status.currentPhaseProgress current += status.currentPhaseProgress
max += status.currentPhaseMaxProgress max += status.currentPhaseMaxProgress
}) })
@ -73,18 +83,21 @@ export class UploadFileWidgetComponent implements OnInit {
} }
isFinished(status: FileStatus) { isFinished(status: FileStatus) {
return status.phase == FileStatusPhase.FAILED || status.phase == FileStatusPhase.SUCCESS return (
status.phase == FileStatusPhase.FAILED ||
status.phase == FileStatusPhase.SUCCESS
)
} }
getStatusColor(status: FileStatus) { getStatusColor(status: FileStatus) {
switch (status.phase) { switch (status.phase) {
case FileStatusPhase.PROCESSING: case FileStatusPhase.PROCESSING:
case FileStatusPhase.UPLOADING: case FileStatusPhase.UPLOADING:
return "primary" return 'primary'
case FileStatusPhase.FAILED: case FileStatusPhase.FAILED:
return "danger" return 'danger'
case FileStatusPhase.SUCCESS: case FileStatusPhase.SUCCESS:
return "success" return 'success'
} }
} }
@ -96,20 +109,16 @@ export class UploadFileWidgetComponent implements OnInit {
this.consumerStatusService.dismissCompleted() this.consumerStatusService.dismissCompleted()
} }
ngOnInit(): void { ngOnInit(): void {}
}
public fileOver(event){ public fileOver(event) {}
}
public fileLeave(event){ public fileLeave(event) {}
}
public dropped(files: NgxFileDropEntry[]) { public dropped(files: NgxFileDropEntry[]) {
for (const droppedFile of files) { for (const droppedFile of files) {
if (droppedFile.fileEntry.isFile) { if (droppedFile.fileEntry.isFile) {
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
fileEntry.file((file: File) => { fileEntry.file((file: File) => {
let formData = new FormData() let formData = new FormData()
formData.append('document', file, file.name) formData.append('document', file, file.name)
@ -117,29 +126,37 @@ export class UploadFileWidgetComponent implements OnInit {
status.message = $localize`Connecting...` status.message = $localize`Connecting...`
this.documentService.uploadDocument(formData).subscribe(event => { this.documentService.uploadDocument(formData).subscribe(
(event) => {
if (event.type == HttpEventType.UploadProgress) { if (event.type == HttpEventType.UploadProgress) {
status.updateProgress(FileStatusPhase.UPLOADING, event.loaded, event.total) status.updateProgress(
FileStatusPhase.UPLOADING,
event.loaded,
event.total
)
status.message = $localize`Uploading...` status.message = $localize`Uploading...`
} else if (event.type == HttpEventType.Response) { } else if (event.type == HttpEventType.Response) {
status.taskId = event.body["task_id"] status.taskId = event.body['task_id']
status.message = $localize`Upload complete, waiting...` status.message = $localize`Upload complete, waiting...`
} }
},
}, error => { (error) => {
switch (error.status) { switch (error.status) {
case 400: { case 400: {
this.consumerStatusService.fail(status, error.error.document) this.consumerStatusService.fail(status, error.error.document)
break; break
} }
default: { default: {
this.consumerStatusService.fail(status, $localize`HTTP error: ${error.status} ${error.statusText}`) this.consumerStatusService.fail(
break; status,
$localize`HTTP error: ${error.status} ${error.statusText}`
)
break
} }
} }
}
)
}) })
});
} }
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { WelcomeWidgetComponent } from './welcome-widget.component'; import { WelcomeWidgetComponent } from './welcome-widget.component'
describe('WelcomeWidgetComponent', () => { describe('WelcomeWidgetComponent', () => {
let component: WelcomeWidgetComponent; let component: WelcomeWidgetComponent
let fixture: ComponentFixture<WelcomeWidgetComponent>; let fixture: ComponentFixture<WelcomeWidgetComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ WelcomeWidgetComponent ] declarations: [WelcomeWidgetComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(WelcomeWidgetComponent); fixture = TestBed.createComponent(WelcomeWidgetComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,15 +1,12 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
@Component({ @Component({
selector: 'app-welcome-widget', selector: 'app-welcome-widget',
templateUrl: './welcome-widget.component.html', templateUrl: './welcome-widget.component.html',
styleUrls: ['./welcome-widget.component.scss'] styleUrls: ['./welcome-widget.component.scss'],
}) })
export class WelcomeWidgetComponent implements OnInit { export class WelcomeWidgetComponent implements OnInit {
constructor() {}
constructor() { } ngOnInit(): void {}
ngOnInit(): void {
}
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { WidgetFrameComponent } from './widget-frame.component'; import { WidgetFrameComponent } from './widget-frame.component'
describe('WidgetFrameComponent', () => { describe('WidgetFrameComponent', () => {
let component: WidgetFrameComponent; let component: WidgetFrameComponent
let fixture: ComponentFixture<WidgetFrameComponent>; let fixture: ComponentFixture<WidgetFrameComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ WidgetFrameComponent ] declarations: [WidgetFrameComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(WidgetFrameComponent); fixture = TestBed.createComponent(WidgetFrameComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,18 +1,15 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core'
@Component({ @Component({
selector: 'app-widget-frame', selector: 'app-widget-frame',
templateUrl: './widget-frame.component.html', templateUrl: './widget-frame.component.html',
styleUrls: ['./widget-frame.component.scss'] styleUrls: ['./widget-frame.component.scss'],
}) })
export class WidgetFrameComponent implements OnInit { export class WidgetFrameComponent implements OnInit {
constructor() {}
constructor() { }
@Input() @Input()
title: string title: string
ngOnInit(): void { ngOnInit(): void {}
}
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DocumentAsnComponent } from './document-asn.component'; import { DocumentAsnComponent } from './document-asn.component'
describe('DocumentASNComponentComponent', () => { describe('DocumentASNComponentComponent', () => {
let component: DocumentAsnComponent; let component: DocumentAsnComponent
let fixture: ComponentFixture<DocumentAsnComponent>; let fixture: ComponentFixture<DocumentAsnComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DocumentAsnComponent ] declarations: [DocumentAsnComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DocumentAsnComponent); fixture = TestBed.createComponent(DocumentAsnComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,27 +1,27 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import {DocumentService} from "../../services/rest/document.service"; import { DocumentService } from '../../services/rest/document.service'
import {ActivatedRoute, Router} from "@angular/router"; import { ActivatedRoute, Router } from '@angular/router'
import {FILTER_ASN} from "../../data/filter-rule-type"; import { FILTER_ASN } from '../../data/filter-rule-type'
@Component({ @Component({
selector: 'app-document-asncomponent', selector: 'app-document-asncomponent',
templateUrl: './document-asn.component.html', templateUrl: './document-asn.component.html',
styleUrls: ['./document-asn.component.scss'] styleUrls: ['./document-asn.component.scss'],
}) })
export class DocumentAsnComponent implements OnInit { export class DocumentAsnComponent implements OnInit {
asn: string asn: string
constructor( constructor(
private documentsService: DocumentService, private documentsService: DocumentService,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router) { } private router: Router
) {}
ngOnInit(): void { ngOnInit(): void {
this.route.paramMap.subscribe((paramMap) => {
this.route.paramMap.subscribe(paramMap => { this.asn = paramMap.get('id')
this.asn = paramMap.get('id'); this.documentsService
this.documentsService.listAllFilteredIds([{rule_type: FILTER_ASN, value: this.asn}]).subscribe(documentId => { .listAllFilteredIds([{ rule_type: FILTER_ASN, value: this.asn }])
.subscribe((documentId) => {
if (documentId.length == 1) { if (documentId.length == 1) {
this.router.navigate(['documents', documentId[0]]) this.router.navigate(['documents', documentId[0]])
} else { } else {
@ -29,6 +29,5 @@ export class DocumentAsnComponent implements OnInit {
} }
}) })
}) })
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DocumentDetailComponent } from './document-detail.component'; import { DocumentDetailComponent } from './document-detail.component'
describe('DocumentDetailComponent', () => { describe('DocumentDetailComponent', () => {
let component: DocumentDetailComponent; let component: DocumentDetailComponent
let fixture: ComponentFixture<DocumentDetailComponent>; let fixture: ComponentFixture<DocumentDetailComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DocumentDetailComponent ] declarations: [DocumentDetailComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DocumentDetailComponent); fixture = TestBed.createComponent(DocumentDetailComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,38 +1,55 @@
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import {
import { FormControl, FormGroup } from '@angular/forms'; Component,
import { ActivatedRoute, Router } from '@angular/router'; OnInit,
import { NgbModal, NgbNav } from '@ng-bootstrap/ng-bootstrap'; OnDestroy,
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; ViewChild,
import { PaperlessDocument } from 'src/app/data/paperless-document'; ElementRef,
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata'; } from '@angular/core'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; import { FormControl, FormGroup } from '@angular/forms'
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'; import { ActivatedRoute, Router } from '@angular/router'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { NgbModal, NgbNav } from '@ng-bootstrap/ng-bootstrap'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'; import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'; import { PaperlessDocument } from 'src/app/data/paperless-document'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'; import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata'
import { DocumentService } from 'src/app/services/rest/document.service'; import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component'; import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
import { CorrespondentEditDialogComponent } from '../manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'; import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'; import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { PDFDocumentProxy } from 'ng2-pdf-viewer'; import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { ToastService } from 'src/app/services/toast.service'; import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { TextComponent } from '../common/input/text/text.component'; import { DocumentService } from 'src/app/services/rest/document.service'
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component'
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'; import { CorrespondentEditDialogComponent } from '../manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'
import { Observable, Subject, BehaviorSubject } from 'rxjs'; import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'
import { first, takeUntil, switchMap, map, debounceTime, distinctUntilChanged } from 'rxjs/operators'; import { PDFDocumentProxy } from 'ng2-pdf-viewer'
import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions'; import { ToastService } from 'src/app/services/toast.service'
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'; import { TextComponent } from '../common/input/text/text.component'
import {
SettingsService,
SETTINGS_KEYS,
} from 'src/app/services/settings.service'
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'
import { Observable, Subject, BehaviorSubject } from 'rxjs'
import {
first,
takeUntil,
switchMap,
map,
debounceTime,
distinctUntilChanged,
} from 'rxjs/operators'
import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions'
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
@Component({ @Component({
selector: 'app-document-detail', selector: 'app-document-detail',
templateUrl: './document-detail.component.html', templateUrl: './document-detail.component.html',
styleUrls: ['./document-detail.component.scss'] styleUrls: ['./document-detail.component.scss'],
}) })
export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponent { export class DocumentDetailComponent
implements OnInit, OnDestroy, DirtyComponent
@ViewChild("inputTitle") {
@ViewChild('inputTitle')
titleInput: TextComponent titleInput: TextComponent
expandOriginalMetadata = false expandOriginalMetadata = false
@ -63,7 +80,7 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
correspondent: new FormControl(), correspondent: new FormControl(),
document_type: new FormControl(), document_type: new FormControl(),
archive_serial_number: new FormControl(), archive_serial_number: new FormControl(),
tags: new FormControl([]) tags: new FormControl([]),
}) })
previewCurrentPage: number = 1 previewCurrentPage: number = 1
@ -76,8 +93,13 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
@ViewChild('nav') nav: NgbNav @ViewChild('nav') nav: NgbNav
@ViewChild('pdfPreview') set pdfPreview(element) { @ViewChild('pdfPreview') set pdfPreview(element) {
// this gets called when compontent added or removed from DOM // this gets called when compontent added or removed from DOM
if (element && element.nativeElement.offsetParent !== null && this.nav?.activeId == 4) { // its visible if (
setTimeout(()=> this.nav?.select(1)); element &&
element.nativeElement.offsetParent !== null &&
this.nav?.activeId == 4
) {
// its visible
setTimeout(() => this.nav?.select(1))
} }
} }
@ -92,14 +114,17 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
private documentListViewService: DocumentListViewService, private documentListViewService: DocumentListViewService,
private documentTitlePipe: DocumentTitlePipe, private documentTitlePipe: DocumentTitlePipe,
private toastService: ToastService, private toastService: ToastService,
private settings: SettingsService) { private settings: SettingsService
this.titleSubject.pipe( ) {
this.titleSubject
.pipe(
debounceTime(1000), debounceTime(1000),
distinctUntilChanged(), distinctUntilChanged(),
takeUntil(this.unsubscribeNotifier) takeUntil(this.unsubscribeNotifier)
).subscribe(titleValue => { )
.subscribe((titleValue) => {
this.title = titleValue this.title = titleValue
this.documentForm.patchValue({'title': titleValue}) this.documentForm.patchValue({ title: titleValue })
}) })
} }
@ -112,28 +137,50 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
} }
getContentType() { getContentType() {
return this.metadata?.has_archive_version ? 'application/pdf' : this.metadata?.original_mime_type return this.metadata?.has_archive_version
? 'application/pdf'
: this.metadata?.original_mime_type
} }
ngOnInit(): void { ngOnInit(): void {
this.documentForm.valueChanges.pipe(takeUntil(this.unsubscribeNotifier)).subscribe(wow => { this.documentForm.valueChanges
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((wow) => {
Object.assign(this.document, this.documentForm.value) Object.assign(this.document, this.documentForm.value)
}) })
this.correspondentService.listAll().pipe(first()).subscribe(result => this.correspondents = result.results) this.correspondentService
this.documentTypeService.listAll().pipe(first()).subscribe(result => this.documentTypes = result.results) .listAll()
.pipe(first())
.subscribe((result) => (this.correspondents = result.results))
this.documentTypeService
.listAll()
.pipe(first())
.subscribe((result) => (this.documentTypes = result.results))
this.route.paramMap.pipe(switchMap(paramMap => { this.route.paramMap
.pipe(
switchMap((paramMap) => {
const documentId = +paramMap.get('id') const documentId = +paramMap.get('id')
return this.documentsService.get(documentId) return this.documentsService.get(documentId)
})).pipe(switchMap((doc) => { })
)
.pipe(
switchMap((doc) => {
this.documentId = doc.id this.documentId = doc.id
this.previewUrl = this.documentsService.getPreviewUrl(this.documentId) this.previewUrl = this.documentsService.getPreviewUrl(this.documentId)
this.downloadUrl = this.documentsService.getDownloadUrl(this.documentId) this.downloadUrl = this.documentsService.getDownloadUrl(
this.downloadOriginalUrl = this.documentsService.getDownloadUrl(this.documentId, true) this.documentId
)
this.downloadOriginalUrl = this.documentsService.getDownloadUrl(
this.documentId,
true
)
this.suggestions = null this.suggestions = null
if (this.openDocumentService.getOpenDocument(this.documentId)) { if (this.openDocumentService.getOpenDocument(this.documentId)) {
this.updateComponent(this.openDocumentService.getOpenDocument(this.documentId)) this.updateComponent(
this.openDocumentService.getOpenDocument(this.documentId)
)
} else { } else {
this.openDocumentService.openDocument(doc) this.openDocumentService.openDocument(doc)
this.updateComponent(doc) this.updateComponent(doc)
@ -147,116 +194,190 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
correspondent: doc.correspondent, correspondent: doc.correspondent,
document_type: doc.document_type, document_type: doc.document_type,
archive_serial_number: doc.archive_serial_number, archive_serial_number: doc.archive_serial_number,
tags: [...doc.tags] tags: [...doc.tags],
}) })
this.isDirty$ = dirtyCheck(this.documentForm, this.store.asObservable()) this.isDirty$ = dirtyCheck(
this.documentForm,
this.store.asObservable()
)
return this.isDirty$.pipe(map(dirty => ({doc, dirty}))) return this.isDirty$.pipe(map((dirty) => ({ doc, dirty })))
})) })
)
.pipe(takeUntil(this.unsubscribeNotifier)) .pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({doc, dirty}) => { .subscribe(
({ doc, dirty }) => {
this.openDocumentService.setDirty(doc.id, dirty) this.openDocumentService.setDirty(doc.id, dirty)
}, error => {this.router.navigate(['404'])}) },
(error) => {
this.router.navigate(['404'])
}
)
} }
ngOnDestroy() : void { ngOnDestroy(): void {
this.unsubscribeNotifier.next(); this.unsubscribeNotifier.next()
this.unsubscribeNotifier.complete(); this.unsubscribeNotifier.complete()
} }
updateComponent(doc: PaperlessDocument) { updateComponent(doc: PaperlessDocument) {
this.document = doc this.document = doc
this.documentsService.getMetadata(doc.id).pipe(first()).subscribe(result => { this.documentsService
.getMetadata(doc.id)
.pipe(first())
.subscribe(
(result) => {
this.metadata = result this.metadata = result
}, error => { },
(error) => {
this.metadata = null this.metadata = null
}) }
this.documentsService.getSuggestions(doc.id).pipe(first()).subscribe(result => { )
this.documentsService
.getSuggestions(doc.id)
.pipe(first())
.subscribe(
(result) => {
this.suggestions = result this.suggestions = result
}, error => { },
(error) => {
this.suggestions = null this.suggestions = null
}) }
)
this.title = this.documentTitlePipe.transform(doc.title) this.title = this.documentTitlePipe.transform(doc.title)
this.documentForm.patchValue(doc) this.documentForm.patchValue(doc)
} }
createDocumentType(newName: string) { createDocumentType(newName: string) {
var modal = this.modalService.open(DocumentTypeEditDialogComponent, {backdrop: 'static'}) var modal = this.modalService.open(DocumentTypeEditDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.dialogMode = 'create' modal.componentInstance.dialogMode = 'create'
if (newName) modal.componentInstance.object = { name: newName } if (newName) modal.componentInstance.object = { name: newName }
modal.componentInstance.success.pipe(switchMap(newDocumentType => { modal.componentInstance.success
return this.documentTypeService.listAll().pipe(map(documentTypes => ({newDocumentType, documentTypes}))) .pipe(
})) switchMap((newDocumentType) => {
return this.documentTypeService
.listAll()
.pipe(map((documentTypes) => ({ newDocumentType, documentTypes })))
})
)
.pipe(takeUntil(this.unsubscribeNotifier)) .pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({newDocumentType, documentTypes}) => { .subscribe(({ newDocumentType, documentTypes }) => {
this.documentTypes = documentTypes.results this.documentTypes = documentTypes.results
this.documentForm.get('document_type').setValue(newDocumentType.id) this.documentForm.get('document_type').setValue(newDocumentType.id)
}) })
} }
createCorrespondent(newName: string) { createCorrespondent(newName: string) {
var modal = this.modalService.open(CorrespondentEditDialogComponent, {backdrop: 'static'}) var modal = this.modalService.open(CorrespondentEditDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.dialogMode = 'create' modal.componentInstance.dialogMode = 'create'
if (newName) modal.componentInstance.object = { name: newName } if (newName) modal.componentInstance.object = { name: newName }
modal.componentInstance.success.pipe(switchMap(newCorrespondent => { modal.componentInstance.success
return this.correspondentService.listAll().pipe(map(correspondents => ({newCorrespondent, correspondents}))) .pipe(
})) switchMap((newCorrespondent) => {
return this.correspondentService
.listAll()
.pipe(
map((correspondents) => ({ newCorrespondent, correspondents }))
)
})
)
.pipe(takeUntil(this.unsubscribeNotifier)) .pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({newCorrespondent, correspondents}) => { .subscribe(({ newCorrespondent, correspondents }) => {
this.correspondents = correspondents.results this.correspondents = correspondents.results
this.documentForm.get('correspondent').setValue(newCorrespondent.id) this.documentForm.get('correspondent').setValue(newCorrespondent.id)
}) })
} }
discard() { discard() {
this.documentsService.get(this.documentId).pipe(first()).subscribe(doc => { this.documentsService
.get(this.documentId)
.pipe(first())
.subscribe(
(doc) => {
Object.assign(this.document, doc) Object.assign(this.document, doc)
this.title = doc.title this.title = doc.title
this.documentForm.patchValue(doc) this.documentForm.patchValue(doc)
}, error => {this.router.navigate(['404'])}) },
(error) => {
this.router.navigate(['404'])
}
)
} }
save() { save() {
this.networkActive = true this.networkActive = true
this.store.next(this.documentForm.value) this.store.next(this.documentForm.value)
this.documentsService.update(this.document).pipe(first()).subscribe(result => { this.documentsService
.update(this.document)
.pipe(first())
.subscribe(
(result) => {
this.close() this.close()
this.networkActive = false this.networkActive = false
this.error = null this.error = null
}, error => { },
(error) => {
this.networkActive = false this.networkActive = false
this.error = error.error this.error = error.error
}) }
)
} }
saveEditNext() { saveEditNext() {
this.networkActive = true this.networkActive = true
this.store.next(this.documentForm.value) this.store.next(this.documentForm.value)
this.documentsService.update(this.document).pipe(switchMap(updateResult => { this.documentsService
return this.documentListViewService.getNext(this.documentId).pipe(map(nextDocId => ({nextDocId, updateResult}))) .update(this.document)
})).pipe(switchMap(({nextDocId, updateResult}) => { .pipe(
if (nextDocId && updateResult) return this.openDocumentService.closeDocument(this.document).pipe(map(closeResult => ({updateResult, nextDocId, closeResult}))) switchMap((updateResult) => {
})) return this.documentListViewService
.getNext(this.documentId)
.pipe(map((nextDocId) => ({ nextDocId, updateResult })))
})
)
.pipe(
switchMap(({ nextDocId, updateResult }) => {
if (nextDocId && updateResult)
return this.openDocumentService
.closeDocument(this.document)
.pipe(
map((closeResult) => ({ updateResult, nextDocId, closeResult }))
)
})
)
.pipe(first()) .pipe(first())
.subscribe(({updateResult, nextDocId, closeResult}) => { .subscribe(
({ updateResult, nextDocId, closeResult }) => {
this.error = null this.error = null
this.networkActive = false this.networkActive = false
if (closeResult && updateResult && nextDocId) { if (closeResult && updateResult && nextDocId) {
this.router.navigate(['documents', nextDocId]) this.router.navigate(['documents', nextDocId])
this.titleInput?.focus() this.titleInput?.focus()
} }
}, error => { },
(error) => {
this.networkActive = false this.networkActive = false
this.error = error.error this.error = error.error
}) }
)
} }
close() { close() {
this.openDocumentService.closeDocument(this.document).pipe(first()).subscribe(closed => { this.openDocumentService
if (!closed) return; .closeDocument(this.document)
.pipe(first())
.subscribe((closed) => {
if (!closed) return
if (this.documentListViewService.activeSavedViewId) { if (this.documentListViewService.activeSavedViewId) {
this.router.navigate(['view', this.documentListViewService.activeSavedViewId]) this.router.navigate([
'view',
this.documentListViewService.activeSavedViewId,
])
} else { } else {
this.router.navigate(['documents']) this.router.navigate(['documents'])
} }
@ -264,28 +385,43 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
} }
delete() { delete() {
let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.title = $localize`Confirm delete` modal.componentInstance.title = $localize`Confirm delete`
modal.componentInstance.messageBold = $localize`Do you really want to delete document "${this.document.title}"?` modal.componentInstance.messageBold = $localize`Do you really want to delete document "${this.document.title}"?`
modal.componentInstance.message = $localize`The files for this document will be deleted permanently. This operation cannot be undone.` modal.componentInstance.message = $localize`The files for this document will be deleted permanently. This operation cannot be undone.`
modal.componentInstance.btnClass = "btn-danger" modal.componentInstance.btnClass = 'btn-danger'
modal.componentInstance.btnCaption = $localize`Delete document` modal.componentInstance.btnCaption = $localize`Delete document`
modal.componentInstance.confirmClicked.pipe(switchMap(() => { modal.componentInstance.confirmClicked
.pipe(
switchMap(() => {
modal.componentInstance.buttonsEnabled = false modal.componentInstance.buttonsEnabled = false
return this.documentsService.delete(this.document) return this.documentsService.delete(this.document)
})) })
)
.pipe(takeUntil(this.unsubscribeNotifier)) .pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(() => { .subscribe(
() => {
modal.close() modal.close()
this.close() this.close()
}, error => { },
this.toastService.showError($localize`Error deleting document: ${JSON.stringify(error)}`) (error) => {
this.toastService.showError(
$localize`Error deleting document: ${JSON.stringify(error)}`
)
modal.componentInstance.buttonsEnabled = true modal.componentInstance.buttonsEnabled = true
}) }
)
} }
moreLike() { moreLike() {
this.documentListViewService.quickFilter([{rule_type: FILTER_FULLTEXT_MORELIKE, value: this.documentId.toString()}]) this.documentListViewService.quickFilter([
{
rule_type: FILTER_FULLTEXT_MORELIKE,
value: this.documentId.toString(),
},
])
} }
hasNext() { hasNext() {
@ -311,5 +447,4 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
pdfPreviewLoaded(pdf: PDFDocumentProxy) { pdfPreviewLoaded(pdf: PDFDocumentProxy) {
this.previewNumPages = pdf.numPages this.previewNumPages = pdf.numPages
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { MetadataCollapseComponent } from './metadata-collapse.component'; import { MetadataCollapseComponent } from './metadata-collapse.component'
describe('MetadataCollapseComponent', () => { describe('MetadataCollapseComponent', () => {
let component: MetadataCollapseComponent; let component: MetadataCollapseComponent
let fixture: ComponentFixture<MetadataCollapseComponent>; let fixture: ComponentFixture<MetadataCollapseComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ MetadataCollapseComponent ] declarations: [MetadataCollapseComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(MetadataCollapseComponent); fixture = TestBed.createComponent(MetadataCollapseComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,13 +1,12 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core'
@Component({ @Component({
selector: 'app-metadata-collapse', selector: 'app-metadata-collapse',
templateUrl: './metadata-collapse.component.html', templateUrl: './metadata-collapse.component.html',
styleUrls: ['./metadata-collapse.component.scss'] styleUrls: ['./metadata-collapse.component.scss'],
}) })
export class MetadataCollapseComponent implements OnInit { export class MetadataCollapseComponent implements OnInit {
constructor() {}
constructor() { }
expand = false expand = false
@ -17,7 +16,5 @@ export class MetadataCollapseComponent implements OnInit {
@Input() @Input()
title = $localize`Metadata` title = $localize`Metadata`
ngOnInit(): void { ngOnInit(): void {}
}
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { BulkEditorComponent } from './bulk-editor.component'; import { BulkEditorComponent } from './bulk-editor.component'
describe('BulkEditorComponent', () => { describe('BulkEditorComponent', () => {
let component: BulkEditorComponent; let component: BulkEditorComponent
let fixture: ComponentFixture<BulkEditorComponent>; let fixture: ComponentFixture<BulkEditorComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ BulkEditorComponent ] declarations: [BulkEditorComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(BulkEditorComponent); fixture = TestBed.createComponent(BulkEditorComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,29 +1,37 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { PaperlessTag } from 'src/app/data/paperless-tag'; import { PaperlessTag } from 'src/app/data/paperless-tag'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { TagService } from 'src/app/services/rest/tag.service'; import { TagService } from 'src/app/services/rest/tag.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'; import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'; import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { DocumentService, SelectionDataItem } from 'src/app/services/rest/document.service'; import {
import { OpenDocumentsService } from 'src/app/services/open-documents.service'; DocumentService,
import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog/confirm-dialog.component'; SelectionDataItem,
import { ChangedItems, FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component'; } from 'src/app/services/rest/document.service'
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'; import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { MatchingModel } from 'src/app/data/matching-model'; import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog/confirm-dialog.component'
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; import {
import { ToastService } from 'src/app/services/toast.service'; ChangedItems,
import { saveAs } from 'file-saver'; FilterableDropdownSelectionModel,
} from '../../common/filterable-dropdown/filterable-dropdown.component'
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
import { MatchingModel } from 'src/app/data/matching-model'
import {
SettingsService,
SETTINGS_KEYS,
} from 'src/app/services/settings.service'
import { ToastService } from 'src/app/services/toast.service'
import { saveAs } from 'file-saver'
@Component({ @Component({
selector: 'app-bulk-editor', selector: 'app-bulk-editor',
templateUrl: './bulk-editor.component.html', templateUrl: './bulk-editor.component.html',
styleUrls: ['./bulk-editor.component.scss'] styleUrls: ['./bulk-editor.component.scss'],
}) })
export class BulkEditorComponent { export class BulkEditorComponent {
tags: PaperlessTag[] tags: PaperlessTag[]
correspondents: PaperlessCorrespondent[] correspondents: PaperlessCorrespondent[]
documentTypes: PaperlessDocumentType[] documentTypes: PaperlessDocumentType[]
@ -42,43 +50,63 @@ export class BulkEditorComponent {
private openDocumentService: OpenDocumentsService, private openDocumentService: OpenDocumentsService,
private settings: SettingsService, private settings: SettingsService,
private toastService: ToastService private toastService: ToastService
) { } ) {}
applyOnClose: boolean = this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE) applyOnClose: boolean = this.settings.get(
showConfirmationDialogs: boolean = this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS) SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE
)
showConfirmationDialogs: boolean = this.settings.get(
SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS
)
ngOnInit() { ngOnInit() {
this.tagService.listAll().subscribe(result => this.tags = result.results) this.tagService
this.correspondentService.listAll().subscribe(result => this.correspondents = result.results) .listAll()
this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results) .subscribe((result) => (this.tags = result.results))
this.correspondentService
.listAll()
.subscribe((result) => (this.correspondents = result.results))
this.documentTypeService
.listAll()
.subscribe((result) => (this.documentTypes = result.results))
} }
private executeBulkOperation(modal, method: string, args) { private executeBulkOperation(modal, method: string, args) {
if (modal) { if (modal) {
modal.componentInstance.buttonsEnabled = false modal.componentInstance.buttonsEnabled = false
} }
this.documentService.bulkEdit(Array.from(this.list.selected), method, args).subscribe( this.documentService
response => { .bulkEdit(Array.from(this.list.selected), method, args)
.subscribe(
(response) => {
this.list.reload() this.list.reload()
this.list.reduceSelectionToFilter() this.list.reduceSelectionToFilter()
this.list.selected.forEach(id => { this.list.selected.forEach((id) => {
this.openDocumentService.refreshDocument(id) this.openDocumentService.refreshDocument(id)
}) })
if (modal) { if (modal) {
modal.close() modal.close()
} }
}, error => { },
(error) => {
if (modal) { if (modal) {
modal.componentInstance.buttonsEnabled = true modal.componentInstance.buttonsEnabled = true
} }
this.toastService.showError($localize`Error executing bulk operation: ${JSON.stringify(error.error)}`) this.toastService.showError(
$localize`Error executing bulk operation: ${JSON.stringify(
error.error
)}`
)
} }
) )
} }
private applySelectionData(items: SelectionDataItem[], selectionModel: FilterableDropdownSelectionModel) { private applySelectionData(
items: SelectionDataItem[],
selectionModel: FilterableDropdownSelectionModel
) {
let selectionData = new Map<number, ToggleableItemState>() let selectionData = new Map<number, ToggleableItemState>()
items.forEach(i => { items.forEach((i) => {
if (i.document_count == this.list.selected.size) { if (i.document_count == this.list.selected.size) {
selectionData.set(i.id, ToggleableItemState.Selected) selectionData.set(i.id, ToggleableItemState.Selected)
} else if (i.document_count > 0) { } else if (i.document_count > 0) {
@ -89,129 +117,210 @@ export class BulkEditorComponent {
} }
openTagsDropdown() { openTagsDropdown() {
this.documentService.getSelectionData(Array.from(this.list.selected)).subscribe(s => { this.documentService
.getSelectionData(Array.from(this.list.selected))
.subscribe((s) => {
this.applySelectionData(s.selected_tags, this.tagSelectionModel) this.applySelectionData(s.selected_tags, this.tagSelectionModel)
}) })
} }
openDocumentTypeDropdown() { openDocumentTypeDropdown() {
this.documentService.getSelectionData(Array.from(this.list.selected)).subscribe(s => { this.documentService
this.applySelectionData(s.selected_document_types, this.documentTypeSelectionModel) .getSelectionData(Array.from(this.list.selected))
.subscribe((s) => {
this.applySelectionData(
s.selected_document_types,
this.documentTypeSelectionModel
)
}) })
} }
openCorrespondentDropdown() { openCorrespondentDropdown() {
this.documentService.getSelectionData(Array.from(this.list.selected)).subscribe(s => { this.documentService
this.applySelectionData(s.selected_correspondents, this.correspondentSelectionModel) .getSelectionData(Array.from(this.list.selected))
.subscribe((s) => {
this.applySelectionData(
s.selected_correspondents,
this.correspondentSelectionModel
)
}) })
} }
private _localizeList(items: MatchingModel[]) { private _localizeList(items: MatchingModel[]) {
if (items.length == 0) { if (items.length == 0) {
return "" return ''
} else if (items.length == 1) { } else if (items.length == 1) {
return $localize`"${items[0].name}"` return $localize`"${items[0].name}"`
} else if (items.length == 2) { } else if (items.length == 2) {
return $localize`:This is for messages like 'modify "tag1" and "tag2"':"${items[0].name}" and "${items[1].name}"` return $localize`:This is for messages like 'modify "tag1" and "tag2"':"${items[0].name}" and "${items[1].name}"`
} else { } else {
let list = items.slice(0, items.length - 1).map(i => $localize`"${i.name}"`).join($localize`:this is used to separate enumerations and should probably be a comma and a whitespace in most languages:, `) let list = items
return $localize`:this is for messages like 'modify "tag1", "tag2" and "tag3"':${list} and "${items[items.length - 1].name}"` .slice(0, items.length - 1)
.map((i) => $localize`"${i.name}"`)
.join(
$localize`:this is used to separate enumerations and should probably be a comma and a whitespace in most languages:, `
)
return $localize`:this is for messages like 'modify "tag1", "tag2" and "tag3"':${list} and "${
items[items.length - 1].name
}"`
} }
} }
setTags(changedTags: ChangedItems) { setTags(changedTags: ChangedItems) {
if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 0) return if (
changedTags.itemsToAdd.length == 0 &&
changedTags.itemsToRemove.length == 0
)
return
if (this.showConfirmationDialogs) { if (this.showConfirmationDialogs) {
let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.title = $localize`Confirm tags assignment` modal.componentInstance.title = $localize`Confirm tags assignment`
if (changedTags.itemsToAdd.length == 1 && changedTags.itemsToRemove.length == 0) { if (
changedTags.itemsToAdd.length == 1 &&
changedTags.itemsToRemove.length == 0
) {
let tag = changedTags.itemsToAdd[0] let tag = changedTags.itemsToAdd[0]
modal.componentInstance.message = $localize`This operation will add the tag "${tag.name}" to ${this.list.selected.size} selected document(s).` modal.componentInstance.message = $localize`This operation will add the tag "${tag.name}" to ${this.list.selected.size} selected document(s).`
} else if (changedTags.itemsToAdd.length > 1 && changedTags.itemsToRemove.length == 0) { } else if (
modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} to ${this.list.selected.size} selected document(s).` changedTags.itemsToAdd.length > 1 &&
} else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 1) { changedTags.itemsToRemove.length == 0
) {
modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(
changedTags.itemsToAdd
)} to ${this.list.selected.size} selected document(s).`
} else if (
changedTags.itemsToAdd.length == 0 &&
changedTags.itemsToRemove.length == 1
) {
let tag = changedTags.itemsToRemove[0] let tag = changedTags.itemsToRemove[0]
modal.componentInstance.message = $localize`This operation will remove the tag "${tag.name}" from ${this.list.selected.size} selected document(s).` modal.componentInstance.message = $localize`This operation will remove the tag "${tag.name}" from ${this.list.selected.size} selected document(s).`
} else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length > 1) { } else if (
modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(changedTags.itemsToRemove)} from ${this.list.selected.size} selected document(s).` changedTags.itemsToAdd.length == 0 &&
changedTags.itemsToRemove.length > 1
) {
modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(
changedTags.itemsToRemove
)} from ${this.list.selected.size} selected document(s).`
} else { } else {
modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} and remove the tags ${this._localizeList(changedTags.itemsToRemove)} on ${this.list.selected.size} selected document(s).` modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(
changedTags.itemsToAdd
)} and remove the tags ${this._localizeList(
changedTags.itemsToRemove
)} on ${this.list.selected.size} selected document(s).`
} }
modal.componentInstance.btnClass = "btn-warning" modal.componentInstance.btnClass = 'btn-warning'
modal.componentInstance.btnCaption = $localize`Confirm` modal.componentInstance.btnCaption = $localize`Confirm`
modal.componentInstance.confirmClicked.subscribe(() => { modal.componentInstance.confirmClicked.subscribe(() => {
this.executeBulkOperation(modal, 'modify_tags', {"add_tags": changedTags.itemsToAdd.map(t => t.id), "remove_tags": changedTags.itemsToRemove.map(t => t.id)}) this.executeBulkOperation(modal, 'modify_tags', {
add_tags: changedTags.itemsToAdd.map((t) => t.id),
remove_tags: changedTags.itemsToRemove.map((t) => t.id),
})
}) })
} else { } else {
this.executeBulkOperation(null, 'modify_tags', {"add_tags": changedTags.itemsToAdd.map(t => t.id), "remove_tags": changedTags.itemsToRemove.map(t => t.id)}) this.executeBulkOperation(null, 'modify_tags', {
add_tags: changedTags.itemsToAdd.map((t) => t.id),
remove_tags: changedTags.itemsToRemove.map((t) => t.id),
})
} }
} }
setCorrespondents(changedCorrespondents: ChangedItems) { setCorrespondents(changedCorrespondents: ChangedItems) {
if (changedCorrespondents.itemsToAdd.length == 0 && changedCorrespondents.itemsToRemove.length == 0) return if (
changedCorrespondents.itemsToAdd.length == 0 &&
changedCorrespondents.itemsToRemove.length == 0
)
return
let correspondent = changedCorrespondents.itemsToAdd.length > 0 ? changedCorrespondents.itemsToAdd[0] : null let correspondent =
changedCorrespondents.itemsToAdd.length > 0
? changedCorrespondents.itemsToAdd[0]
: null
if (this.showConfirmationDialogs) { if (this.showConfirmationDialogs) {
let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.title = $localize`Confirm correspondent assignment` modal.componentInstance.title = $localize`Confirm correspondent assignment`
if (correspondent) { if (correspondent) {
modal.componentInstance.message = $localize`This operation will assign the correspondent "${correspondent.name}" to ${this.list.selected.size} selected document(s).` modal.componentInstance.message = $localize`This operation will assign the correspondent "${correspondent.name}" to ${this.list.selected.size} selected document(s).`
} else { } else {
modal.componentInstance.message = $localize`This operation will remove the correspondent from ${this.list.selected.size} selected document(s).` modal.componentInstance.message = $localize`This operation will remove the correspondent from ${this.list.selected.size} selected document(s).`
} }
modal.componentInstance.btnClass = "btn-warning" modal.componentInstance.btnClass = 'btn-warning'
modal.componentInstance.btnCaption = $localize`Confirm` modal.componentInstance.btnCaption = $localize`Confirm`
modal.componentInstance.confirmClicked.subscribe(() => { modal.componentInstance.confirmClicked.subscribe(() => {
this.executeBulkOperation(modal, 'set_correspondent', {"correspondent": correspondent ? correspondent.id : null}) this.executeBulkOperation(modal, 'set_correspondent', {
correspondent: correspondent ? correspondent.id : null,
})
}) })
} else { } else {
this.executeBulkOperation(null, 'set_correspondent', {"correspondent": correspondent ? correspondent.id : null}) this.executeBulkOperation(null, 'set_correspondent', {
correspondent: correspondent ? correspondent.id : null,
})
} }
} }
setDocumentTypes(changedDocumentTypes: ChangedItems) { setDocumentTypes(changedDocumentTypes: ChangedItems) {
if (changedDocumentTypes.itemsToAdd.length == 0 && changedDocumentTypes.itemsToRemove.length == 0) return if (
changedDocumentTypes.itemsToAdd.length == 0 &&
changedDocumentTypes.itemsToRemove.length == 0
)
return
let documentType = changedDocumentTypes.itemsToAdd.length > 0 ? changedDocumentTypes.itemsToAdd[0] : null let documentType =
changedDocumentTypes.itemsToAdd.length > 0
? changedDocumentTypes.itemsToAdd[0]
: null
if (this.showConfirmationDialogs) { if (this.showConfirmationDialogs) {
let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.title = $localize`Confirm document type assignment` modal.componentInstance.title = $localize`Confirm document type assignment`
if (documentType) { if (documentType) {
modal.componentInstance.message = $localize`This operation will assign the document type "${documentType.name}" to ${this.list.selected.size} selected document(s).` modal.componentInstance.message = $localize`This operation will assign the document type "${documentType.name}" to ${this.list.selected.size} selected document(s).`
} else { } else {
modal.componentInstance.message = $localize`This operation will remove the document type from ${this.list.selected.size} selected document(s).` modal.componentInstance.message = $localize`This operation will remove the document type from ${this.list.selected.size} selected document(s).`
} }
modal.componentInstance.btnClass = "btn-warning" modal.componentInstance.btnClass = 'btn-warning'
modal.componentInstance.btnCaption = $localize`Confirm` modal.componentInstance.btnCaption = $localize`Confirm`
modal.componentInstance.confirmClicked.subscribe(() => { modal.componentInstance.confirmClicked.subscribe(() => {
this.executeBulkOperation(modal, 'set_document_type', {"document_type": documentType ? documentType.id : null}) this.executeBulkOperation(modal, 'set_document_type', {
document_type: documentType ? documentType.id : null,
})
}) })
} else { } else {
this.executeBulkOperation(null, 'set_document_type', {"document_type": documentType ? documentType.id : null}) this.executeBulkOperation(null, 'set_document_type', {
document_type: documentType ? documentType.id : null,
})
} }
} }
applyDelete() { applyDelete() {
let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.delayConfirm(5) modal.componentInstance.delayConfirm(5)
modal.componentInstance.title = $localize`Delete confirm` modal.componentInstance.title = $localize`Delete confirm`
modal.componentInstance.messageBold = $localize`This operation will permanently delete ${this.list.selected.size} selected document(s).` modal.componentInstance.messageBold = $localize`This operation will permanently delete ${this.list.selected.size} selected document(s).`
modal.componentInstance.message = $localize`This operation cannot be undone.` modal.componentInstance.message = $localize`This operation cannot be undone.`
modal.componentInstance.btnClass = "btn-danger" modal.componentInstance.btnClass = 'btn-danger'
modal.componentInstance.btnCaption = $localize`Delete document(s)` modal.componentInstance.btnCaption = $localize`Delete document(s)`
modal.componentInstance.confirmClicked.subscribe(() => { modal.componentInstance.confirmClicked.subscribe(() => {
modal.componentInstance.buttonsEnabled = false modal.componentInstance.buttonsEnabled = false
this.executeBulkOperation(modal, "delete", {}) this.executeBulkOperation(modal, 'delete', {})
}) })
} }
downloadSelected(content = "archive") { downloadSelected(content = 'archive') {
this.documentService.bulkDownload(Array.from(this.list.selected), content).subscribe((result: any) => { this.documentService
saveAs(result, 'documents.zip'); .bulkDownload(Array.from(this.list.selected), content)
.subscribe((result: any) => {
saveAs(result, 'documents.zip')
}) })
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DocumentCardLargeComponent } from './document-card-large.component'; import { DocumentCardLargeComponent } from './document-card-large.component'
describe('DocumentCardLargeComponent', () => { describe('DocumentCardLargeComponent', () => {
let component: DocumentCardLargeComponent; let component: DocumentCardLargeComponent
let fixture: ComponentFixture<DocumentCardLargeComponent>; let fixture: ComponentFixture<DocumentCardLargeComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DocumentCardLargeComponent ] declarations: [DocumentCardLargeComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DocumentCardLargeComponent); fixture = TestBed.createComponent(DocumentCardLargeComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,20 +1,36 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import {
import { DomSanitizer } from '@angular/platform-browser'; Component,
import { PaperlessDocument } from 'src/app/data/paperless-document'; EventEmitter,
import { DocumentService } from 'src/app/services/rest/document.service'; Input,
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; OnInit,
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'; Output,
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; ViewChild,
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'; } from '@angular/core'
import { DomSanitizer } from '@angular/platform-browser'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { DocumentService } from 'src/app/services/rest/document.service'
import {
SettingsService,
SETTINGS_KEYS,
} from 'src/app/services/settings.service'
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
@Component({ @Component({
selector: 'app-document-card-large', selector: 'app-document-card-large',
templateUrl: './document-card-large.component.html', templateUrl: './document-card-large.component.html',
styleUrls: ['./document-card-large.component.scss', '../popover-preview/popover-preview.scss'] styleUrls: [
'./document-card-large.component.scss',
'../popover-preview/popover-preview.scss',
],
}) })
export class DocumentCardLargeComponent implements OnInit { export class DocumentCardLargeComponent implements OnInit {
constructor(
constructor(private documentService: DocumentService, private sanitizer: DomSanitizer, private settingsService: SettingsService) { } private documentService: DocumentService,
private sanitizer: DomSanitizer,
private settingsService: SettingsService
) {}
@Input() @Input()
selected = false selected = false
@ -39,7 +55,7 @@ export class DocumentCardLargeComponent implements OnInit {
clickDocumentType = new EventEmitter<number>() clickDocumentType = new EventEmitter<number>()
@Output() @Output()
clickMoreLike= new EventEmitter() clickMoreLike = new EventEmitter()
@ViewChild('popover') popover: NgbPopover @ViewChild('popover') popover: NgbPopover
@ -49,17 +65,16 @@ export class DocumentCardLargeComponent implements OnInit {
get searchScoreClass() { get searchScoreClass() {
if (this.document.__search_hit__) { if (this.document.__search_hit__) {
if (this.document.__search_hit__.score > 0.7) { if (this.document.__search_hit__.score > 0.7) {
return "success" return 'success'
} else if (this.document.__search_hit__.score > 0.3) { } else if (this.document.__search_hit__.score > 0.3) {
return "warning" return 'warning'
} else { } else {
return "danger" return 'danger'
} }
} }
} }
ngOnInit(): void { ngOnInit(): void {}
}
getIsThumbInverted() { getIsThumbInverted() {
return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED) return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
@ -90,7 +105,7 @@ export class DocumentCardLargeComponent implements OnInit {
} else { } else {
this.popover.close() this.popover.close()
} }
}, 600); }, 600)
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DocumentCardSmallComponent } from './document-card-small.component'; import { DocumentCardSmallComponent } from './document-card-small.component'
describe('DocumentCardSmallComponent', () => { describe('DocumentCardSmallComponent', () => {
let component: DocumentCardSmallComponent; let component: DocumentCardSmallComponent
let fixture: ComponentFixture<DocumentCardSmallComponent>; let fixture: ComponentFixture<DocumentCardSmallComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DocumentCardSmallComponent ] declarations: [DocumentCardSmallComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DocumentCardSmallComponent); fixture = TestBed.createComponent(DocumentCardSmallComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,18 +1,33 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import {
import { map } from 'rxjs/operators'; Component,
import { PaperlessDocument } from 'src/app/data/paperless-document'; EventEmitter,
import { DocumentService } from 'src/app/services/rest/document.service'; Input,
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; OnInit,
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'; Output,
ViewChild,
} from '@angular/core'
import { map } from 'rxjs/operators'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { DocumentService } from 'src/app/services/rest/document.service'
import {
SettingsService,
SETTINGS_KEYS,
} from 'src/app/services/settings.service'
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
@Component({ @Component({
selector: 'app-document-card-small', selector: 'app-document-card-small',
templateUrl: './document-card-small.component.html', templateUrl: './document-card-small.component.html',
styleUrls: ['./document-card-small.component.scss', '../popover-preview/popover-preview.scss'] styleUrls: [
'./document-card-small.component.scss',
'../popover-preview/popover-preview.scss',
],
}) })
export class DocumentCardSmallComponent implements OnInit { export class DocumentCardSmallComponent implements OnInit {
constructor(
constructor(private documentService: DocumentService, private settingsService: SettingsService) { } private documentService: DocumentService,
private settingsService: SettingsService
) {}
@Input() @Input()
selected = false selected = false
@ -39,8 +54,7 @@ export class DocumentCardSmallComponent implements OnInit {
mouseOnPreview = false mouseOnPreview = false
popoverHidden = true popoverHidden = true
ngOnInit(): void { ngOnInit(): void {}
}
getIsThumbInverted() { getIsThumbInverted() {
return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED) return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
@ -60,7 +74,7 @@ export class DocumentCardSmallComponent implements OnInit {
getTagsLimited$() { getTagsLimited$() {
return this.document.tags$.pipe( return this.document.tags$.pipe(
map(tags => { map((tags) => {
if (tags.length > 7) { if (tags.length > 7) {
this.moreTags = tags.length - 6 this.moreTags = tags.length - 6
return tags.slice(0, 6) return tags.slice(0, 6)
@ -84,7 +98,7 @@ export class DocumentCardSmallComponent implements OnInit {
} else { } else {
this.popover.close() this.popover.close()
} }
}, 600); }, 600)
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DocumentListComponent } from './document-list.component'; import { DocumentListComponent } from './document-list.component'
describe('DocumentListComponent', () => { describe('DocumentListComponent', () => {
let component: DocumentListComponent; let component: DocumentListComponent
let fixture: ComponentFixture<DocumentListComponent>; let fixture: ComponentFixture<DocumentListComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DocumentListComponent ] declarations: [DocumentListComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DocumentListComponent); fixture = TestBed.createComponent(DocumentListComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,27 +1,39 @@
import { Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; import {
import { ActivatedRoute, Router } from '@angular/router'; Component,
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; OnDestroy,
import { Subscription } from 'rxjs'; OnInit,
import { FilterRule, isFullTextFilterRule } from 'src/app/data/filter-rule'; QueryList,
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'; ViewChild,
import { PaperlessDocument } from 'src/app/data/paperless-document'; ViewChildren,
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'; } from '@angular/core'
import { SortableDirective, SortEvent } from 'src/app/directives/sortable.directive'; import { ActivatedRoute, Router } from '@angular/router'
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { Subscription } from 'rxjs'
import { DOCUMENT_SORT_FIELDS, DOCUMENT_SORT_FIELDS_FULLTEXT } from 'src/app/services/rest/document.service'; import { FilterRule, isFullTextFilterRule } from 'src/app/data/filter-rule'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'; import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
import { ToastService } from 'src/app/services/toast.service'; import { PaperlessDocument } from 'src/app/data/paperless-document'
import { FilterEditorComponent } from './filter-editor/filter-editor.component'; import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component'; import {
SortableDirective,
SortEvent,
} from 'src/app/directives/sortable.directive'
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import {
DOCUMENT_SORT_FIELDS,
DOCUMENT_SORT_FIELDS_FULLTEXT,
} from 'src/app/services/rest/document.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { ToastService } from 'src/app/services/toast.service'
import { FilterEditorComponent } from './filter-editor/filter-editor.component'
import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component'
@Component({ @Component({
selector: 'app-document-list', selector: 'app-document-list',
templateUrl: './document-list.component.html', templateUrl: './document-list.component.html',
styleUrls: ['./document-list.component.scss'] styleUrls: ['./document-list.component.scss'],
}) })
export class DocumentListComponent implements OnInit, OnDestroy { export class DocumentListComponent implements OnInit, OnDestroy {
constructor( constructor(
public list: DocumentListViewService, public list: DocumentListViewService,
public savedViewService: SavedViewService, public savedViewService: SavedViewService,
@ -30,12 +42,12 @@ export class DocumentListComponent implements OnInit, OnDestroy {
private toastService: ToastService, private toastService: ToastService,
private modalService: NgbModal, private modalService: NgbModal,
private consumerStatusService: ConsumerStatusService private consumerStatusService: ConsumerStatusService
) { } ) {}
@ViewChild("filterEditor") @ViewChild('filterEditor')
private filterEditor: FilterEditorComponent private filterEditor: FilterEditorComponent
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>; @ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
displayMode = 'smallCards' // largeCards, smallCards, details displayMode = 'smallCards' // largeCards, smallCards, details
@ -52,7 +64,9 @@ export class DocumentListComponent implements OnInit, OnDestroy {
} }
getSortFields() { getSortFields() {
return isFullTextFilterRule(this.list.filterRules) ? DOCUMENT_SORT_FIELDS_FULLTEXT : DOCUMENT_SORT_FIELDS return isFullTextFilterRule(this.list.filterRules)
? DOCUMENT_SORT_FIELDS_FULLTEXT
: DOCUMENT_SORT_FIELDS
} }
onSort(event: SortEvent) { onSort(event: SortEvent) {
@ -71,14 +85,16 @@ export class DocumentListComponent implements OnInit, OnDestroy {
if (localStorage.getItem('document-list:displayMode') != null) { if (localStorage.getItem('document-list:displayMode') != null) {
this.displayMode = localStorage.getItem('document-list:displayMode') this.displayMode = localStorage.getItem('document-list:displayMode')
} }
this.consumptionFinishedSubscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(() => { this.consumptionFinishedSubscription = this.consumerStatusService
.onDocumentConsumptionFinished()
.subscribe(() => {
this.list.reload() this.list.reload()
}) })
this.route.paramMap.subscribe(params => { this.route.paramMap.subscribe((params) => {
if (params.has('id')) { if (params.has('id')) {
this.savedViewService.getCached(+params.get('id')).subscribe(view => { this.savedViewService.getCached(+params.get('id')).subscribe((view) => {
if (!view) { if (!view) {
this.router.navigate(["404"]) this.router.navigate(['404'])
return return
} }
this.list.activateSavedView(view) this.list.activateSavedView(view)
@ -110,19 +126,23 @@ export class DocumentListComponent implements OnInit, OnDestroy {
id: this.list.activeSavedViewId, id: this.list.activeSavedViewId,
filter_rules: this.list.filterRules, filter_rules: this.list.filterRules,
sort_field: this.list.sortField, sort_field: this.list.sortField,
sort_reverse: this.list.sortReverse sort_reverse: this.list.sortReverse,
} }
this.savedViewService.patch(savedView).subscribe(result => { this.savedViewService.patch(savedView).subscribe((result) => {
this.toastService.showInfo($localize`View "${this.list.activeSavedViewTitle}" saved successfully.`) this.toastService.showInfo(
$localize`View "${this.list.activeSavedViewTitle}" saved successfully.`
)
this.unmodifiedFilterRules = this.list.filterRules this.unmodifiedFilterRules = this.list.filterRules
}) })
} }
} }
saveViewConfigAs() { saveViewConfigAs() {
let modal = this.modalService.open(SaveViewConfigDialogComponent, {backdrop: 'static'}) let modal = this.modalService.open(SaveViewConfigDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.defaultName = this.filterEditor.generateFilterName() modal.componentInstance.defaultName = this.filterEditor.generateFilterName()
modal.componentInstance.saveClicked.subscribe(formValue => { modal.componentInstance.saveClicked.subscribe((formValue) => {
modal.componentInstance.buttonsEnabled = false modal.componentInstance.buttonsEnabled = false
let savedView: PaperlessSavedView = { let savedView: PaperlessSavedView = {
name: formValue.name, name: formValue.name,
@ -130,16 +150,21 @@ export class DocumentListComponent implements OnInit, OnDestroy {
show_in_sidebar: formValue.showInSideBar, show_in_sidebar: formValue.showInSideBar,
filter_rules: this.list.filterRules, filter_rules: this.list.filterRules,
sort_reverse: this.list.sortReverse, sort_reverse: this.list.sortReverse,
sort_field: this.list.sortField sort_field: this.list.sortField,
} }
this.savedViewService.create(savedView).subscribe(() => { this.savedViewService.create(savedView).subscribe(
() => {
modal.close() modal.close()
this.toastService.showInfo($localize`View "${savedView.name}" created successfully.`) this.toastService.showInfo(
}, error => { $localize`View "${savedView.name}" created successfully.`
)
},
(error) => {
modal.componentInstance.error = error.error modal.componentInstance.error = error.error
modal.componentInstance.buttonsEnabled = true modal.componentInstance.buttonsEnabled = true
}) }
)
}) })
} }
@ -170,7 +195,9 @@ export class DocumentListComponent implements OnInit, OnDestroy {
} }
clickMoreLike(documentID: number) { clickMoreLike(documentID: number) {
this.list.quickFilter([{rule_type: FILTER_FULLTEXT_MORELIKE, value: documentID.toString()}]) this.list.quickFilter([
{ rule_type: FILTER_FULLTEXT_MORELIKE, value: documentID.toString() },
])
} }
trackByDocumentId(index, item: PaperlessDocument) { trackByDocumentId(index, item: PaperlessDocument) {

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FilterEditorComponent } from './filter-editor.component'; import { FilterEditorComponent } from './filter-editor.component'
describe('FilterEditorComponent', () => { describe('FilterEditorComponent', () => {
let component: FilterEditorComponent; let component: FilterEditorComponent
let fixture: ComponentFixture<FilterEditorComponent>; let fixture: ComponentFixture<FilterEditorComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ FilterEditorComponent ] declarations: [FilterEditorComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(FilterEditorComponent); fixture = TestBed.createComponent(FilterEditorComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,56 +1,85 @@
import { Component, EventEmitter, Input, Output, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import {
import { PaperlessTag } from 'src/app/data/paperless-tag'; Component,
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; EventEmitter,
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; Input,
import { Subject, Subscription } from 'rxjs'; Output,
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; OnInit,
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'; OnDestroy,
import { TagService } from 'src/app/services/rest/tag.service'; ViewChild,
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'; ElementRef,
import { FilterRule } from 'src/app/data/filter-rule'; } from '@angular/core'
import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_ASN, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_FULLTEXT_MORELIKE, FILTER_FULLTEXT_QUERY, FILTER_HAS_ANY_TAG, FILTER_HAS_TAGS_ALL, FILTER_HAS_TAGS_ANY, FILTER_DOES_NOT_HAVE_TAG, FILTER_TITLE, FILTER_TITLE_CONTENT } from 'src/app/data/filter-rule-type'; import { PaperlessTag } from 'src/app/data/paperless-tag'
import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component'; import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'; import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { DocumentService } from 'src/app/services/rest/document.service'; import { Subject, Subscription } from 'rxjs'
import { PaperlessDocument } from 'src/app/data/paperless-document'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { TagService } from 'src/app/services/rest/tag.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { FilterRule } from 'src/app/data/filter-rule'
import {
FILTER_ADDED_AFTER,
FILTER_ADDED_BEFORE,
FILTER_ASN,
FILTER_CORRESPONDENT,
FILTER_CREATED_AFTER,
FILTER_CREATED_BEFORE,
FILTER_DOCUMENT_TYPE,
FILTER_FULLTEXT_MORELIKE,
FILTER_FULLTEXT_QUERY,
FILTER_HAS_ANY_TAG,
FILTER_HAS_TAGS_ALL,
FILTER_HAS_TAGS_ANY,
FILTER_DOES_NOT_HAVE_TAG,
FILTER_TITLE,
FILTER_TITLE_CONTENT,
} from 'src/app/data/filter-rule-type'
import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component'
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
import { DocumentService } from 'src/app/services/rest/document.service'
import { PaperlessDocument } from 'src/app/data/paperless-document'
const TEXT_FILTER_TARGET_TITLE = "title" const TEXT_FILTER_TARGET_TITLE = 'title'
const TEXT_FILTER_TARGET_TITLE_CONTENT = "title-content" const TEXT_FILTER_TARGET_TITLE_CONTENT = 'title-content'
const TEXT_FILTER_TARGET_ASN = "asn" const TEXT_FILTER_TARGET_ASN = 'asn'
const TEXT_FILTER_TARGET_FULLTEXT_QUERY = "fulltext-query" const TEXT_FILTER_TARGET_FULLTEXT_QUERY = 'fulltext-query'
const TEXT_FILTER_TARGET_FULLTEXT_MORELIKE = "fulltext-morelike" const TEXT_FILTER_TARGET_FULLTEXT_MORELIKE = 'fulltext-morelike'
@Component({ @Component({
selector: 'app-filter-editor', selector: 'app-filter-editor',
templateUrl: './filter-editor.component.html', templateUrl: './filter-editor.component.html',
styleUrls: ['./filter-editor.component.scss'] styleUrls: ['./filter-editor.component.scss'],
}) })
export class FilterEditorComponent implements OnInit, OnDestroy { export class FilterEditorComponent implements OnInit, OnDestroy {
generateFilterName() { generateFilterName() {
if (this.filterRules.length == 1) { if (this.filterRules.length == 1) {
let rule = this.filterRules[0] let rule = this.filterRules[0]
switch(this.filterRules[0].rule_type) { switch (this.filterRules[0].rule_type) {
case FILTER_CORRESPONDENT: case FILTER_CORRESPONDENT:
if (rule.value) { if (rule.value) {
return $localize`Correspondent: ${this.correspondents.find(c => c.id == +rule.value)?.name}` return $localize`Correspondent: ${
this.correspondents.find((c) => c.id == +rule.value)?.name
}`
} else { } else {
return $localize`Without correspondent` return $localize`Without correspondent`
} }
case FILTER_DOCUMENT_TYPE: case FILTER_DOCUMENT_TYPE:
if (rule.value) { if (rule.value) {
return $localize`Type: ${this.documentTypes.find(dt => dt.id == +rule.value)?.name}` return $localize`Type: ${
this.documentTypes.find((dt) => dt.id == +rule.value)?.name
}`
} else { } else {
return $localize`Without document type` return $localize`Without document type`
} }
case FILTER_HAS_TAGS_ALL: case FILTER_HAS_TAGS_ALL:
return $localize`Tag: ${this.tags.find(t => t.id == +rule.value)?.name}` return $localize`Tag: ${
this.tags.find((t) => t.id == +rule.value)?.name
}`
case FILTER_HAS_ANY_TAG: case FILTER_HAS_ANY_TAG:
if (rule.value == "false") { if (rule.value == 'false') {
return $localize`Without any tag` return $localize`Without any tag`
} }
@ -62,7 +91,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
} }
return "" return ''
} }
constructor( constructor(
@ -70,28 +99,37 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
private tagService: TagService, private tagService: TagService,
private correspondentService: CorrespondentService, private correspondentService: CorrespondentService,
private documentService: DocumentService private documentService: DocumentService
) { } ) {}
@ViewChild("textFilterInput") @ViewChild('textFilterInput')
textFilterInput: ElementRef textFilterInput: ElementRef
tags: PaperlessTag[] = [] tags: PaperlessTag[] = []
correspondents: PaperlessCorrespondent[] = [] correspondents: PaperlessCorrespondent[] = []
documentTypes: PaperlessDocumentType[] = [] documentTypes: PaperlessDocumentType[] = []
_textFilter = "" _textFilter = ''
_moreLikeId: number _moreLikeId: number
_moreLikeDoc: PaperlessDocument _moreLikeDoc: PaperlessDocument
get textFilterTargets() { get textFilterTargets() {
let targets = [ let targets = [
{id: TEXT_FILTER_TARGET_TITLE, name: $localize`Title`}, { id: TEXT_FILTER_TARGET_TITLE, name: $localize`Title` },
{id: TEXT_FILTER_TARGET_TITLE_CONTENT, name: $localize`Title & content`}, {
{id: TEXT_FILTER_TARGET_ASN, name: $localize`ASN`}, id: TEXT_FILTER_TARGET_TITLE_CONTENT,
{id: TEXT_FILTER_TARGET_FULLTEXT_QUERY, name: $localize`Advanced search`} name: $localize`Title & content`,
},
{ id: TEXT_FILTER_TARGET_ASN, name: $localize`ASN` },
{
id: TEXT_FILTER_TARGET_FULLTEXT_QUERY,
name: $localize`Advanced search`,
},
] ]
if (this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE) { if (this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE) {
targets.push({id: TEXT_FILTER_TARGET_FULLTEXT_MORELIKE, name: $localize`More like`}) targets.push({
id: TEXT_FILTER_TARGET_FULLTEXT_MORELIKE,
name: $localize`More like`,
})
} }
return targets return targets
} }
@ -99,10 +137,10 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
get textFilterTargetName() { get textFilterTargetName() {
return this.textFilterTargets.find(t => t.id == this.textFilterTarget)?.name return this.textFilterTargets.find((t) => t.id == this.textFilterTarget)
?.name
} }
tagSelectionModel = new FilterableDropdownSelectionModel() tagSelectionModel = new FilterableDropdownSelectionModel()
correspondentSelectionModel = new FilterableDropdownSelectionModel() correspondentSelectionModel = new FilterableDropdownSelectionModel()
documentTypeSelectionModel = new FilterableDropdownSelectionModel() documentTypeSelectionModel = new FilterableDropdownSelectionModel()
@ -126,7 +164,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
@Input() @Input()
set filterRules (value: FilterRule[]) { set filterRules(value: FilterRule[]) {
this._filterRules = value this._filterRules = value
this.documentTypeSelectionModel.clear(false) this.documentTypeSelectionModel.clear(false)
@ -139,7 +177,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.dateCreatedBefore = null this.dateCreatedBefore = null
this.dateCreatedAfter = null this.dateCreatedAfter = null
value.forEach(rule => { value.forEach((rule) => {
switch (rule.rule_type) { switch (rule.rule_type) {
case FILTER_TITLE: case FILTER_TITLE:
this._textFilter = rule.value this._textFilter = rule.value
@ -160,7 +198,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
case FILTER_FULLTEXT_MORELIKE: case FILTER_FULLTEXT_MORELIKE:
this._moreLikeId = +rule.value this._moreLikeId = +rule.value
this.textFilterTarget = TEXT_FILTER_TARGET_FULLTEXT_MORELIKE this.textFilterTarget = TEXT_FILTER_TARGET_FULLTEXT_MORELIKE
this.documentService.get(this._moreLikeId).subscribe(result => { this.documentService.get(this._moreLikeId).subscribe((result) => {
this._moreLikeDoc = result this._moreLikeDoc = result
this._textFilter = result.title this._textFilter = result.title
}) })
@ -178,23 +216,43 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.dateAddedBefore = rule.value this.dateAddedBefore = rule.value
break break
case FILTER_HAS_TAGS_ALL: case FILTER_HAS_TAGS_ALL:
this.tagSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false) this.tagSelectionModel.set(
rule.value ? +rule.value : null,
ToggleableItemState.Selected,
false
)
break break
case FILTER_HAS_TAGS_ANY: case FILTER_HAS_TAGS_ANY:
this.tagSelectionModel.logicalOperator = 'or' this.tagSelectionModel.logicalOperator = 'or'
this.tagSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false) this.tagSelectionModel.set(
rule.value ? +rule.value : null,
ToggleableItemState.Selected,
false
)
break break
case FILTER_HAS_ANY_TAG: case FILTER_HAS_ANY_TAG:
this.tagSelectionModel.set(null, ToggleableItemState.Selected, false) this.tagSelectionModel.set(null, ToggleableItemState.Selected, false)
break break
case FILTER_DOES_NOT_HAVE_TAG: case FILTER_DOES_NOT_HAVE_TAG:
this.tagSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Excluded, false) this.tagSelectionModel.set(
rule.value ? +rule.value : null,
ToggleableItemState.Excluded,
false
)
break break
case FILTER_CORRESPONDENT: case FILTER_CORRESPONDENT:
this.correspondentSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false) this.correspondentSelectionModel.set(
rule.value ? +rule.value : null,
ToggleableItemState.Selected,
false
)
break break
case FILTER_DOCUMENT_TYPE: case FILTER_DOCUMENT_TYPE:
this.documentTypeSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false) this.documentTypeSelectionModel.set(
rule.value ? +rule.value : null,
ToggleableItemState.Selected,
false
)
break break
} }
}) })
@ -203,49 +261,104 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
get filterRules(): FilterRule[] { get filterRules(): FilterRule[] {
let filterRules: FilterRule[] = [] let filterRules: FilterRule[] = []
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT) { if (
filterRules.push({rule_type: FILTER_TITLE_CONTENT, value: this._textFilter}) this._textFilter &&
this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT
) {
filterRules.push({
rule_type: FILTER_TITLE_CONTENT,
value: this._textFilter,
})
} }
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE) { if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE) {
filterRules.push({rule_type: FILTER_TITLE, value: this._textFilter}) filterRules.push({ rule_type: FILTER_TITLE, value: this._textFilter })
} }
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_ASN) { if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_ASN) {
filterRules.push({rule_type: FILTER_ASN, value: this._textFilter}) filterRules.push({ rule_type: FILTER_ASN, value: this._textFilter })
} }
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_QUERY) { if (
filterRules.push({rule_type: FILTER_FULLTEXT_QUERY, value: this._textFilter}) this._textFilter &&
this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_QUERY
) {
filterRules.push({
rule_type: FILTER_FULLTEXT_QUERY,
value: this._textFilter,
})
} }
if (this._moreLikeId && this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE) { if (
filterRules.push({rule_type: FILTER_FULLTEXT_MORELIKE, value: this._moreLikeId?.toString()}) this._moreLikeId &&
this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE
) {
filterRules.push({
rule_type: FILTER_FULLTEXT_MORELIKE,
value: this._moreLikeId?.toString(),
})
} }
if (this.tagSelectionModel.isNoneSelected()) { if (this.tagSelectionModel.isNoneSelected()) {
filterRules.push({rule_type: FILTER_HAS_ANY_TAG, value: "false"}) filterRules.push({ rule_type: FILTER_HAS_ANY_TAG, value: 'false' })
} else { } else {
const tagFilterType = this.tagSelectionModel.logicalOperator == 'and' ? FILTER_HAS_TAGS_ALL : FILTER_HAS_TAGS_ANY const tagFilterType =
this.tagSelectionModel.getSelectedItems().filter(tag => tag.id).forEach(tag => { this.tagSelectionModel.logicalOperator == 'and'
filterRules.push({rule_type: tagFilterType, value: tag.id?.toString()}) ? FILTER_HAS_TAGS_ALL
: FILTER_HAS_TAGS_ANY
this.tagSelectionModel
.getSelectedItems()
.filter((tag) => tag.id)
.forEach((tag) => {
filterRules.push({
rule_type: tagFilterType,
value: tag.id?.toString(),
})
})
this.tagSelectionModel
.getExcludedItems()
.filter((tag) => tag.id)
.forEach((tag) => {
filterRules.push({
rule_type: FILTER_DOES_NOT_HAVE_TAG,
value: tag.id?.toString(),
}) })
this.tagSelectionModel.getExcludedItems().filter(tag => tag.id).forEach(tag => {
filterRules.push({rule_type: FILTER_DOES_NOT_HAVE_TAG, value: tag.id?.toString()})
}) })
} }
this.correspondentSelectionModel.getSelectedItems().forEach(correspondent => { this.correspondentSelectionModel
filterRules.push({rule_type: FILTER_CORRESPONDENT, value: correspondent.id?.toString()}) .getSelectedItems()
.forEach((correspondent) => {
filterRules.push({
rule_type: FILTER_CORRESPONDENT,
value: correspondent.id?.toString(),
})
})
this.documentTypeSelectionModel
.getSelectedItems()
.forEach((documentType) => {
filterRules.push({
rule_type: FILTER_DOCUMENT_TYPE,
value: documentType.id?.toString(),
}) })
this.documentTypeSelectionModel.getSelectedItems().forEach(documentType => {
filterRules.push({rule_type: FILTER_DOCUMENT_TYPE, value: documentType.id?.toString()})
}) })
if (this.dateCreatedBefore) { if (this.dateCreatedBefore) {
filterRules.push({rule_type: FILTER_CREATED_BEFORE, value: this.dateCreatedBefore}) filterRules.push({
rule_type: FILTER_CREATED_BEFORE,
value: this.dateCreatedBefore,
})
} }
if (this.dateCreatedAfter) { if (this.dateCreatedAfter) {
filterRules.push({rule_type: FILTER_CREATED_AFTER, value: this.dateCreatedAfter}) filterRules.push({
rule_type: FILTER_CREATED_AFTER,
value: this.dateCreatedAfter,
})
} }
if (this.dateAddedBefore) { if (this.dateAddedBefore) {
filterRules.push({rule_type: FILTER_ADDED_BEFORE, value: this.dateAddedBefore}) filterRules.push({
rule_type: FILTER_ADDED_BEFORE,
value: this.dateAddedBefore,
})
} }
if (this.dateAddedAfter) { if (this.dateAddedAfter) {
filterRules.push({rule_type: FILTER_ADDED_AFTER, value: this.dateAddedAfter}) filterRules.push({
rule_type: FILTER_ADDED_AFTER,
value: this.dateAddedAfter,
})
} }
return filterRules return filterRules
} }
@ -260,14 +373,20 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
if (this._unmodifiedFilterRules.length != this._filterRules.length) { if (this._unmodifiedFilterRules.length != this._filterRules.length) {
modified = true modified = true
} else { } else {
modified = this._unmodifiedFilterRules.some(rule => { modified = this._unmodifiedFilterRules.some((rule) => {
return (this._filterRules.find(fri => fri.rule_type == rule.rule_type && fri.value == rule.value) == undefined) return (
this._filterRules.find(
(fri) => fri.rule_type == rule.rule_type && fri.value == rule.value
) == undefined
)
}) })
if (!modified) { if (!modified) {
// only check other direction if we havent already determined is modified // only check other direction if we havent already determined is modified
modified = this._filterRules.some(rule => { modified = this._filterRules.some((rule) => {
this._unmodifiedFilterRules.find(fr => fr.rule_type == rule.rule_type && fr.value == rule.value) == undefined this._unmodifiedFilterRules.find(
(fr) => fr.rule_type == rule.rule_type && fr.value == rule.value
) == undefined
}) })
} }
} }
@ -290,23 +409,27 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
subscription: Subscription subscription: Subscription
ngOnInit() { ngOnInit() {
this.tagService.listAll().subscribe(result => this.tags = result.results) this.tagService
this.correspondentService.listAll().subscribe(result => this.correspondents = result.results) .listAll()
this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results) .subscribe((result) => (this.tags = result.results))
this.correspondentService
.listAll()
.subscribe((result) => (this.correspondents = result.results))
this.documentTypeService
.listAll()
.subscribe((result) => (this.documentTypes = result.results))
this.textFilterDebounce = new Subject<string>() this.textFilterDebounce = new Subject<string>()
this.subscription = this.textFilterDebounce.pipe( this.subscription = this.textFilterDebounce
debounceTime(400), .pipe(debounceTime(400), distinctUntilChanged())
distinctUntilChanged() .subscribe((text) => {
).subscribe(text => {
this._textFilter = text this._textFilter = text
this.documentService.searchQuery = text this.documentService.searchQuery = text
this.updateRules() this.updateRules()
}) })
if (this._textFilter) this.documentService.searchQuery = this._textFilter if (this._textFilter) this.documentService.searchQuery = this._textFilter
} }
ngOnDestroy() { ngOnDestroy() {
@ -324,11 +447,17 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
addCorrespondent(correspondentId: number) { addCorrespondent(correspondentId: number) {
this.correspondentSelectionModel.set(correspondentId, ToggleableItemState.Selected) this.correspondentSelectionModel.set(
correspondentId,
ToggleableItemState.Selected
)
} }
addDocumentType(documentTypeId: number) { addDocumentType(documentTypeId: number) {
this.documentTypeSelectionModel.set(documentTypeId, ToggleableItemState.Selected) this.documentTypeSelectionModel.set(
documentTypeId,
ToggleableItemState.Selected
)
} }
onTagsDropdownOpen() { onTagsDropdownOpen() {
@ -344,8 +473,11 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
changeTextFilterTarget(target) { changeTextFilterTarget(target) {
if (this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE && target != TEXT_FILTER_TARGET_FULLTEXT_MORELIKE) { if (
this._textFilter = "" this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE &&
target != TEXT_FILTER_TARGET_FULLTEXT_MORELIKE
) {
this._textFilter = ''
} }
this.textFilterTarget = target this.textFilterTarget = target
this.textFilterInput.nativeElement.focus() this.textFilterInput.nativeElement.focus()

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { SaveViewConfigDialogComponent } from './save-view-config-dialog.component'; import { SaveViewConfigDialogComponent } from './save-view-config-dialog.component'
describe('SaveViewConfigDialogComponent', () => { describe('SaveViewConfigDialogComponent', () => {
let component: SaveViewConfigDialogComponent; let component: SaveViewConfigDialogComponent
let fixture: ComponentFixture<SaveViewConfigDialogComponent>; let fixture: ComponentFixture<SaveViewConfigDialogComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ SaveViewConfigDialogComponent ] declarations: [SaveViewConfigDialogComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(SaveViewConfigDialogComponent); fixture = TestBed.createComponent(SaveViewConfigDialogComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,15 +1,14 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
@Component({ @Component({
selector: 'app-save-view-config-dialog', selector: 'app-save-view-config-dialog',
templateUrl: './save-view-config-dialog.component.html', templateUrl: './save-view-config-dialog.component.html',
styleUrls: ['./save-view-config-dialog.component.scss'] styleUrls: ['./save-view-config-dialog.component.scss'],
}) })
export class SaveViewConfigDialogComponent implements OnInit { export class SaveViewConfigDialogComponent implements OnInit {
constructor(private modal: NgbActiveModal) {}
constructor(private modal: NgbActiveModal) { }
@Output() @Output()
public saveClicked = new EventEmitter() public saveClicked = new EventEmitter()
@ -22,7 +21,7 @@ export class SaveViewConfigDialogComponent implements OnInit {
closeEnabled = false closeEnabled = false
_defaultName = "" _defaultName = ''
get defaultName() { get defaultName() {
return this._defaultName return this._defaultName
@ -31,7 +30,7 @@ export class SaveViewConfigDialogComponent implements OnInit {
@Input() @Input()
set defaultName(value: string) { set defaultName(value: string) {
this._defaultName = value this._defaultName = value
this.saveViewConfigForm.patchValue({name: value}) this.saveViewConfigForm.patchValue({ name: value })
} }
saveViewConfigForm = new FormGroup({ saveViewConfigForm = new FormGroup({
@ -44,7 +43,7 @@ export class SaveViewConfigDialogComponent implements OnInit {
// wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM // wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM
setTimeout(() => { setTimeout(() => {
this.closeEnabled = true this.closeEnabled = true
}); })
} }
save() { save() {

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog.component'; import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog.component'
describe('CorrespondentEditDialogComponent', () => { describe('CorrespondentEditDialogComponent', () => {
let component: CorrespondentEditDialogComponent; let component: CorrespondentEditDialogComponent
let fixture: ComponentFixture<CorrespondentEditDialogComponent>; let fixture: ComponentFixture<CorrespondentEditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ CorrespondentEditDialogComponent ] declarations: [CorrespondentEditDialogComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(CorrespondentEditDialogComponent); fixture = TestBed.createComponent(CorrespondentEditDialogComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,19 +1,22 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'; import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'; import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service'
@Component({ @Component({
selector: 'app-correspondent-edit-dialog', selector: 'app-correspondent-edit-dialog',
templateUrl: './correspondent-edit-dialog.component.html', templateUrl: './correspondent-edit-dialog.component.html',
styleUrls: ['./correspondent-edit-dialog.component.scss'] styleUrls: ['./correspondent-edit-dialog.component.scss'],
}) })
export class CorrespondentEditDialogComponent extends EditDialogComponent<PaperlessCorrespondent> { export class CorrespondentEditDialogComponent extends EditDialogComponent<PaperlessCorrespondent> {
constructor(
constructor(service: CorrespondentService, activeModal: NgbActiveModal, toastService: ToastService) { service: CorrespondentService,
activeModal: NgbActiveModal,
toastService: ToastService
) {
super(service, activeModal, toastService) super(service, activeModal, toastService)
} }
@ -29,9 +32,8 @@ export class CorrespondentEditDialogComponent extends EditDialogComponent<Paperl
return new FormGroup({ return new FormGroup({
name: new FormControl(''), name: new FormControl(''),
matching_algorithm: new FormControl(1), matching_algorithm: new FormControl(1),
match: new FormControl(""), match: new FormControl(''),
is_insensitive: new FormControl(true) is_insensitive: new FormControl(true),
}) })
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { CorrespondentListComponent } from './correspondent-list.component'; import { CorrespondentListComponent } from './correspondent-list.component'
describe('CorrespondentListComponent', () => { describe('CorrespondentListComponent', () => {
let component: CorrespondentListComponent; let component: CorrespondentListComponent
let fixture: ComponentFixture<CorrespondentListComponent>; let fixture: ComponentFixture<CorrespondentListComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ CorrespondentListComponent ] declarations: [CorrespondentListComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(CorrespondentListComponent); fixture = TestBed.createComponent(CorrespondentListComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,25 +1,31 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { FILTER_CORRESPONDENT } from 'src/app/data/filter-rule-type'; import { FILTER_CORRESPONDENT } from 'src/app/data/filter-rule-type'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'; import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service'
import { GenericListComponent } from '../generic-list/generic-list.component'; import { GenericListComponent } from '../generic-list/generic-list.component'
import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog/correspondent-edit-dialog.component'; import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog/correspondent-edit-dialog.component'
@Component({ @Component({
selector: 'app-correspondent-list', selector: 'app-correspondent-list',
templateUrl: './correspondent-list.component.html', templateUrl: './correspondent-list.component.html',
styleUrls: ['./correspondent-list.component.scss'] styleUrls: ['./correspondent-list.component.scss'],
}) })
export class CorrespondentListComponent extends GenericListComponent<PaperlessCorrespondent> { export class CorrespondentListComponent extends GenericListComponent<PaperlessCorrespondent> {
constructor(
constructor(correspondentsService: CorrespondentService, modalService: NgbModal, correspondentsService: CorrespondentService,
modalService: NgbModal,
private list: DocumentListViewService, private list: DocumentListViewService,
toastService: ToastService toastService: ToastService
) { ) {
super(correspondentsService,modalService,CorrespondentEditDialogComponent, toastService) super(
correspondentsService,
modalService,
CorrespondentEditDialogComponent,
toastService
)
} }
getDeleteMessage(object: PaperlessCorrespondent) { getDeleteMessage(object: PaperlessCorrespondent) {
@ -27,6 +33,8 @@ export class CorrespondentListComponent extends GenericListComponent<PaperlessCo
} }
filterDocuments(object: PaperlessCorrespondent) { filterDocuments(object: PaperlessCorrespondent) {
this.list.quickFilter([{rule_type: FILTER_CORRESPONDENT, value: object.id.toString()}]) this.list.quickFilter([
{ rule_type: FILTER_CORRESPONDENT, value: object.id.toString() },
])
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog.component'; import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog.component'
describe('DocumentTypeEditDialogComponent', () => { describe('DocumentTypeEditDialogComponent', () => {
let component: DocumentTypeEditDialogComponent; let component: DocumentTypeEditDialogComponent
let fixture: ComponentFixture<DocumentTypeEditDialogComponent>; let fixture: ComponentFixture<DocumentTypeEditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DocumentTypeEditDialogComponent ] declarations: [DocumentTypeEditDialogComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DocumentTypeEditDialogComponent); fixture = TestBed.createComponent(DocumentTypeEditDialogComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,19 +1,22 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'; import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'; import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service'
@Component({ @Component({
selector: 'app-document-type-edit-dialog', selector: 'app-document-type-edit-dialog',
templateUrl: './document-type-edit-dialog.component.html', templateUrl: './document-type-edit-dialog.component.html',
styleUrls: ['./document-type-edit-dialog.component.scss'] styleUrls: ['./document-type-edit-dialog.component.scss'],
}) })
export class DocumentTypeEditDialogComponent extends EditDialogComponent<PaperlessDocumentType> { export class DocumentTypeEditDialogComponent extends EditDialogComponent<PaperlessDocumentType> {
constructor(
constructor(service: DocumentTypeService, activeModal: NgbActiveModal, toastService: ToastService) { service: DocumentTypeService,
activeModal: NgbActiveModal,
toastService: ToastService
) {
super(service, activeModal, toastService) super(service, activeModal, toastService)
} }
@ -29,9 +32,8 @@ export class DocumentTypeEditDialogComponent extends EditDialogComponent<Paperle
return new FormGroup({ return new FormGroup({
name: new FormControl(''), name: new FormControl(''),
matching_algorithm: new FormControl(1), matching_algorithm: new FormControl(1),
match: new FormControl(""), match: new FormControl(''),
is_insensitive: new FormControl(true) is_insensitive: new FormControl(true),
}) })
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DocumentTypeListComponent } from './document-type-list.component'; import { DocumentTypeListComponent } from './document-type-list.component'
describe('DocumentTypeListComponent', () => { describe('DocumentTypeListComponent', () => {
let component: DocumentTypeListComponent; let component: DocumentTypeListComponent
let fixture: ComponentFixture<DocumentTypeListComponent>; let fixture: ComponentFixture<DocumentTypeListComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DocumentTypeListComponent ] declarations: [DocumentTypeListComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DocumentTypeListComponent); fixture = TestBed.createComponent(DocumentTypeListComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,21 +1,22 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { FILTER_DOCUMENT_TYPE } from 'src/app/data/filter-rule-type'; import { FILTER_DOCUMENT_TYPE } from 'src/app/data/filter-rule-type'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'; import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service'
import { GenericListComponent } from '../generic-list/generic-list.component'; import { GenericListComponent } from '../generic-list/generic-list.component'
import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog/document-type-edit-dialog.component'; import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog/document-type-edit-dialog.component'
@Component({ @Component({
selector: 'app-document-type-list', selector: 'app-document-type-list',
templateUrl: './document-type-list.component.html', templateUrl: './document-type-list.component.html',
styleUrls: ['./document-type-list.component.scss'] styleUrls: ['./document-type-list.component.scss'],
}) })
export class DocumentTypeListComponent extends GenericListComponent<PaperlessDocumentType> { export class DocumentTypeListComponent extends GenericListComponent<PaperlessDocumentType> {
constructor(
constructor(service: DocumentTypeService, modalService: NgbModal, service: DocumentTypeService,
modalService: NgbModal,
private list: DocumentListViewService, private list: DocumentListViewService,
toastService: ToastService toastService: ToastService
) { ) {
@ -26,8 +27,9 @@ export class DocumentTypeListComponent extends GenericListComponent<PaperlessDoc
return $localize`Do you really want to delete the document type "${object.name}"?` return $localize`Do you really want to delete the document type "${object.name}"?`
} }
filterDocuments(object: PaperlessDocumentType) { filterDocuments(object: PaperlessDocumentType) {
this.list.quickFilter([{rule_type: FILTER_DOCUMENT_TYPE, value: object.id.toString()}]) this.list.quickFilter([
{ rule_type: FILTER_DOCUMENT_TYPE, value: object.id.toString() },
])
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { GenericListComponent } from './generic-list.component'; import { GenericListComponent } from './generic-list.component'
describe('GenericListComponent', () => { describe('GenericListComponent', () => {
let component: GenericListComponent; let component: GenericListComponent
let fixture: ComponentFixture<GenericListComponent>; let fixture: ComponentFixture<GenericListComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ GenericListComponent ] declarations: [GenericListComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(GenericListComponent); fixture = TestBed.createComponent(GenericListComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,25 +1,39 @@
import { Directive, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core'; import {
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; Directive,
import { Subject, Subscription } from 'rxjs'; OnDestroy,
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; OnInit,
import { MatchingModel, MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model'; QueryList,
import { ObjectWithId } from 'src/app/data/object-with-id'; ViewChildren,
import { SortableDirective, SortEvent } from 'src/app/directives/sortable.directive'; } from '@angular/core'
import { AbstractNameFilterService } from 'src/app/services/rest/abstract-name-filter-service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ToastService } from 'src/app/services/toast.service'; import { Subject, Subscription } from 'rxjs'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
import {
MatchingModel,
MATCHING_ALGORITHMS,
MATCH_AUTO,
} from 'src/app/data/matching-model'
import { ObjectWithId } from 'src/app/data/object-with-id'
import {
SortableDirective,
SortEvent,
} from 'src/app/directives/sortable.directive'
import { AbstractNameFilterService } from 'src/app/services/rest/abstract-name-filter-service'
import { ToastService } from 'src/app/services/toast.service'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
@Directive() @Directive()
export abstract class GenericListComponent<T extends ObjectWithId> implements OnInit, OnDestroy { export abstract class GenericListComponent<T extends ObjectWithId>
implements OnInit, OnDestroy
{
constructor( constructor(
private service: AbstractNameFilterService<T>, private service: AbstractNameFilterService<T>,
private modalService: NgbModal, private modalService: NgbModal,
private editDialogComponent: any, private editDialogComponent: any,
private toastService: ToastService) { private toastService: ToastService
} ) {}
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>; @ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
public data: T[] = [] public data: T[] = []
@ -38,9 +52,11 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
if (o.matching_algorithm == MATCH_AUTO) { if (o.matching_algorithm == MATCH_AUTO) {
return $localize`Automatic` return $localize`Automatic`
} else if (o.match && o.match.length > 0) { } else if (o.match && o.match.length > 0) {
return `${MATCHING_ALGORITHMS.find(a => a.id == o.matching_algorithm).shortName}: ${o.match}` return `${
MATCHING_ALGORITHMS.find((a) => a.id == o.matching_algorithm).shortName
}: ${o.match}`
} else { } else {
return "-" return '-'
} }
} }
@ -50,16 +66,14 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
this.reloadData() this.reloadData()
} }
ngOnInit(): void { ngOnInit(): void {
this.reloadData() this.reloadData()
this.nameFilterDebounce = new Subject<string>() this.nameFilterDebounce = new Subject<string>()
this.subscription = this.nameFilterDebounce.pipe( this.subscription = this.nameFilterDebounce
debounceTime(400), .pipe(debounceTime(400), distinctUntilChanged())
distinctUntilChanged() .subscribe((title) => {
).subscribe(title => {
this._nameFilter = title this._nameFilter = title
this.page = 1 this.page = 1
this.reloadData() this.reloadData()
@ -71,25 +85,37 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
} }
reloadData() { reloadData() {
this.service.listFiltered(this.page, null, this.sortField, this.sortReverse, this._nameFilter).subscribe(c => { this.service
.listFiltered(
this.page,
null,
this.sortField,
this.sortReverse,
this._nameFilter
)
.subscribe((c) => {
this.data = c.results this.data = c.results
this.collectionSize = c.count this.collectionSize = c.count
}); })
} }
openCreateDialog() { openCreateDialog() {
var activeModal = this.modalService.open(this.editDialogComponent, {backdrop: 'static'}) var activeModal = this.modalService.open(this.editDialogComponent, {
backdrop: 'static',
})
activeModal.componentInstance.dialogMode = 'create' activeModal.componentInstance.dialogMode = 'create'
activeModal.componentInstance.success.subscribe(o => { activeModal.componentInstance.success.subscribe((o) => {
this.reloadData() this.reloadData()
}) })
} }
openEditDialog(object: T) { openEditDialog(object: T) {
var activeModal = this.modalService.open(this.editDialogComponent, {backdrop: 'static'}) var activeModal = this.modalService.open(this.editDialogComponent, {
backdrop: 'static',
})
activeModal.componentInstance.object = object activeModal.componentInstance.object = object
activeModal.componentInstance.dialogMode = 'edit' activeModal.componentInstance.dialogMode = 'edit'
activeModal.componentInstance.success.subscribe(o => { activeModal.componentInstance.success.subscribe((o) => {
this.reloadData() this.reloadData()
}) })
} }
@ -99,23 +125,31 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
} }
openDeleteDialog(object: T) { openDeleteDialog(object: T) {
var activeModal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) var activeModal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
activeModal.componentInstance.title = $localize`Confirm delete` activeModal.componentInstance.title = $localize`Confirm delete`
activeModal.componentInstance.messageBold = this.getDeleteMessage(object) activeModal.componentInstance.messageBold = this.getDeleteMessage(object)
activeModal.componentInstance.message = $localize`Associated documents will not be deleted.` activeModal.componentInstance.message = $localize`Associated documents will not be deleted.`
activeModal.componentInstance.btnClass = "btn-danger" activeModal.componentInstance.btnClass = 'btn-danger'
activeModal.componentInstance.btnCaption = $localize`Delete` activeModal.componentInstance.btnCaption = $localize`Delete`
activeModal.componentInstance.confirmClicked.subscribe(() => { activeModal.componentInstance.confirmClicked.subscribe(() => {
activeModal.componentInstance.buttonsEnabled = false activeModal.componentInstance.buttonsEnabled = false
this.service.delete(object).subscribe(_ => { this.service.delete(object).subscribe(
(_) => {
activeModal.close() activeModal.close()
this.reloadData() this.reloadData()
}, error => { },
(error) => {
activeModal.componentInstance.buttonsEnabled = true activeModal.componentInstance.buttonsEnabled = true
this.toastService.showError($localize`Error while deleting element: ${JSON.stringify(error.error)}`) this.toastService.showError(
}) $localize`Error while deleting element: ${JSON.stringify(
error.error
)}`
)
} }
) )
})
} }
get nameFilter() { get nameFilter() {

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { LogsComponent } from './logs.component'; import { LogsComponent } from './logs.component'
describe('LogsComponent', () => { describe('LogsComponent', () => {
let component: LogsComponent; let component: LogsComponent
let fixture: ComponentFixture<LogsComponent>; let fixture: ComponentFixture<LogsComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ LogsComponent ] declarations: [LogsComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(LogsComponent); fixture = TestBed.createComponent(LogsComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,14 +1,19 @@
import { Component, ElementRef, OnInit, AfterViewChecked, ViewChild } from '@angular/core'; import {
import { LogService } from 'src/app/services/rest/log.service'; Component,
ElementRef,
OnInit,
AfterViewChecked,
ViewChild,
} from '@angular/core'
import { LogService } from 'src/app/services/rest/log.service'
@Component({ @Component({
selector: 'app-logs', selector: 'app-logs',
templateUrl: './logs.component.html', templateUrl: './logs.component.html',
styleUrls: ['./logs.component.scss'] styleUrls: ['./logs.component.scss'],
}) })
export class LogsComponent implements OnInit, AfterViewChecked { export class LogsComponent implements OnInit, AfterViewChecked {
constructor(private logService: LogService) {}
constructor(private logService: LogService) { }
logs: string[] = [] logs: string[] = []
@ -19,7 +24,7 @@ export class LogsComponent implements OnInit, AfterViewChecked {
@ViewChild('logContainer') logContainer: ElementRef @ViewChild('logContainer') logContainer: ElementRef
ngOnInit(): void { ngOnInit(): void {
this.logService.list().subscribe(result => { this.logService.list().subscribe((result) => {
this.logFiles = result this.logFiles = result
if (this.logFiles.length > 0) { if (this.logFiles.length > 0) {
this.activeLog = this.logFiles[0] this.activeLog = this.logFiles[0]
@ -29,25 +34,28 @@ export class LogsComponent implements OnInit, AfterViewChecked {
} }
ngAfterViewChecked() { ngAfterViewChecked() {
this.scrollToBottom(); this.scrollToBottom()
} }
reloadLogs() { reloadLogs() {
this.logService.get(this.activeLog).subscribe(result => { this.logService.get(this.activeLog).subscribe(
(result) => {
this.logs = result this.logs = result
}, error => { },
(error) => {
this.logs = [] this.logs = []
}) }
)
} }
getLogLevel(log: string) { getLogLevel(log: string) {
if (log.indexOf("[DEBUG]") != -1) { if (log.indexOf('[DEBUG]') != -1) {
return 10 return 10
} else if (log.indexOf("[WARNING]") != -1) { } else if (log.indexOf('[WARNING]') != -1) {
return 30 return 30
} else if (log.indexOf("[ERROR]") != -1) { } else if (log.indexOf('[ERROR]') != -1) {
return 40 return 40
} else if (log.indexOf("[CRITICAL]") != -1) { } else if (log.indexOf('[CRITICAL]') != -1) {
return 50 return 50
} else { } else {
return 20 return 20
@ -58,8 +66,7 @@ export class LogsComponent implements OnInit, AfterViewChecked {
this.logContainer?.nativeElement.scroll({ this.logContainer?.nativeElement.scroll({
top: this.logContainer.nativeElement.scrollHeight, top: this.logContainer.nativeElement.scrollHeight,
left: 0, left: 0,
behavior: 'auto' behavior: 'auto',
}); })
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { SettingsComponent } from './settings.component'; import { SettingsComponent } from './settings.component'
describe('SettingsComponent', () => { describe('SettingsComponent', () => {
let component: SettingsComponent; let component: SettingsComponent
let fixture: ComponentFixture<SettingsComponent>; let fixture: ComponentFixture<SettingsComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ SettingsComponent ] declarations: [SettingsComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(SettingsComponent); fixture = TestBed.createComponent(SettingsComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,39 +1,49 @@
import { Component, Inject, LOCALE_ID, OnInit, OnDestroy, Renderer2 } from '@angular/core'; import {
import { FormControl, FormGroup } from '@angular/forms'; Component,
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'; Inject,
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; LOCALE_ID,
import { SavedViewService } from 'src/app/services/rest/saved-view.service'; OnInit,
import { LanguageOption, SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; OnDestroy,
import { ToastService } from 'src/app/services/toast.service'; Renderer2,
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'; } from '@angular/core'
import { Observable, Subscription, BehaviorSubject } from 'rxjs'; import { FormControl, FormGroup } from '@angular/forms'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import {
LanguageOption,
SettingsService,
SETTINGS_KEYS,
} from 'src/app/services/settings.service'
import { ToastService } from 'src/app/services/toast.service'
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'
import { Observable, Subscription, BehaviorSubject } from 'rxjs'
@Component({ @Component({
selector: 'app-settings', selector: 'app-settings',
templateUrl: './settings.component.html', templateUrl: './settings.component.html',
styleUrls: ['./settings.component.scss'] styleUrls: ['./settings.component.scss'],
}) })
export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
savedViewGroup = new FormGroup({}) savedViewGroup = new FormGroup({})
settingsForm = new FormGroup({ settingsForm = new FormGroup({
'bulkEditConfirmationDialogs': new FormControl(null), bulkEditConfirmationDialogs: new FormControl(null),
'bulkEditApplyOnClose': new FormControl(null), bulkEditApplyOnClose: new FormControl(null),
'documentListItemPerPage': new FormControl(null), documentListItemPerPage: new FormControl(null),
'darkModeUseSystem': new FormControl(null), darkModeUseSystem: new FormControl(null),
'darkModeEnabled': new FormControl(null), darkModeEnabled: new FormControl(null),
'darkModeInvertThumbs': new FormControl(null), darkModeInvertThumbs: new FormControl(null),
'themeColor': new FormControl(null), themeColor: new FormControl(null),
'useNativePdfViewer': new FormControl(null), useNativePdfViewer: new FormControl(null),
'savedViews': this.savedViewGroup, savedViews: this.savedViewGroup,
'displayLanguage': new FormControl(null), displayLanguage: new FormControl(null),
'dateLocale': new FormControl(null), dateLocale: new FormControl(null),
'dateFormat': new FormControl(null), dateFormat: new FormControl(null),
'notificationsConsumerNewDocument': new FormControl(null), notificationsConsumerNewDocument: new FormControl(null),
'notificationsConsumerSuccess': new FormControl(null), notificationsConsumerSuccess: new FormControl(null),
'notificationsConsumerFailed': new FormControl(null), notificationsConsumerFailed: new FormControl(null),
'notificationsConsumerSuppressOnDashboard': new FormControl(null), notificationsConsumerSuppressOnDashboard: new FormControl(null),
}) })
savedViews: PaperlessSavedView[] savedViews: PaperlessSavedView[]
@ -44,7 +54,11 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
isDirty: Boolean = false isDirty: Boolean = false
get computedDateLocale(): string { get computedDateLocale(): string {
return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage || this.currentLocale return (
this.settingsForm.value.dateLocale ||
this.settingsForm.value.displayLanguage ||
this.currentLocale
)
} }
constructor( constructor(
@ -53,48 +67,71 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
private toastService: ToastService, private toastService: ToastService,
private settings: SettingsService, private settings: SettingsService,
@Inject(LOCALE_ID) public currentLocale: string @Inject(LOCALE_ID) public currentLocale: string
) { } ) {}
ngOnInit() { ngOnInit() {
this.savedViewService.listAll().subscribe(r => { this.savedViewService.listAll().subscribe((r) => {
this.savedViews = r.results this.savedViews = r.results
let storeData = { let storeData = {
'bulkEditConfirmationDialogs': this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS), bulkEditConfirmationDialogs: this.settings.get(
'bulkEditApplyOnClose': this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE), SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS
'documentListItemPerPage': this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE), ),
'darkModeUseSystem': this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM), bulkEditApplyOnClose: this.settings.get(
'darkModeEnabled': this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED), SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE
'darkModeInvertThumbs': this.settings.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED), ),
'themeColor': this.settings.get(SETTINGS_KEYS.THEME_COLOR), documentListItemPerPage: this.settings.get(
'useNativePdfViewer': this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER), SETTINGS_KEYS.DOCUMENT_LIST_SIZE
'savedViews': {}, ),
'displayLanguage': this.settings.getLanguage(), darkModeUseSystem: this.settings.get(
'dateLocale': this.settings.get(SETTINGS_KEYS.DATE_LOCALE), SETTINGS_KEYS.DARK_MODE_USE_SYSTEM
'dateFormat': this.settings.get(SETTINGS_KEYS.DATE_FORMAT), ),
'notificationsConsumerNewDocument': this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT), darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED),
'notificationsConsumerSuccess': this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS), darkModeInvertThumbs: this.settings.get(
'notificationsConsumerFailed': this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED), SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED
'notificationsConsumerSuppressOnDashboard': this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD), ),
themeColor: this.settings.get(SETTINGS_KEYS.THEME_COLOR),
useNativePdfViewer: this.settings.get(
SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER
),
savedViews: {},
displayLanguage: this.settings.getLanguage(),
dateLocale: this.settings.get(SETTINGS_KEYS.DATE_LOCALE),
dateFormat: this.settings.get(SETTINGS_KEYS.DATE_FORMAT),
notificationsConsumerNewDocument: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT
),
notificationsConsumerSuccess: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS
),
notificationsConsumerFailed: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED
),
notificationsConsumerSuppressOnDashboard: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD
),
} }
for (let view of this.savedViews) { for (let view of this.savedViews) {
storeData.savedViews[view.id.toString()] = { storeData.savedViews[view.id.toString()] = {
"id": view.id, id: view.id,
"name": view.name, name: view.name,
"show_on_dashboard": view.show_on_dashboard, show_on_dashboard: view.show_on_dashboard,
"show_in_sidebar": view.show_in_sidebar show_in_sidebar: view.show_in_sidebar,
} }
this.savedViewGroup.addControl(view.id.toString(), new FormGroup({ this.savedViewGroup.addControl(
"id": new FormControl(null), view.id.toString(),
"name": new FormControl(null), new FormGroup({
"show_on_dashboard": new FormControl(null), id: new FormControl(null),
"show_in_sidebar": new FormControl(null) name: new FormControl(null),
})) show_on_dashboard: new FormControl(null),
show_in_sidebar: new FormControl(null),
})
)
} }
this.store = new BehaviorSubject(storeData) this.store = new BehaviorSubject(storeData)
this.storeSub = this.store.asObservable().subscribe(state => { this.storeSub = this.store.asObservable().subscribe((state) => {
this.settingsForm.patchValue(state, { emitEvent: false }) this.settingsForm.patchValue(state, { emitEvent: false })
}) })
@ -102,45 +139,93 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable()) this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable())
// Record dirty in case we need to 'undo' appearance settings if not saved on close // Record dirty in case we need to 'undo' appearance settings if not saved on close
this.isDirty$.subscribe(dirty => { this.isDirty$.subscribe((dirty) => {
this.isDirty = dirty this.isDirty = dirty
}) })
// "Live" visual changes prior to save // "Live" visual changes prior to save
this.settingsForm.valueChanges.subscribe(() => { this.settingsForm.valueChanges.subscribe(() => {
this.settings.updateAppearanceSettings(this.settingsForm.get('darkModeUseSystem').value, this.settingsForm.get('darkModeEnabled').value, this.settingsForm.get('themeColor').value) this.settings.updateAppearanceSettings(
this.settingsForm.get('darkModeUseSystem').value,
this.settingsForm.get('darkModeEnabled').value,
this.settingsForm.get('themeColor').value
)
}) })
}) })
} }
ngOnDestroy() { ngOnDestroy() {
if (this.isDirty) this.settings.updateAppearanceSettings() // in case user changed appearance but didnt save if (this.isDirty) this.settings.updateAppearanceSettings() // in case user changed appearance but didnt save
this.storeSub && this.storeSub.unsubscribe(); this.storeSub && this.storeSub.unsubscribe()
} }
deleteSavedView(savedView: PaperlessSavedView) { deleteSavedView(savedView: PaperlessSavedView) {
this.savedViewService.delete(savedView).subscribe(() => { this.savedViewService.delete(savedView).subscribe(() => {
this.savedViewGroup.removeControl(savedView.id.toString()) this.savedViewGroup.removeControl(savedView.id.toString())
this.savedViews.splice(this.savedViews.indexOf(savedView), 1) this.savedViews.splice(this.savedViews.indexOf(savedView), 1)
this.toastService.showInfo($localize`Saved view "${savedView.name}" deleted.`) this.toastService.showInfo(
$localize`Saved view "${savedView.name}" deleted.`
)
}) })
} }
private saveLocalSettings() { private saveLocalSettings() {
this.settings.set(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE, this.settingsForm.value.bulkEditApplyOnClose) this.settings.set(
this.settings.set(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS, this.settingsForm.value.bulkEditConfirmationDialogs) SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE,
this.settings.set(SETTINGS_KEYS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage) this.settingsForm.value.bulkEditApplyOnClose
this.settings.set(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem) )
this.settings.set(SETTINGS_KEYS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString()) this.settings.set(
this.settings.set(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED, (this.settingsForm.value.darkModeInvertThumbs == true).toString()) SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS,
this.settings.set(SETTINGS_KEYS.THEME_COLOR, (this.settingsForm.value.themeColor).toString()) this.settingsForm.value.bulkEditConfirmationDialogs
this.settings.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, this.settingsForm.value.useNativePdfViewer) )
this.settings.set(SETTINGS_KEYS.DATE_LOCALE, this.settingsForm.value.dateLocale) this.settings.set(
this.settings.set(SETTINGS_KEYS.DATE_FORMAT, this.settingsForm.value.dateFormat) SETTINGS_KEYS.DOCUMENT_LIST_SIZE,
this.settings.set(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT, this.settingsForm.value.notificationsConsumerNewDocument) this.settingsForm.value.documentListItemPerPage
this.settings.set(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS, this.settingsForm.value.notificationsConsumerSuccess) )
this.settings.set(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED, this.settingsForm.value.notificationsConsumerFailed) this.settings.set(
this.settings.set(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD, this.settingsForm.value.notificationsConsumerSuppressOnDashboard) SETTINGS_KEYS.DARK_MODE_USE_SYSTEM,
this.settingsForm.value.darkModeUseSystem
)
this.settings.set(
SETTINGS_KEYS.DARK_MODE_ENABLED,
(this.settingsForm.value.darkModeEnabled == true).toString()
)
this.settings.set(
SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED,
(this.settingsForm.value.darkModeInvertThumbs == true).toString()
)
this.settings.set(
SETTINGS_KEYS.THEME_COLOR,
this.settingsForm.value.themeColor.toString()
)
this.settings.set(
SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER,
this.settingsForm.value.useNativePdfViewer
)
this.settings.set(
SETTINGS_KEYS.DATE_LOCALE,
this.settingsForm.value.dateLocale
)
this.settings.set(
SETTINGS_KEYS.DATE_FORMAT,
this.settingsForm.value.dateFormat
)
this.settings.set(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT,
this.settingsForm.value.notificationsConsumerNewDocument
)
this.settings.set(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS,
this.settingsForm.value.notificationsConsumerSuccess
)
this.settings.set(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED,
this.settingsForm.value.notificationsConsumerFailed
)
this.settings.set(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD,
this.settingsForm.value.notificationsConsumerSuppressOnDashboard
)
this.settings.setLanguage(this.settingsForm.value.displayLanguage) this.settings.setLanguage(this.settingsForm.value.displayLanguage)
this.store.next(this.settingsForm.value) this.store.next(this.settingsForm.value)
this.documentListViewService.updatePageSize() this.documentListViewService.updatePageSize()
@ -149,14 +234,14 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
} }
get displayLanguageOptions(): LanguageOption[] { get displayLanguageOptions(): LanguageOption[] {
return [ return [{ code: '', name: $localize`Use system language` }].concat(
{code: "", name: $localize`Use system language`} this.settings.getLanguageOptions()
].concat(this.settings.getLanguageOptions()) )
} }
get dateLocaleOptions(): LanguageOption[] { get dateLocaleOptions(): LanguageOption[] {
return [ return [
{code: "", name: $localize`Use date format of display language`} { code: '', name: $localize`Use date format of display language` },
].concat(this.settings.getDateLocaleOptions()) ].concat(this.settings.getDateLocaleOptions())
} }
@ -170,18 +255,24 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
x.push(this.savedViewGroup.value[id]) x.push(this.savedViewGroup.value[id])
} }
if (x.length > 0) { if (x.length > 0) {
this.savedViewService.patchMany(x).subscribe(s => { this.savedViewService.patchMany(x).subscribe(
(s) => {
this.saveLocalSettings() this.saveLocalSettings()
}, error => { },
this.toastService.showError($localize`Error while storing settings on server: ${JSON.stringify(error.error)}`) (error) => {
}) this.toastService.showError(
$localize`Error while storing settings on server: ${JSON.stringify(
error.error
)}`
)
}
)
} else { } else {
this.saveLocalSettings() this.saveLocalSettings()
} }
} }
clearThemeColor() { clearThemeColor() {
this.settingsForm.get('themeColor').patchValue(''); this.settingsForm.get('themeColor').patchValue('')
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { TagEditDialogComponent } from './tag-edit-dialog.component'; import { TagEditDialogComponent } from './tag-edit-dialog.component'
describe('TagEditDialogComponent', () => { describe('TagEditDialogComponent', () => {
let component: TagEditDialogComponent; let component: TagEditDialogComponent
let fixture: ComponentFixture<TagEditDialogComponent>; let fixture: ComponentFixture<TagEditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ TagEditDialogComponent ] declarations: [TagEditDialogComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(TagEditDialogComponent); fixture = TestBed.createComponent(TagEditDialogComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,20 +1,23 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'; import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { PaperlessTag } from 'src/app/data/paperless-tag'; import { PaperlessTag } from 'src/app/data/paperless-tag'
import { TagService } from 'src/app/services/rest/tag.service'; import { TagService } from 'src/app/services/rest/tag.service'
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service'
import { randomColor } from 'src/app/utils/color'; import { randomColor } from 'src/app/utils/color'
@Component({ @Component({
selector: 'app-tag-edit-dialog', selector: 'app-tag-edit-dialog',
templateUrl: './tag-edit-dialog.component.html', templateUrl: './tag-edit-dialog.component.html',
styleUrls: ['./tag-edit-dialog.component.scss'] styleUrls: ['./tag-edit-dialog.component.scss'],
}) })
export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> { export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
constructor(
constructor(service: TagService, activeModal: NgbActiveModal, toastService: ToastService) { service: TagService,
activeModal: NgbActiveModal,
toastService: ToastService
) {
super(service, activeModal, toastService) super(service, activeModal, toastService)
} }
@ -32,9 +35,8 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
color: new FormControl(randomColor()), color: new FormControl(randomColor()),
is_inbox_tag: new FormControl(false), is_inbox_tag: new FormControl(false),
matching_algorithm: new FormControl(1), matching_algorithm: new FormControl(1),
match: new FormControl(""), match: new FormControl(''),
is_insensitive: new FormControl(true) is_insensitive: new FormControl(true),
}) })
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { TagListComponent } from './tag-list.component'; import { TagListComponent } from './tag-list.component'
describe('TagListComponent', () => { describe('TagListComponent', () => {
let component: TagListComponent; let component: TagListComponent
let fixture: ComponentFixture<TagListComponent>; let fixture: ComponentFixture<TagListComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ TagListComponent ] declarations: [TagListComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(TagListComponent); fixture = TestBed.createComponent(TagListComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,21 +1,22 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type'; import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type'
import { PaperlessTag } from 'src/app/data/paperless-tag'; import { PaperlessTag } from 'src/app/data/paperless-tag'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { TagService } from 'src/app/services/rest/tag.service'; import { TagService } from 'src/app/services/rest/tag.service'
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service'
import { GenericListComponent } from '../generic-list/generic-list.component'; import { GenericListComponent } from '../generic-list/generic-list.component'
import { TagEditDialogComponent } from './tag-edit-dialog/tag-edit-dialog.component'; import { TagEditDialogComponent } from './tag-edit-dialog/tag-edit-dialog.component'
@Component({ @Component({
selector: 'app-tag-list', selector: 'app-tag-list',
templateUrl: './tag-list.component.html', templateUrl: './tag-list.component.html',
styleUrls: ['./tag-list.component.scss'] styleUrls: ['./tag-list.component.scss'],
}) })
export class TagListComponent extends GenericListComponent<PaperlessTag> { export class TagListComponent extends GenericListComponent<PaperlessTag> {
constructor(
constructor(tagService: TagService, modalService: NgbModal, tagService: TagService,
modalService: NgbModal,
private list: DocumentListViewService, private list: DocumentListViewService,
toastService: ToastService toastService: ToastService
) { ) {
@ -27,7 +28,8 @@ export class TagListComponent extends GenericListComponent<PaperlessTag> {
} }
filterDocuments(object: PaperlessTag) { filterDocuments(object: PaperlessTag) {
this.list.quickFilter([{rule_type: FILTER_HAS_TAGS_ALL, value: object.id.toString()}]) this.list.quickFilter([
{ rule_type: FILTER_HAS_TAGS_ALL, value: object.id.toString() },
])
} }
} }

View File

@ -1,25 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NotFoundComponent } from './not-found.component'; import { NotFoundComponent } from './not-found.component'
describe('NotFoundComponent', () => { describe('NotFoundComponent', () => {
let component: NotFoundComponent; let component: NotFoundComponent
let fixture: ComponentFixture<NotFoundComponent>; let fixture: ComponentFixture<NotFoundComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ NotFoundComponent ] declarations: [NotFoundComponent],
}).compileComponents()
}) })
.compileComponents();
});
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(NotFoundComponent); fixture = TestBed.createComponent(NotFoundComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,15 +1,12 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
@Component({ @Component({
selector: 'app-not-found', selector: 'app-not-found',
templateUrl: './not-found.component.html', templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.scss'] styleUrls: ['./not-found.component.scss'],
}) })
export class NotFoundComponent implements OnInit { export class NotFoundComponent implements OnInit {
constructor() {}
constructor() { } ngOnInit(): void {}
ngOnInit(): void {
}
} }

View File

@ -27,40 +27,160 @@ export const FILTER_FULLTEXT_QUERY = 20
export const FILTER_FULLTEXT_MORELIKE = 21 export const FILTER_FULLTEXT_MORELIKE = 21
export const FILTER_RULE_TYPES: FilterRuleType[] = [ export const FILTER_RULE_TYPES: FilterRuleType[] = [
{
id: FILTER_TITLE,
filtervar: 'title__icontains',
datatype: 'string',
multi: false,
default: '',
},
{
id: FILTER_CONTENT,
filtervar: 'content__icontains',
datatype: 'string',
multi: false,
default: '',
},
{id: FILTER_TITLE, filtervar: "title__icontains", datatype: "string", multi: false, default: ""}, {
{id: FILTER_CONTENT, filtervar: "content__icontains", datatype: "string", multi: false, default: ""}, id: FILTER_ASN,
filtervar: 'archive_serial_number',
datatype: 'number',
multi: false,
},
{id: FILTER_ASN, filtervar: "archive_serial_number", datatype: "number", multi: false}, {
id: FILTER_CORRESPONDENT,
filtervar: 'correspondent__id',
isnull_filtervar: 'correspondent__isnull',
datatype: 'correspondent',
multi: false,
},
{
id: FILTER_DOCUMENT_TYPE,
filtervar: 'document_type__id',
isnull_filtervar: 'document_type__isnull',
datatype: 'document_type',
multi: false,
},
{id: FILTER_CORRESPONDENT, filtervar: "correspondent__id", isnull_filtervar: "correspondent__isnull", datatype: "correspondent", multi: false}, {
{id: FILTER_DOCUMENT_TYPE, filtervar: "document_type__id", isnull_filtervar: "document_type__isnull", datatype: "document_type", multi: false}, id: FILTER_IS_IN_INBOX,
filtervar: 'is_in_inbox',
datatype: 'boolean',
multi: false,
default: true,
},
{
id: FILTER_HAS_TAGS_ALL,
filtervar: 'tags__id__all',
datatype: 'tag',
multi: true,
},
{
id: FILTER_HAS_TAGS_ANY,
filtervar: 'tags__id__in',
datatype: 'tag',
multi: true,
},
{
id: FILTER_DOES_NOT_HAVE_TAG,
filtervar: 'tags__id__none',
datatype: 'tag',
multi: true,
},
{
id: FILTER_HAS_ANY_TAG,
filtervar: 'is_tagged',
datatype: 'boolean',
multi: false,
default: true,
},
{id: FILTER_IS_IN_INBOX, filtervar: "is_in_inbox", datatype: "boolean", multi: false, default: true}, {
{id: FILTER_HAS_TAGS_ALL, filtervar: "tags__id__all", datatype: "tag", multi: true}, id: FILTER_CREATED_BEFORE,
{id: FILTER_HAS_TAGS_ANY, filtervar: "tags__id__in", datatype: "tag", multi: true}, filtervar: 'created__date__lt',
{id: FILTER_DOES_NOT_HAVE_TAG, filtervar: "tags__id__none", datatype: "tag", multi: true}, datatype: 'date',
{id: FILTER_HAS_ANY_TAG, filtervar: "is_tagged", datatype: "boolean", multi: false, default: true}, multi: false,
},
{
id: FILTER_CREATED_AFTER,
filtervar: 'created__date__gt',
datatype: 'date',
multi: false,
},
{id: FILTER_CREATED_BEFORE, filtervar: "created__date__lt", datatype: "date", multi: false}, {
{id: FILTER_CREATED_AFTER, filtervar: "created__date__gt", datatype: "date", multi: false}, id: FILTER_CREATED_YEAR,
filtervar: 'created__year',
datatype: 'number',
multi: false,
},
{
id: FILTER_CREATED_MONTH,
filtervar: 'created__month',
datatype: 'number',
multi: false,
},
{
id: FILTER_CREATED_DAY,
filtervar: 'created__day',
datatype: 'number',
multi: false,
},
{id: FILTER_CREATED_YEAR, filtervar: "created__year", datatype: "number", multi: false}, {
{id: FILTER_CREATED_MONTH, filtervar: "created__month", datatype: "number", multi: false}, id: FILTER_ADDED_BEFORE,
{id: FILTER_CREATED_DAY, filtervar: "created__day", datatype: "number", multi: false}, filtervar: 'added__date__lt',
datatype: 'date',
multi: false,
},
{
id: FILTER_ADDED_AFTER,
filtervar: 'added__date__gt',
datatype: 'date',
multi: false,
},
{id: FILTER_ADDED_BEFORE, filtervar: "added__date__lt", datatype: "date", multi: false}, {
{id: FILTER_ADDED_AFTER, filtervar: "added__date__gt", datatype: "date", multi: false}, id: FILTER_MODIFIED_BEFORE,
filtervar: 'modified__date__lt',
datatype: 'date',
multi: false,
},
{
id: FILTER_MODIFIED_AFTER,
filtervar: 'modified__date__gt',
datatype: 'date',
multi: false,
},
{
id: FILTER_ASN_ISNULL,
filtervar: 'archive_serial_number__isnull',
datatype: 'boolean',
multi: false,
},
{id: FILTER_MODIFIED_BEFORE, filtervar: "modified__date__lt", datatype: "date", multi: false}, {
{id: FILTER_MODIFIED_AFTER, filtervar: "modified__date__gt", datatype: "date", multi: false}, id: FILTER_TITLE_CONTENT,
{id: FILTER_ASN_ISNULL, filtervar: "archive_serial_number__isnull", datatype: "boolean", multi: false}, filtervar: 'title_content',
datatype: 'string',
multi: false,
},
{id: FILTER_TITLE_CONTENT, filtervar: "title_content", datatype: "string", multi: false}, {
id: FILTER_FULLTEXT_QUERY,
filtervar: 'query',
datatype: 'string',
multi: false,
},
{id: FILTER_FULLTEXT_QUERY, filtervar: "query", datatype: "string", multi: false}, {
id: FILTER_FULLTEXT_MORELIKE,
{id: FILTER_FULLTEXT_MORELIKE, filtervar: "more_like_id", datatype: "number", multi: false}, filtervar: 'more_like_id',
datatype: 'number',
multi: false,
},
] ]
export interface FilterRuleType { export interface FilterRuleType {

View File

@ -1,10 +1,13 @@
import { FILTER_FULLTEXT_MORELIKE, FILTER_FULLTEXT_QUERY } from "./filter-rule-type" import {
FILTER_FULLTEXT_MORELIKE,
FILTER_FULLTEXT_QUERY,
} from './filter-rule-type'
export function cloneFilterRules(filterRules: FilterRule[]): FilterRule[] { export function cloneFilterRules(filterRules: FilterRule[]): FilterRule[] {
if (filterRules) { if (filterRules) {
let newRules: FilterRule[] = [] let newRules: FilterRule[] = []
for (let rule of filterRules) { for (let rule of filterRules) {
newRules.push({rule_type: rule.rule_type, value: rule.value}) newRules.push({ rule_type: rule.rule_type, value: rule.value })
} }
return newRules return newRules
} else { } else {
@ -13,7 +16,13 @@ export function cloneFilterRules(filterRules: FilterRule[]): FilterRule[] {
} }
export function isFullTextFilterRule(filterRules: FilterRule[]): boolean { export function isFullTextFilterRule(filterRules: FilterRule[]): boolean {
return filterRules.find(r => r.rule_type == FILTER_FULLTEXT_QUERY || r.rule_type == FILTER_FULLTEXT_MORELIKE) != null return (
filterRules.find(
(r) =>
r.rule_type == FILTER_FULLTEXT_QUERY ||
r.rule_type == FILTER_FULLTEXT_MORELIKE
) != null
)
} }
export interface FilterRule { export interface FilterRule {

View File

@ -1,5 +1,4 @@
import { ObjectWithId } from './object-with-id'; import { ObjectWithId } from './object-with-id'
export const MATCH_ANY = 1 export const MATCH_ANY = 1
export const MATCH_ALL = 2 export const MATCH_ALL = 2
@ -9,16 +8,39 @@ export const MATCH_FUZZY = 5
export const MATCH_AUTO = 6 export const MATCH_AUTO = 6
export const MATCHING_ALGORITHMS = [ export const MATCHING_ALGORITHMS = [
{id: MATCH_ANY, shortName: $localize`Any word`, name: $localize`Any: Document contains any of these words (space separated)`}, {
{id: MATCH_ALL, shortName: $localize`All words`, name: $localize`All: Document contains all of these words (space separated)`}, id: MATCH_ANY,
{id: MATCH_LITERAL, shortName: $localize`Exact match`, name: $localize`Exact: Document contains this string`}, shortName: $localize`Any word`,
{id: MATCH_REGEX, shortName: $localize`Regular expression`, name: $localize`Regular expression: Document matches this regular expression`}, name: $localize`Any: Document contains any of these words (space separated)`,
{id: MATCH_FUZZY, shortName: $localize`Fuzzy word`, name: $localize`Fuzzy: Document contains a word similar to this word`}, },
{id: MATCH_AUTO, shortName: $localize`Automatic`, name: $localize`Auto: Learn matching automatically`}, {
id: MATCH_ALL,
shortName: $localize`All words`,
name: $localize`All: Document contains all of these words (space separated)`,
},
{
id: MATCH_LITERAL,
shortName: $localize`Exact match`,
name: $localize`Exact: Document contains this string`,
},
{
id: MATCH_REGEX,
shortName: $localize`Regular expression`,
name: $localize`Regular expression: Document matches this regular expression`,
},
{
id: MATCH_FUZZY,
shortName: $localize`Fuzzy word`,
name: $localize`Fuzzy: Document contains a word similar to this word`,
},
{
id: MATCH_AUTO,
shortName: $localize`Automatic`,
name: $localize`Auto: Learn matching automatically`,
},
] ]
export interface MatchingModel extends ObjectWithId { export interface MatchingModel extends ObjectWithId {
name?: string name?: string
slug?: string slug?: string
@ -30,5 +52,4 @@ export interface MatchingModel extends ObjectWithId {
is_insensitive?: boolean is_insensitive?: boolean
document_count?: number document_count?: number
} }

View File

@ -1,5 +1,3 @@
export interface ObjectWithId { export interface ObjectWithId {
id?: number id?: number
} }

View File

@ -1,7 +1,5 @@
import { MatchingModel } from './matching-model'; import { MatchingModel } from './matching-model'
export interface PaperlessCorrespondent extends MatchingModel { export interface PaperlessCorrespondent extends MatchingModel {
last_correspondence?: Date last_correspondence?: Date
} }

View File

@ -1,5 +1,4 @@
export interface PaperlessDocumentMetadata { export interface PaperlessDocumentMetadata {
original_checksum?: string original_checksum?: string
archived_checksum?: string archived_checksum?: string
@ -9,5 +8,4 @@ export interface PaperlessDocumentMetadata {
media_filename?: string media_filename?: string
has_archive_version?: boolean has_archive_version?: boolean
} }

View File

@ -1,9 +1,7 @@
export interface PaperlessDocumentSuggestions { export interface PaperlessDocumentSuggestions {
tags?: number[] tags?: number[]
correspondents?: number[] correspondents?: number[]
document_types?: number[] document_types?: number[]
} }

View File

@ -1,5 +1,3 @@
import { MatchingModel } from './matching-model'; import { MatchingModel } from './matching-model'
export interface PaperlessDocumentType extends MatchingModel { export interface PaperlessDocumentType extends MatchingModel {}
}

View File

@ -5,16 +5,13 @@ import { PaperlessDocumentType } from './paperless-document-type'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
export interface SearchHit { export interface SearchHit {
score?: number score?: number
rank?: number rank?: number
highlights?: string highlights?: string
} }
export interface PaperlessDocument extends ObjectWithId { export interface PaperlessDocument extends ObjectWithId {
correspondent$?: Observable<PaperlessCorrespondent> correspondent$?: Observable<PaperlessCorrespondent>
correspondent?: number correspondent?: number
@ -50,5 +47,4 @@ export interface PaperlessDocument extends ObjectWithId {
archive_serial_number?: number archive_serial_number?: number
__search_hit__?: SearchHit __search_hit__?: SearchHit
} }

Some files were not shown because too many files have changed in this diff Show More