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

View File

@ -1,11 +1,13 @@
import { browser, by, element } from 'protractor';
import { browser, by, element } from 'protractor'
export class AppPage {
navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>;
return browser.get(browser.baseUrl) as Promise<unknown>
}
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 { Routes, RouterModule } from '@angular/router';
import { AppFrameComponent } from './components/app-frame/app-frame.component';
import { DashboardComponent } from './components/dashboard/dashboard.component';
import { DocumentDetailComponent } from './components/document-detail/document-detail.component';
import { DocumentListComponent } from './components/document-list/document-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 { LogsComponent } from './components/manage/logs/logs.component';
import { SettingsComponent } from './components/manage/settings/settings.component';
import { TagListComponent } from './components/manage/tag-list/tag-list.component';
import { NotFoundComponent } from './components/not-found/not-found.component';
import {DocumentAsnComponent} from "./components/document-asn/document-asn.component";
import { DirtyFormGuard } from './guards/dirty-form.guard';
import { NgModule } from '@angular/core'
import { Routes, RouterModule } from '@angular/router'
import { AppFrameComponent } from './components/app-frame/app-frame.component'
import { DashboardComponent } from './components/dashboard/dashboard.component'
import { DocumentDetailComponent } from './components/document-detail/document-detail.component'
import { DocumentListComponent } from './components/document-list/document-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 { LogsComponent } from './components/manage/logs/logs.component'
import { SettingsComponent } from './components/manage/settings/settings.component'
import { TagListComponent } from './components/manage/tag-list/tag-list.component'
import { NotFoundComponent } from './components/not-found/not-found.component'
import { DocumentAsnComponent } from './components/document-asn/document-asn.component'
import { DirtyFormGuard } from './guards/dirty-form.guard'
const routes: Routes = [
{path: '', redirectTo: 'dashboard', pathMatch: 'full'},
{path: '', component: AppFrameComponent, children: [
{path: 'dashboard', component: DashboardComponent },
{path: 'documents', component: DocumentListComponent },
{path: 'view/:id', component: DocumentListComponent },
{path: 'documents/:id', component: DocumentDetailComponent },
{path: 'asn/:id', component: DocumentAsnComponent },
{path: 'tags', component: TagListComponent },
{path: 'documenttypes', component: DocumentTypeListComponent },
{path: 'correspondents', component: CorrespondentListComponent },
{path: 'logs', component: LogsComponent },
{path: 'settings', component: SettingsComponent, canDeactivate: [DirtyFormGuard] },
]},
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{
path: '',
component: AppFrameComponent,
children: [
{ path: 'dashboard', component: DashboardComponent },
{ path: 'documents', component: DocumentListComponent },
{ path: 'view/:id', component: DocumentListComponent },
{ path: 'documents/:id', component: DocumentDetailComponent },
{ path: 'asn/:id', component: DocumentAsnComponent },
{ path: 'tags', component: TagListComponent },
{ path: 'documenttypes', component: DocumentTypeListComponent },
{ path: 'correspondents', component: CorrespondentListComponent },
{ path: 'logs', component: LogsComponent },
{
path: 'settings',
component: SettingsComponent,
canDeactivate: [DirtyFormGuard],
},
],
},
{path: '404', component: NotFoundComponent},
{path: '**', redirectTo: '/404', pathMatch: 'full'}
];
{ path: '404', component: NotFoundComponent },
{ path: '**', redirectTo: '/404', pathMatch: 'full' },
]
@NgModule({
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 { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import { TestBed } from '@angular/core/testing'
import { RouterTestingModule } from '@angular/router/testing'
import { AppComponent } from './app.component'
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
imports: [RouterTestingModule],
declarations: [AppComponent],
}).compileComponents()
})
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
const fixture = TestBed.createComponent(AppComponent)
const app = fixture.componentInstance
expect(app).toBeTruthy()
})
it(`should have as title 'paperless-ui'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('paperless-ui');
});
const fixture = TestBed.createComponent(AppComponent)
const app = fixture.componentInstance
expect(app.title).toEqual('paperless-ui')
})
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain('paperless-ui app is running!');
});
});
const fixture = TestBed.createComponent(AppComponent)
fixture.detectChanges()
const compiled = fixture.nativeElement
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 { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { ConsumerStatusService } from './services/consumer-status.service';
import { ToastService } from './services/toast.service';
import { SettingsService, SETTINGS_KEYS } from './services/settings.service'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { Router } from '@angular/router'
import { Subscription } from 'rxjs'
import { ConsumerStatusService } from './services/consumer-status.service'
import { ToastService } from './services/toast.service'
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit, OnDestroy {
newDocumentSubscription: Subscription
successSubscription: Subscription
failedSubscription: Subscription
newDocumentSubscription: Subscription;
successSubscription: Subscription;
failedSubscription: Subscription;
constructor (private settings: SettingsService, private consumerStatusService: ConsumerStatusService, private toastService: ToastService, private router: Router) {
let anyWindow = (window as any)
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.js';
constructor(
private settings: SettingsService,
private consumerStatusService: ConsumerStatusService,
private toastService: ToastService,
private router: Router
) {
let anyWindow = window as any
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.js'
this.settings.updateAppearanceSettings()
}
@ -36,7 +40,12 @@ export class AppComponent implements OnInit, OnDestroy {
}
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 this.settings.get(key)
@ -45,26 +54,50 @@ export class AppComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.consumerStatusService.connect()
this.successSubscription = this.consumerStatusService
.onDocumentConsumptionFinished()
.subscribe((status) => {
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.successSubscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
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.failedSubscription = this.consumerStatusService
.onDocumentConsumptionFailed()
.subscribe((status) => {
if (
this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED)
) {
this.toastService.showError(
$localize`Could not add ${status.filename}\: ${status.message}`
)
}
})
this.failedSubscription = this.consumerStatusService.onDocumentConsumptionFailed().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 => {
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.`})
}
})
this.newDocumentSubscription = this.consumerStatusService
.onDocumentDetected()
.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 { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NgbDateAdapter, NgbDateParserFormatter, NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { DocumentListComponent } from './components/document-list/document-list.component';
import { DocumentDetailComponent } from './components/document-detail/document-detail.component';
import { DashboardComponent } from './components/dashboard/dashboard.component';
import { TagListComponent } from './components/manage/tag-list/tag-list.component';
import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component';
import { LogsComponent } from './components/manage/logs/logs.component';
import { SettingsComponent } from './components/manage/settings/settings.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DatePipe, registerLocaleData } from '@angular/common';
import { NotFoundComponent } from './components/not-found/not-found.component';
import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component';
import { ConfirmDialogComponent } from './components/common/confirm-dialog/confirm-dialog.component';
import { CorrespondentEditDialogComponent } from './components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component';
import { TagEditDialogComponent } from './components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component';
import { DocumentTypeEditDialogComponent } from './components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component';
import { TagComponent } from './components/common/tag/tag.component';
import { PageHeaderComponent } from './components/common/page-header/page-header.component';
import { AppFrameComponent } from './components/app-frame/app-frame.component';
import { ToastsComponent } from './components/common/toasts/toasts.component';
import { FilterEditorComponent } from './components/document-list/filter-editor/filter-editor.component';
import { FilterableDropdownComponent } from './components/common/filterable-dropdown/filterable-dropdown.component';
import { ToggleableDropdownButtonComponent } from './components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component';
import { DateDropdownComponent } from './components/common/date-dropdown/date-dropdown.component';
import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component';
import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component';
import { BulkEditorComponent } from './components/document-list/bulk-editor/bulk-editor.component';
import { NgxFileDropModule } from 'ngx-file-drop';
import { TextComponent } from './components/common/input/text/text.component';
import { SelectComponent } from './components/common/input/select/select.component';
import { CheckComponent } from './components/common/input/check/check.component';
import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { TagsComponent } from './components/common/input/tags/tags.component';
import { SortableDirective } from './directives/sortable.directive';
import { CookieService } from 'ngx-cookie-service';
import { CsrfInterceptor } from './interceptors/csrf.interceptor';
import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-view-widget/saved-view-widget.component';
import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component';
import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component';
import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component';
import { PdfViewerModule } from 'ng2-pdf-viewer';
import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component';
import { YesNoPipe } from './pipes/yes-no.pipe';
import { FileSizePipe } from './pipes/file-size.pipe';
import { FilterPipe } from './pipes/filter.pipe';
import { DocumentTitlePipe } from './pipes/document-title.pipe';
import { MetadataCollapseComponent } from './components/document-detail/metadata-collapse/metadata-collapse.component';
import { SelectDialogComponent } from './components/common/select-dialog/select-dialog.component';
import { NgSelectModule } from '@ng-select/ng-select';
import { NumberComponent } from './components/common/input/number/number.component';
import { SafePipe } from './pipes/safe.pipe';
import { CustomDatePipe } from './pipes/custom-date.pipe';
import { DateComponent } from './components/common/input/date/date.component';
import { ISODateTimeAdapter } from './utils/ngb-iso-date-time-adapter';
import { LocalizedDateParserFormatter } from './utils/ngb-date-parser-formatter';
import { ApiVersionInterceptor } from './interceptors/api-version.interceptor';
import { ColorSliderModule } from 'ngx-color/slider';
import { ColorComponent } from './components/common/input/color/color.component';
import { DocumentAsnComponent } from './components/document-asn/document-asn.component';
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';
import { AppRoutingModule } from './app-routing.module'
import { AppComponent } from './app.component'
import {
NgbDateAdapter,
NgbDateParserFormatter,
NgbModule,
} from '@ng-bootstrap/ng-bootstrap'
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'
import { DocumentListComponent } from './components/document-list/document-list.component'
import { DocumentDetailComponent } from './components/document-detail/document-detail.component'
import { DashboardComponent } from './components/dashboard/dashboard.component'
import { TagListComponent } from './components/manage/tag-list/tag-list.component'
import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component'
import { LogsComponent } from './components/manage/logs/logs.component'
import { SettingsComponent } from './components/manage/settings/settings.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { DatePipe, registerLocaleData } from '@angular/common'
import { NotFoundComponent } from './components/not-found/not-found.component'
import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component'
import { ConfirmDialogComponent } from './components/common/confirm-dialog/confirm-dialog.component'
import { CorrespondentEditDialogComponent } from './components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'
import { TagEditDialogComponent } from './components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component'
import { DocumentTypeEditDialogComponent } from './components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'
import { TagComponent } from './components/common/tag/tag.component'
import { PageHeaderComponent } from './components/common/page-header/page-header.component'
import { AppFrameComponent } from './components/app-frame/app-frame.component'
import { ToastsComponent } from './components/common/toasts/toasts.component'
import { FilterEditorComponent } from './components/document-list/filter-editor/filter-editor.component'
import { FilterableDropdownComponent } from './components/common/filterable-dropdown/filterable-dropdown.component'
import { ToggleableDropdownButtonComponent } from './components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
import { DateDropdownComponent } from './components/common/date-dropdown/date-dropdown.component'
import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component'
import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component'
import { BulkEditorComponent } from './components/document-list/bulk-editor/bulk-editor.component'
import { NgxFileDropModule } from 'ngx-file-drop'
import { TextComponent } from './components/common/input/text/text.component'
import { SelectComponent } from './components/common/input/select/select.component'
import { CheckComponent } from './components/common/input/check/check.component'
import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component'
import { InfiniteScrollModule } from 'ngx-infinite-scroll'
import { TagsComponent } from './components/common/input/tags/tags.component'
import { SortableDirective } from './directives/sortable.directive'
import { CookieService } from 'ngx-cookie-service'
import { CsrfInterceptor } from './interceptors/csrf.interceptor'
import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-view-widget/saved-view-widget.component'
import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component'
import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component'
import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component'
import { PdfViewerModule } from 'ng2-pdf-viewer'
import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component'
import { YesNoPipe } from './pipes/yes-no.pipe'
import { FileSizePipe } from './pipes/file-size.pipe'
import { FilterPipe } from './pipes/filter.pipe'
import { DocumentTitlePipe } from './pipes/document-title.pipe'
import { MetadataCollapseComponent } from './components/document-detail/metadata-collapse/metadata-collapse.component'
import { SelectDialogComponent } from './components/common/select-dialog/select-dialog.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { NumberComponent } from './components/common/input/number/number.component'
import { SafePipe } from './pipes/safe.pipe'
import { CustomDatePipe } from './pipes/custom-date.pipe'
import { DateComponent } from './components/common/input/date/date.component'
import { ISODateTimeAdapter } from './utils/ngb-iso-date-time-adapter'
import { LocalizedDateParserFormatter } from './utils/ngb-date-parser-formatter'
import { ApiVersionInterceptor } from './interceptors/api-version.interceptor'
import { ColorSliderModule } from 'ngx-color/slider'
import { ColorComponent } from './components/common/input/color/color.component'
import { DocumentAsnComponent } from './components/document-asn/document-asn.component'
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(localeDa)
@ -91,8 +94,8 @@ registerLocaleData(localeIt)
registerLocaleData(localeLb)
registerLocaleData(localeNl)
registerLocaleData(localePl)
registerLocaleData(localePt, "pt-BR")
registerLocaleData(localePt, "pt-PT")
registerLocaleData(localePt, 'pt-BR')
registerLocaleData(localePt, 'pt-PT')
registerLocaleData(localeRo)
registerLocaleData(localeRu)
registerLocaleData(localeSv)
@ -146,7 +149,7 @@ registerLocaleData(localeSv)
CustomDatePipe,
DateComponent,
ColorComponent,
DocumentAsnComponent
DocumentAsnComponent,
],
imports: [
BrowserModule,
@ -159,24 +162,26 @@ registerLocaleData(localeSv)
InfiniteScrollModule,
PdfViewerModule,
NgSelectModule,
ColorSliderModule
ColorSliderModule,
],
providers: [
DatePipe,
CookieService, {
CookieService,
{
provide: HTTP_INTERCEPTORS,
useClass: CsrfInterceptor,
multi: true
},{
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: ApiVersionInterceptor,
multi: true
multi: true,
},
FilterPipe,
DocumentTitlePipe,
{provide: NgbDateAdapter, useClass: ISODateTimeAdapter},
{provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter}
{ provide: NgbDateAdapter, useClass: ISODateTimeAdapter },
{ 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', () => {
let component: AppFrameComponent;
let fixture: ComponentFixture<AppFrameComponent>;
let component: AppFrameComponent
let fixture: ComponentFixture<AppFrameComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AppFrameComponent ]
})
.compileComponents();
});
declarations: [AppFrameComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(AppFrameComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(AppFrameComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,26 +1,31 @@
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { ActivatedRoute, Router, Params } from '@angular/router';
import { from, Observable, Subscription, BehaviorSubject } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, switchMap, first } from 'rxjs/operators';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
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';
import { Component } from '@angular/core'
import { FormControl } from '@angular/forms'
import { ActivatedRoute, Router, Params } from '@angular/router'
import { from, Observable, Subscription, BehaviorSubject } from 'rxjs'
import {
debounceTime,
distinctUntilChanged,
map,
switchMap,
first,
} from 'rxjs/operators'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
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({
selector: 'app-app-frame',
templateUrl: './app-frame.component.html',
styleUrls: ['./app-frame.component.scss']
styleUrls: ['./app-frame.component.scss'],
})
export class AppFrameComponent {
constructor (
constructor(
public router: Router,
private activatedRoute: ActivatedRoute,
private openDocumentsService: OpenDocumentsService,
@ -28,7 +33,7 @@ export class AppFrameComponent {
public savedViewService: SavedViewService,
private list: DocumentListViewService,
private meta: Meta
) { }
) {}
versionString = `${environment.appTitle} ${environment.version}`
@ -48,14 +53,14 @@ export class AppFrameComponent {
text$.pipe(
debounceTime(200),
distinctUntilChanged(),
map(term => {
map((term) => {
if (term.lastIndexOf(' ') != -1) {
return term.substring(term.lastIndexOf(' ') + 1)
} else {
return term
}
}),
switchMap(term =>
switchMap((term) =>
term.length < 2 ? from([[]]) : this.searchService.autocomplete(term)
)
)
@ -66,49 +71,60 @@ export class AppFrameComponent {
let lastSpaceIndex = currentSearch.lastIndexOf(' ')
if (lastSpaceIndex != -1) {
currentSearch = currentSearch.substring(0, lastSpaceIndex + 1)
currentSearch += event.item + " "
currentSearch += event.item + ' '
} else {
currentSearch = event.item + " "
currentSearch = event.item + ' '
}
this.searchField.patchValue(currentSearch)
}
search() {
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) {
this.openDocumentsService.closeDocument(d).pipe(first()).subscribe(confirmed => {
if (confirmed) {
this.closeMenu()
let route = this.activatedRoute.snapshot
while (route.firstChild) {
route = route.firstChild
this.openDocumentsService
.closeDocument(d)
.pipe(first())
.subscribe((confirmed) => {
if (confirmed) {
this.closeMenu()
let route = this.activatedRoute.snapshot
while (route.firstChild) {
route = route.firstChild
}
if (
route.component == DocumentDetailComponent &&
route.params['id'] == d.id
) {
this.router.navigate([''])
}
}
if (route.component == DocumentDetailComponent && route.params['id'] == d.id) {
this.router.navigate([""])
}
}
})
})
}
closeAll() {
// user may need to confirm losing unsaved changes
this.openDocumentsService.closeAll().pipe(first()).subscribe(confirmed => {
if (confirmed) {
this.closeMenu()
this.openDocumentsService
.closeAll()
.pipe(first())
.subscribe((confirmed) => {
if (confirmed) {
this.closeMenu()
// TODO: is there a better way to do this?
let route = this.activatedRoute
while (route.firstChild) {
route = route.firstChild
// TODO: is there a better way to do this?
let route = this.activatedRoute
while (route.firstChild) {
route = route.firstChild
}
if (route.component === DocumentDetailComponent) {
this.router.navigate([''])
}
}
if (route.component === DocumentDetailComponent) {
this.router.navigate([""])
}
}
})
})
}
get displayName() {
@ -123,5 +139,4 @@ export class AppFrameComponent {
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', () => {
let component: ConfirmDialogComponent;
let fixture: ComponentFixture<ConfirmDialogComponent>;
let component: ConfirmDialogComponent
let fixture: ComponentFixture<ConfirmDialogComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ConfirmDialogComponent ]
})
.compileComponents();
});
declarations: [ConfirmDialogComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(ConfirmDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(ConfirmDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,15 +1,14 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { Subject } from 'rxjs';
import { Component, EventEmitter, Input, Output } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { Subject } from 'rxjs'
@Component({
selector: 'app-confirm-dialog',
templateUrl: './confirm-dialog.component.html',
styleUrls: ['./confirm-dialog.component.scss']
styleUrls: ['./confirm-dialog.component.scss'],
})
export class ConfirmDialogComponent {
constructor(public activeModal: NgbActiveModal) { }
constructor(public activeModal: NgbActiveModal) {}
@Output()
public confirmClicked = new EventEmitter()
@ -24,7 +23,7 @@ export class ConfirmDialogComponent {
message
@Input()
btnClass = "btn-primary"
btnClass = 'btn-primary'
@Input()
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', () => {
let component: DateDropdownComponent;
let fixture: ComponentFixture<DateDropdownComponent>;
let component: DateDropdownComponent
let fixture: ComponentFixture<DateDropdownComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DateDropdownComponent ]
})
.compileComponents();
});
declarations: [DateDropdownComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(DateDropdownComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(DateDropdownComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,10 +1,17 @@
import { formatDate } from '@angular/common';
import { Component, EventEmitter, Input, Output, 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';
import { formatDate } from '@angular/common'
import {
Component,
EventEmitter,
Input,
Output,
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 {
before?: string
@ -20,21 +27,18 @@ const LAST_YEAR = 3
selector: 'app-date-dropdown',
templateUrl: './date-dropdown.component.html',
styleUrls: ['./date-dropdown.component.scss'],
providers: [
{provide: NgbDateAdapter, useClass: ISODateAdapter},
]
providers: [{ provide: NgbDateAdapter, useClass: ISODateAdapter }],
})
export class DateDropdownComponent implements OnInit, OnDestroy {
constructor(settings: SettingsService) {
this.datePlaceHolder = settings.getLocalizedDateInputFormat()
}
quickFilters = [
{id: LAST_7_DAYS, name: $localize`Last 7 days`},
{id: LAST_MONTH, name: $localize`Last month`},
{id: LAST_3_MONTHS, name: $localize`Last 3 months`},
{id: LAST_YEAR, name: $localize`Last year`}
{ id: LAST_7_DAYS, name: $localize`Last 7 days` },
{ id: LAST_MONTH, name: $localize`Last month` },
{ id: LAST_3_MONTHS, name: $localize`Last 3 months` },
{ id: LAST_YEAR, name: $localize`Last year` },
]
datePlaceHolder: string
@ -62,9 +66,7 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
private sub: Subscription
ngOnInit() {
this.sub = this.datesSetDebounce$.pipe(
debounceTime(400)
).subscribe(() => {
this.sub = this.datesSetDebounce$.pipe(debounceTime(400)).subscribe(() => {
this.onChange()
})
}
@ -81,11 +83,11 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
switch (qf) {
case LAST_7_DAYS:
date.setDate(date.getDate() - 7)
break;
break
case LAST_MONTH:
date.setMonth(date.getMonth() - 1)
break;
break
case LAST_3_MONTHS:
date.setMonth(date.getMonth() - 3)
@ -94,20 +96,22 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
case LAST_YEAR:
date.setFullYear(date.getFullYear() - 1)
break
}
this.dateAfter = formatDate(date, 'yyyy-MM-dd', "en-us", "UTC")
}
this.dateAfter = formatDate(date, 'yyyy-MM-dd', 'en-us', 'UTC')
this.onChange()
}
onChange() {
this.dateAfterChange.emit(this.dateAfter)
this.dateBeforeChange.emit(this.dateBefore)
this.datesSet.emit({after: this.dateAfter, before: this.dateBefore})
this.datesSet.emit({ after: this.dateAfter, before: this.dateBefore })
}
onChangeDebounce() {
this.datesSetDebounce$.next({after: this.dateAfter, before: this.dateBefore})
this.datesSetDebounce$.next({
after: this.dateAfter,
before: this.dateBefore,
})
}
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', () => {
let component: EditDialogComponent;
let fixture: ComponentFixture<EditDialogComponent>;
let component: EditDialogComponent
let fixture: ComponentFixture<EditDialogComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ EditDialogComponent ]
})
.compileComponents();
});
declarations: [EditDialogComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(EditDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(EditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { FormGroup } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model';
import { ObjectWithId } from 'src/app/data/object-with-id';
import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service';
import { ToastService } from 'src/app/services/toast.service';
import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'
import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model'
import { ObjectWithId } from 'src/app/data/object-with-id'
import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service'
import { ToastService } from 'src/app/services/toast.service'
@Directive()
export abstract class EditDialogComponent<T extends ObjectWithId> implements OnInit {
export abstract class EditDialogComponent<T extends ObjectWithId>
implements OnInit
{
constructor(
private service: AbstractPaperlessService<T>,
private activeModal: NgbActiveModal,
private toastService: ToastService) { }
private toastService: ToastService
) {}
@Input()
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
setTimeout(() => {
this.closeEnabled = true
});
})
}
getCreateTitle() {
@ -65,7 +67,7 @@ export abstract class EditDialogComponent<T extends ObjectWithId> implements OnI
case 'edit':
return this.getEditTitle()
default:
break;
break
}
}
@ -78,25 +80,31 @@ export abstract class EditDialogComponent<T extends ObjectWithId> implements OnI
}
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>
switch (this.dialogMode) {
case 'create':
serverResponse = this.service.create(newObject)
break;
break
case 'edit':
serverResponse = this.service.update(newObject)
default:
break;
break
}
this.networkActive = true
serverResponse.subscribe(result => {
this.activeModal.close()
this.success.emit(result)
}, error => {
this.error = error.error
this.networkActive = false
})
serverResponse.subscribe(
(result) => {
this.activeModal.close()
this.success.emit(result)
},
(error) => {
this.error = error.error
this.networkActive = false
}
)
}
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', () => {
let component: FilterableDropodownComponent;
let fixture: ComponentFixture<FilterableDropodownComponent>;
let component: FilterableDropodownComponent
let fixture: ComponentFixture<FilterableDropodownComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FilterableDropodownComponent ]
})
.compileComponents();
});
declarations: [FilterableDropodownComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(FilterableDropodownComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(FilterableDropodownComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { FilterPipe } from 'src/app/pipes/filter.pipe';
import {
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 { ToggleableItemState } from './toggleable-dropdown-button/toggleable-dropdown-button.component';
import { MatchingModel } from 'src/app/data/matching-model';
import { Subject } from 'rxjs';
import { ToggleableItemState } from './toggleable-dropdown-button/toggleable-dropdown-button.component'
import { MatchingModel } from 'src/app/data/matching-model'
import { Subject } from 'rxjs'
export interface ChangedItems {
itemsToAdd: MatchingModel[],
itemsToAdd: MatchingModel[]
itemsToRemove: MatchingModel[]
}
export class FilterableDropdownSelectionModel {
changed = new Subject<FilterableDropdownSelectionModel>()
multiple = false
@ -22,14 +28,20 @@ export class FilterableDropdownSelectionModel {
get itemsSorted(): MatchingModel[] {
// 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) {
return -1
} else if (a.id != null && b.id == null) {
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
} 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
} else {
return a.name.localeCompare(b.name)
@ -42,11 +54,17 @@ export class FilterableDropdownSelectionModel {
private temporarySelectionStates = new Map<number, ToggleableItemState>()
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() {
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) {
@ -62,9 +80,16 @@ export class FilterableDropdownSelectionModel {
toggle(id: number, fireEvent = true) {
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)
} else if (state == ToggleableItemState.Selected || state == ToggleableItemState.Excluded) {
} else if (
state == ToggleableItemState.Selected ||
state == ToggleableItemState.Excluded
) {
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)
if (state == null || state != ToggleableItemState.Excluded) {
this.temporarySelectionStates.set(id, ToggleableItemState.Excluded)
@ -130,7 +155,9 @@ export class FilterableDropdownSelectionModel {
}
get(id: number) {
return this.temporarySelectionStates.get(id) || ToggleableItemState.NotSelected
return (
this.temporarySelectionStates.get(id) || ToggleableItemState.NotSelected
)
}
selectionSize() {
@ -150,9 +177,19 @@ export class FilterableDropdownSelectionModel {
}
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
} 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
} else if (this.temporaryLogicalOperator !== this._logicalOperator) {
return true
@ -162,7 +199,10 @@ export class FilterableDropdownSelectionModel {
}
isNoneSelected() {
return this.selectionSize() == 1 && this.get(null) == ToggleableItemState.Selected
return (
this.selectionSize() == 1 &&
this.get(null) == ToggleableItemState.Selected
)
}
init(map) {
@ -187,8 +227,17 @@ export class FilterableDropdownSelectionModel {
diff(): ChangedItems {
return {
itemsToAdd: this.items.filter(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)),
itemsToAdd: this.items.filter(
(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({
selector: 'app-filterable-dropdown',
templateUrl: './filterable-dropdown.component.html',
styleUrls: ['./filterable-dropdown.component.scss']
styleUrls: ['./filterable-dropdown.component.scss'],
})
export class FilterableDropdownComponent {
@ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
@ViewChild('dropdown') dropdown: NgbDropdown
@ -211,7 +259,7 @@ export class FilterableDropdownComponent {
this._selectionModel.items = Array.from(items)
this._selectionModel.items.unshift({
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.multiple = this.selectionModel.multiple
}
model.changed.subscribe(updatedModel => {
model.changed.subscribe((updatedModel) => {
this.selectionModelChange.next(updatedModel)
})
this._selectionModel = model
@ -255,7 +303,7 @@ export class FilterableDropdownComponent {
title: string
@Input()
filterPlaceholder: string = ""
filterPlaceholder: string = ''
@Input()
icon: string
@ -276,14 +324,17 @@ export class FilterableDropdownComponent {
open = new EventEmitter()
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
constructor(private filterPipe: FilterPipe) {
this.selectionModel = new FilterableDropdownSelectionModel()
this.selectionModelChange.subscribe(updatedModel => {
this.selectionModelChange.subscribe((updatedModel) => {
this.modelIsDirty = updatedModel.isDirty()
})
}
@ -300,7 +351,7 @@ export class FilterableDropdownComponent {
dropdownOpenChange(open: boolean): void {
if (open) {
setTimeout(() => {
this.listFilterTextInput.nativeElement.focus();
this.listFilterTextInput.nativeElement.focus()
}, 0)
if (this.editing) {
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', () => {
let component: ToggleableDropdownButtonComponent;
let fixture: ComponentFixture<ToggleableDropdownButtonComponent>;
let component: ToggleableDropdownButtonComponent
let fixture: ComponentFixture<ToggleableDropdownButtonComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ToggleableDropdownButtonComponent ]
})
.compileComponents();
});
declarations: [ToggleableDropdownButtonComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(ToggleableDropdownButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(ToggleableDropdownButtonComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { MatchingModel } from 'src/app/data/matching-model';
import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core'
import { MatchingModel } from 'src/app/data/matching-model'
export enum ToggleableItemState {
NotSelected = 0,
Selected = 1,
PartiallySelected = 2,
Excluded = 3
Excluded = 3,
}
@Component({
selector: 'app-toggleable-dropdown-button',
templateUrl: './toggleable-dropdown-button.component.html',
styleUrls: ['./toggleable-dropdown-button.component.scss']
styleUrls: ['./toggleable-dropdown-button.component.scss'],
})
export class ToggleableDropdownButtonComponent {
@Input()
item: MatchingModel

View File

@ -1,30 +1,29 @@
import { Directive, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor } from '@angular/forms';
import { v4 as uuidv4 } from 'uuid';
import { Directive, ElementRef, Input, OnInit, ViewChild } from '@angular/core'
import { ControlValueAccessor } from '@angular/forms'
import { v4 as uuidv4 } from 'uuid'
@Directive()
export class AbstractInputComponent<T> implements OnInit, ControlValueAccessor {
@ViewChild("inputField")
@ViewChild('inputField')
inputField: ElementRef
constructor() { }
constructor() {}
onChange = (newValue: T) => {};
onChange = (newValue: T) => {}
onTouched = () => {};
onTouched = () => {}
writeValue(newValue: any): void {
this.value = newValue
}
registerOnChange(fn: any): void {
this.onChange = fn;
this.onChange = fn
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
this.onTouched = fn
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
this.disabled = isDisabled
}
focus() {
@ -37,7 +36,7 @@ export class AbstractInputComponent<T> implements OnInit, ControlValueAccessor {
title: string
@Input()
disabled = false;
disabled = false
@Input()
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', () => {
let component: CheckComponent;
let fixture: ComponentFixture<CheckComponent>;
let component: CheckComponent
let fixture: ComponentFixture<CheckComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CheckComponent ]
})
.compileComponents();
});
declarations: [CheckComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(CheckComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(CheckComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,22 +1,22 @@
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { v4 as uuidv4 } from 'uuid';
import { AbstractInputComponent } from '../abstract-input';
import { Component, forwardRef, Input, OnInit } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
import { v4 as uuidv4 } from 'uuid'
import { AbstractInputComponent } from '../abstract-input'
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckComponent),
multi: true
}],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckComponent),
multi: true,
},
],
selector: 'app-input-check',
templateUrl: './check.component.html',
styleUrls: ['./check.component.scss']
styleUrls: ['./check.component.scss'],
})
export class CheckComponent extends AbstractInputComponent<boolean> {
constructor() {
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', () => {
let component: ColorComponent;
let fixture: ComponentFixture<ColorComponent>;
let component: ColorComponent
let fixture: ComponentFixture<ColorComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ColorComponent ]
})
.compileComponents();
});
declarations: [ColorComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(ColorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(ColorComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,20 +1,21 @@
import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { randomColor } from 'src/app/utils/color';
import { AbstractInputComponent } from '../abstract-input';
import { Component, forwardRef } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { randomColor } from 'src/app/utils/color'
import { AbstractInputComponent } from '../abstract-input'
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ColorComponent),
multi: true
}],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ColorComponent),
multi: true,
},
],
selector: 'app-input-color',
templateUrl: './color.component.html',
styleUrls: ['./color.component.scss']
styleUrls: ['./color.component.scss'],
})
export class ColorComponent extends AbstractInputComponent<string> {
constructor() {
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', () => {
let component: DateComponent;
let fixture: ComponentFixture<DateComponent>;
let component: DateComponent
let fixture: ComponentFixture<DateComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DateComponent ]
})
.compileComponents();
});
declarations: [DateComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(DateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(DateComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,21 +1,24 @@
import { Component, forwardRef, OnInit } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { SettingsService } from 'src/app/services/settings.service';
import { AbstractInputComponent } from '../abstract-input';
import { Component, forwardRef, OnInit } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { SettingsService } from 'src/app/services/settings.service'
import { AbstractInputComponent } from '../abstract-input'
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateComponent),
multi: true
}],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateComponent),
multi: true,
},
],
selector: 'app-input-date',
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) {
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', () => {
let component: NumberComponent;
let fixture: ComponentFixture<NumberComponent>;
let component: NumberComponent
let fixture: ComponentFixture<NumberComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NumberComponent ]
})
.compileComponents();
});
declarations: [NumberComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(NumberComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(NumberComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,21 +1,22 @@
import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { FILTER_ASN_ISNULL } from 'src/app/data/filter-rule-type';
import { DocumentService } from 'src/app/services/rest/document.service';
import { AbstractInputComponent } from '../abstract-input';
import { Component, forwardRef } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { FILTER_ASN_ISNULL } from 'src/app/data/filter-rule-type'
import { DocumentService } from 'src/app/services/rest/document.service'
import { AbstractInputComponent } from '../abstract-input'
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NumberComponent),
multi: true
}],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NumberComponent),
multi: true,
},
],
selector: 'app-input-number',
templateUrl: './number.component.html',
styleUrls: ['./number.component.scss']
styleUrls: ['./number.component.scss'],
})
export class NumberComponent extends AbstractInputComponent<number> {
constructor(private documentService: DocumentService) {
super()
}
@ -24,16 +25,17 @@ export class NumberComponent extends AbstractInputComponent<number> {
if (this.value) {
return
}
this.documentService.listFiltered(1, 1, "archive_serial_number", true, [{rule_type: FILTER_ASN_ISNULL, value: "false"}]).subscribe(
results => {
this.documentService
.listFiltered(1, 1, 'archive_serial_number', true, [
{ rule_type: FILTER_ASN_ISNULL, value: 'false' },
])
.subscribe((results) => {
if (results.count > 0) {
this.value = results.results[0].archive_serial_number + 1
} else {
this.value = 1
}
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', () => {
let component: SelectComponent;
let fixture: ComponentFixture<SelectComponent>;
let component: SelectComponent
let fixture: ComponentFixture<SelectComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SelectComponent ]
})
.compileComponents();
});
declarations: [SelectComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(SelectComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(SelectComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,23 +1,30 @@
import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { AbstractInputComponent } from '../abstract-input';
import {
Component,
EventEmitter,
forwardRef,
Input,
Output,
} from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { AbstractInputComponent } from '../abstract-input'
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectComponent),
multi: true
}],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectComponent),
multi: true,
},
],
selector: 'app-input-select',
templateUrl: './select.component.html',
styleUrls: ['./select.component.scss']
styleUrls: ['./select.component.scss'],
})
export class SelectComponent extends AbstractInputComponent<number> {
constructor() {
super()
this.addItemRef = this.addItem.bind(this)
}
}
@Input()
items: any[]
@ -47,7 +54,9 @@ export class SelectComponent extends AbstractInputComponent<number> {
getSuggestions() {
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 {
return []
}
@ -75,7 +84,6 @@ export class SelectComponent extends AbstractInputComponent<number> {
onBlur() {
setTimeout(() => {
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', () => {
let component: TagsComponent;
let fixture: ComponentFixture<TagsComponent>;
let component: TagsComponent
let fixture: ComponentFixture<TagsComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TagsComponent ]
})
.compileComponents();
});
declarations: [TagsComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(TagsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(TagsComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,45 +1,46 @@
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
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 { TagService } from 'src/app/services/rest/tag.service';
import { Component, forwardRef, Input, OnInit } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
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 { TagService } from 'src/app/services/rest/tag.service'
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TagsComponent),
multi: true
}],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TagsComponent),
multi: true,
},
],
selector: 'app-input-tags',
templateUrl: './tags.component.html',
styleUrls: ['./tags.component.scss']
styleUrls: ['./tags.component.scss'],
})
export class TagsComponent implements OnInit, ControlValueAccessor {
constructor(private tagService: TagService, private modalService: NgbModal) {
this.createTagRef = this.createTag.bind(this)
}
onChange = (newValue: number[]) => {};
onChange = (newValue: number[]) => {}
onTouched = () => {};
onTouched = () => {}
writeValue(newValue: number[]): void {
this.value = newValue
}
registerOnChange(fn: any): void {
this.onChange = fn;
this.onChange = fn
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
this.onTouched = fn
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
this.disabled = isDisabled
}
ngOnInit(): void {
this.tagService.listAll().subscribe(result => {
this.tagService.listAll().subscribe((result) => {
this.tags = result.results
})
}
@ -63,7 +64,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
getTag(id) {
if (this.tags) {
return this.tags.find(tag => tag.id == id)
return this.tags.find((tag) => tag.id == id)
} else {
return null
}
@ -80,12 +81,15 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
}
createTag(name: string = null) {
var modal = this.modalService.open(TagEditDialogComponent, {backdrop: 'static'})
var modal = this.modalService.open(TagEditDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.dialogMode = 'create'
if (name) modal.componentInstance.object = { name: name }
else if (this._lastSearchTerm) modal.componentInstance.object = { name: this._lastSearchTerm }
modal.componentInstance.success.subscribe(newTag => {
this.tagService.listAll().subscribe(tags => {
else if (this._lastSearchTerm)
modal.componentInstance.object = { name: this._lastSearchTerm }
modal.componentInstance.success.subscribe((newTag) => {
this.tagService.listAll().subscribe((tags) => {
this.tags = tags.results
this.value = [...this.value, newTag.id]
this.onChange(this.value)
@ -95,7 +99,9 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
getSuggestions() {
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 {
return []
}
@ -117,7 +123,6 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
onBlur() {
setTimeout(() => {
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', () => {
let component: TextComponent;
let fixture: ComponentFixture<TextComponent>;
let component: TextComponent
let fixture: ComponentFixture<TextComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TextComponent ]
})
.compileComponents();
});
declarations: [TextComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(TextComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(TextComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,21 +1,21 @@
import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { AbstractInputComponent } from '../abstract-input';
import { Component, forwardRef } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { AbstractInputComponent } from '../abstract-input'
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TextComponent),
multi: true
}],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TextComponent),
multi: true,
},
],
selector: 'app-input-text',
templateUrl: './text.component.html',
styleUrls: ['./text.component.scss']
styleUrls: ['./text.component.scss'],
})
export class TextComponent extends AbstractInputComponent<string> {
constructor() {
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', () => {
let component: PageHeaderComponent;
let fixture: ComponentFixture<PageHeaderComponent>;
let component: PageHeaderComponent
let fixture: ComponentFixture<PageHeaderComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PageHeaderComponent ]
})
.compileComponents();
});
declarations: [PageHeaderComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(PageHeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(PageHeaderComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,17 +1,16 @@
import { Component, Input } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { environment } from 'src/environments/environment';
import { Component, Input } from '@angular/core'
import { Title } from '@angular/platform-browser'
import { environment } from 'src/environments/environment'
@Component({
selector: 'app-page-header',
templateUrl: './page-header.component.html',
styleUrls: ['./page-header.component.scss']
styleUrls: ['./page-header.component.scss'],
})
export class PageHeaderComponent {
constructor(private titleService: Title) {}
constructor(private titleService: Title) { }
_title = ""
_title = ''
@Input()
set title(title: string) {
@ -24,6 +23,5 @@ export class PageHeaderComponent {
}
@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', () => {
let component: SelectDialogComponent;
let fixture: ComponentFixture<SelectDialogComponent>;
let component: SelectDialogComponent
let fixture: ComponentFixture<SelectDialogComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SelectDialogComponent ]
})
.compileComponents();
});
declarations: [SelectDialogComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(SelectDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(SelectDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ObjectWithId } from 'src/app/data/object-with-id';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { ObjectWithId } from 'src/app/data/object-with-id'
@Component({
selector: 'app-select-dialog',
templateUrl: './select-dialog.component.html',
styleUrls: ['./select-dialog.component.scss']
styleUrls: ['./select-dialog.component.scss'],
})
export class SelectDialogComponent implements OnInit {
constructor(public activeModal: NgbActiveModal) { }
constructor(public activeModal: NgbActiveModal) {}
@Output()
public selectClicked = new EventEmitter()
@ -25,8 +24,7 @@ export class SelectDialogComponent implements OnInit {
selected: number
ngOnInit(): void {
}
ngOnInit(): void {}
cancelClicked() {
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', () => {
let component: TagComponent;
let fixture: ComponentFixture<TagComponent>;
let component: TagComponent
let fixture: ComponentFixture<TagComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TagComponent ]
})
.compileComponents();
});
declarations: [TagComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(TagComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(TagComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,25 +1,22 @@
import { Component, Input, OnInit } from '@angular/core';
import { PaperlessTag } from 'src/app/data/paperless-tag';
import { Component, Input, OnInit } from '@angular/core'
import { PaperlessTag } from 'src/app/data/paperless-tag'
@Component({
selector: 'app-tag',
templateUrl: './tag.component.html',
styleUrls: ['./tag.component.scss']
styleUrls: ['./tag.component.scss'],
})
export class TagComponent implements OnInit {
constructor() { }
constructor() {}
@Input()
tag: PaperlessTag
@Input()
linkTitle: string = ""
linkTitle: string = ''
@Input()
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', () => {
let component: ToastsComponent;
let fixture: ComponentFixture<ToastsComponent>;
let component: ToastsComponent
let fixture: ComponentFixture<ToastsComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ToastsComponent ]
})
.compileComponents();
});
declarations: [ToastsComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(ToastsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(ToastsComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,15 +1,14 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { Toast, ToastService } from 'src/app/services/toast.service';
import { Component, OnDestroy, OnInit } from '@angular/core'
import { Subscription } from 'rxjs'
import { Toast, ToastService } from 'src/app/services/toast.service'
@Component({
selector: 'app-toasts',
templateUrl: './toasts.component.html',
styleUrls: ['./toasts.component.scss']
styleUrls: ['./toasts.component.scss'],
})
export class ToastsComponent implements OnInit, OnDestroy {
constructor(private toastService: ToastService) { }
constructor(private toastService: ToastService) {}
subscription: Subscription
@ -20,7 +19,8 @@ export class ToastsComponent implements OnInit, OnDestroy {
}
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', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
let component: DashboardComponent
let fixture: ComponentFixture<DashboardComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DashboardComponent ]
})
.compileComponents();
});
declarations: [DashboardComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(DashboardComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,20 +1,15 @@
import { Component, OnInit } from '@angular/core';
import { Meta } from '@angular/platform-browser';
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
import { Component, OnInit } from '@angular/core'
import { Meta } from '@angular/platform-browser'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
styleUrls: ['./dashboard.component.scss'],
})
export class DashboardComponent implements OnInit {
constructor(
private savedViewService: SavedViewService,
private meta: Meta
) { }
constructor(private savedViewService: SavedViewService, private meta: Meta) {}
get displayName() {
let tagFullName = this.meta.getTag('name=full_name')
@ -39,9 +34,10 @@ export class DashboardComponent implements OnInit {
savedViews: PaperlessSavedView[] = []
ngOnInit(): void {
this.savedViewService.listAll().subscribe(results => {
this.savedViews = results.results.filter(savedView => savedView.show_on_dashboard)
this.savedViewService.listAll().subscribe((results) => {
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', () => {
let component: SavedViewWidgetComponent;
let fixture: ComponentFixture<SavedViewWidgetComponent>;
let component: SavedViewWidgetComponent
let fixture: ComponentFixture<SavedViewWidgetComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SavedViewWidgetComponent ]
})
.compileComponents();
});
declarations: [SavedViewWidgetComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(SavedViewWidgetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(SavedViewWidgetComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,24 +1,24 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { ConsumerStatusService } from 'src/app/services/consumer-status.service';
import { DocumentService } from 'src/app/services/rest/document.service';
import { Component, Input, OnDestroy, OnInit } from '@angular/core'
import { Router } from '@angular/router'
import { Subscription } from 'rxjs'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { DocumentService } from 'src/app/services/rest/document.service'
@Component({
selector: 'app-saved-view-widget',
templateUrl: './saved-view-widget.component.html',
styleUrls: ['./saved-view-widget.component.scss']
styleUrls: ['./saved-view-widget.component.scss'],
})
export class SavedViewWidgetComponent implements OnInit, OnDestroy {
constructor(
private documentService: DocumentService,
private router: Router,
private list: DocumentListViewService,
private consumerStatusService: ConsumerStatusService) { }
private consumerStatusService: ConsumerStatusService
) {}
@Input()
savedView: PaperlessSavedView
@ -29,9 +29,11 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.reload()
this.subscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
this.reload()
})
this.subscription = this.consumerStatusService
.onDocumentConsumptionFinished()
.subscribe((status) => {
this.reload()
})
}
ngOnDestroy(): void {
@ -39,9 +41,17 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
}
reload() {
this.documentService.listFiltered(1,10,this.savedView.sort_field, this.savedView.sort_reverse, this.savedView.filter_rules).subscribe(result => {
this.documents = result.results
})
this.documentService
.listFiltered(
1,
10,
this.savedView.sort_field,
this.savedView.sort_reverse,
this.savedView.filter_rules
)
.subscribe((result) => {
this.documents = result.results
})
}
showAll() {
@ -49,8 +59,7 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
this.router.navigate(['view', this.savedView.id])
} else {
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', () => {
let component: StatisticsWidgetComponent;
let fixture: ComponentFixture<StatisticsWidgetComponent>;
let component: StatisticsWidgetComponent
let fixture: ComponentFixture<StatisticsWidgetComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ StatisticsWidgetComponent ]
})
.compileComponents();
});
declarations: [StatisticsWidgetComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(StatisticsWidgetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(StatisticsWidgetComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,24 +1,24 @@
import { HttpClient } from '@angular/common/http';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { ConsumerStatusService } from 'src/app/services/consumer-status.service';
import { environment } from 'src/environments/environment';
import { HttpClient } from '@angular/common/http'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { Observable, Subscription } from 'rxjs'
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { environment } from 'src/environments/environment'
export interface Statistics {
documents_total?: number
documents_inbox?: number
}
@Component({
selector: 'app-statistics-widget',
templateUrl: './statistics-widget.component.html',
styleUrls: ['./statistics-widget.component.scss']
styleUrls: ['./statistics-widget.component.scss'],
})
export class StatisticsWidgetComponent implements OnInit, OnDestroy {
constructor(private http: HttpClient,
private consumerStatusService: ConsumerStatusService) { }
constructor(
private http: HttpClient,
private consumerStatusService: ConsumerStatusService
) {}
statistics: Statistics = {}
@ -29,20 +29,21 @@ export class StatisticsWidgetComponent implements OnInit, OnDestroy {
}
reload() {
this.getStatistics().subscribe(statistics => {
this.getStatistics().subscribe((statistics) => {
this.statistics = statistics
})
}
ngOnInit(): void {
this.reload()
this.subscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
this.reload()
})
this.subscription = this.consumerStatusService
.onDocumentConsumptionFinished()
.subscribe((status) => {
this.reload()
})
}
ngOnDestroy(): void {
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', () => {
let component: UploadFileWidgetComponent;
let fixture: ComponentFixture<UploadFileWidgetComponent>;
let component: UploadFileWidgetComponent
let fixture: ComponentFixture<UploadFileWidgetComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ UploadFileWidgetComponent ]
})
.compileComponents();
});
declarations: [UploadFileWidgetComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(UploadFileWidgetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(UploadFileWidgetComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

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

View File

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

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', () => {
let component: DocumentDetailComponent;
let fixture: ComponentFixture<DocumentDetailComponent>;
let component: DocumentDetailComponent
let fixture: ComponentFixture<DocumentDetailComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DocumentDetailComponent ]
})
.compileComponents();
});
declarations: [DocumentDetailComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(DocumentDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(DocumentDetailComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { FormControl, FormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { NgbModal, NgbNav } from '@ng-bootstrap/ng-bootstrap';
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata';
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe';
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
import { DocumentService } from 'src/app/services/rest/document.service';
import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component';
import { CorrespondentEditDialogComponent } from '../manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component';
import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component';
import { PDFDocumentProxy } from 'ng2-pdf-viewer';
import { ToastService } from 'src/app/services/toast.service';
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';
import {
Component,
OnInit,
OnDestroy,
ViewChild,
ElementRef,
} from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { ActivatedRoute, Router } from '@angular/router'
import { NgbModal, NgbNav } from '@ng-bootstrap/ng-bootstrap'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { DocumentService } from 'src/app/services/rest/document.service'
import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component'
import { CorrespondentEditDialogComponent } from '../manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'
import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'
import { PDFDocumentProxy } from 'ng2-pdf-viewer'
import { ToastService } from 'src/app/services/toast.service'
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({
selector: 'app-document-detail',
templateUrl: './document-detail.component.html',
styleUrls: ['./document-detail.component.scss']
styleUrls: ['./document-detail.component.scss'],
})
export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponent {
@ViewChild("inputTitle")
export class DocumentDetailComponent
implements OnInit, OnDestroy, DirtyComponent
{
@ViewChild('inputTitle')
titleInput: TextComponent
expandOriginalMetadata = false
@ -63,7 +80,7 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
correspondent: new FormControl(),
document_type: new FormControl(),
archive_serial_number: new FormControl(),
tags: new FormControl([])
tags: new FormControl([]),
})
previewCurrentPage: number = 1
@ -76,8 +93,13 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
@ViewChild('nav') nav: NgbNav
@ViewChild('pdfPreview') set pdfPreview(element) {
// this gets called when compontent added or removed from DOM
if (element && element.nativeElement.offsetParent !== null && this.nav?.activeId == 4) { // its visible
setTimeout(()=> this.nav?.select(1));
if (
element &&
element.nativeElement.offsetParent !== null &&
this.nav?.activeId == 4
) {
// its visible
setTimeout(() => this.nav?.select(1))
}
}
@ -92,16 +114,19 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
private documentListViewService: DocumentListViewService,
private documentTitlePipe: DocumentTitlePipe,
private toastService: ToastService,
private settings: SettingsService) {
this.titleSubject.pipe(
private settings: SettingsService
) {
this.titleSubject
.pipe(
debounceTime(1000),
distinctUntilChanged(),
takeUntil(this.unsubscribeNotifier)
).subscribe(titleValue => {
)
.subscribe((titleValue) => {
this.title = titleValue
this.documentForm.patchValue({'title': titleValue})
this.documentForm.patchValue({ title: titleValue })
})
}
}
titleKeyUp(event) {
this.titleSubject.next(event.target?.value)
@ -112,180 +137,291 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
}
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 {
this.documentForm.valueChanges.pipe(takeUntil(this.unsubscribeNotifier)).subscribe(wow => {
Object.assign(this.document, this.documentForm.value)
})
this.correspondentService.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 => {
const documentId = +paramMap.get('id')
return this.documentsService.get(documentId)
})).pipe(switchMap((doc) => {
this.documentId = doc.id
this.previewUrl = this.documentsService.getPreviewUrl(this.documentId)
this.downloadUrl = this.documentsService.getDownloadUrl(this.documentId)
this.downloadOriginalUrl = this.documentsService.getDownloadUrl(this.documentId, true)
this.suggestions = null
if (this.openDocumentService.getOpenDocument(this.documentId)) {
this.updateComponent(this.openDocumentService.getOpenDocument(this.documentId))
} else {
this.openDocumentService.openDocument(doc)
this.updateComponent(doc)
}
// Initialize dirtyCheck
this.store = new BehaviorSubject({
title: doc.title,
content: doc.content,
created: doc.created,
correspondent: doc.correspondent,
document_type: doc.document_type,
archive_serial_number: doc.archive_serial_number,
tags: [...doc.tags]
this.documentForm.valueChanges
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((wow) => {
Object.assign(this.document, this.documentForm.value)
})
this.isDirty$ = dirtyCheck(this.documentForm, this.store.asObservable())
this.correspondentService
.listAll()
.pipe(first())
.subscribe((result) => (this.correspondents = result.results))
this.documentTypeService
.listAll()
.pipe(first())
.subscribe((result) => (this.documentTypes = result.results))
return this.isDirty$.pipe(map(dirty => ({doc, dirty})))
}))
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({doc, dirty}) => {
this.openDocumentService.setDirty(doc.id, dirty)
}, error => {this.router.navigate(['404'])})
this.route.paramMap
.pipe(
switchMap((paramMap) => {
const documentId = +paramMap.get('id')
return this.documentsService.get(documentId)
})
)
.pipe(
switchMap((doc) => {
this.documentId = doc.id
this.previewUrl = this.documentsService.getPreviewUrl(this.documentId)
this.downloadUrl = this.documentsService.getDownloadUrl(
this.documentId
)
this.downloadOriginalUrl = this.documentsService.getDownloadUrl(
this.documentId,
true
)
this.suggestions = null
if (this.openDocumentService.getOpenDocument(this.documentId)) {
this.updateComponent(
this.openDocumentService.getOpenDocument(this.documentId)
)
} else {
this.openDocumentService.openDocument(doc)
this.updateComponent(doc)
}
// Initialize dirtyCheck
this.store = new BehaviorSubject({
title: doc.title,
content: doc.content,
created: doc.created,
correspondent: doc.correspondent,
document_type: doc.document_type,
archive_serial_number: doc.archive_serial_number,
tags: [...doc.tags],
})
this.isDirty$ = dirtyCheck(
this.documentForm,
this.store.asObservable()
)
return this.isDirty$.pipe(map((dirty) => ({ doc, dirty })))
})
)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(
({ doc, dirty }) => {
this.openDocumentService.setDirty(doc.id, dirty)
},
(error) => {
this.router.navigate(['404'])
}
)
}
ngOnDestroy() : void {
this.unsubscribeNotifier.next();
this.unsubscribeNotifier.complete();
ngOnDestroy(): void {
this.unsubscribeNotifier.next()
this.unsubscribeNotifier.complete()
}
updateComponent(doc: PaperlessDocument) {
this.document = doc
this.documentsService.getMetadata(doc.id).pipe(first()).subscribe(result => {
this.metadata = result
}, error => {
this.metadata = null
})
this.documentsService.getSuggestions(doc.id).pipe(first()).subscribe(result => {
this.suggestions = result
}, error => {
this.suggestions = null
})
this.documentsService
.getMetadata(doc.id)
.pipe(first())
.subscribe(
(result) => {
this.metadata = result
},
(error) => {
this.metadata = null
}
)
this.documentsService
.getSuggestions(doc.id)
.pipe(first())
.subscribe(
(result) => {
this.suggestions = result
},
(error) => {
this.suggestions = null
}
)
this.title = this.documentTitlePipe.transform(doc.title)
this.documentForm.patchValue(doc)
}
createDocumentType(newName: string) {
var modal = this.modalService.open(DocumentTypeEditDialogComponent, {backdrop: 'static'})
var modal = this.modalService.open(DocumentTypeEditDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.dialogMode = 'create'
if (newName) modal.componentInstance.object = { name: newName }
modal.componentInstance.success.pipe(switchMap(newDocumentType => {
return this.documentTypeService.listAll().pipe(map(documentTypes => ({newDocumentType, documentTypes})))
}))
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({newDocumentType, documentTypes}) => {
this.documentTypes = documentTypes.results
this.documentForm.get('document_type').setValue(newDocumentType.id)
})
modal.componentInstance.success
.pipe(
switchMap((newDocumentType) => {
return this.documentTypeService
.listAll()
.pipe(map((documentTypes) => ({ newDocumentType, documentTypes })))
})
)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({ newDocumentType, documentTypes }) => {
this.documentTypes = documentTypes.results
this.documentForm.get('document_type').setValue(newDocumentType.id)
})
}
createCorrespondent(newName: string) {
var modal = this.modalService.open(CorrespondentEditDialogComponent, {backdrop: 'static'})
var modal = this.modalService.open(CorrespondentEditDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.dialogMode = 'create'
if (newName) modal.componentInstance.object = { name: newName }
modal.componentInstance.success.pipe(switchMap(newCorrespondent => {
return this.correspondentService.listAll().pipe(map(correspondents => ({newCorrespondent, correspondents})))
}))
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({newCorrespondent, correspondents}) => {
this.correspondents = correspondents.results
this.documentForm.get('correspondent').setValue(newCorrespondent.id)
})
modal.componentInstance.success
.pipe(
switchMap((newCorrespondent) => {
return this.correspondentService
.listAll()
.pipe(
map((correspondents) => ({ newCorrespondent, correspondents }))
)
})
)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({ newCorrespondent, correspondents }) => {
this.correspondents = correspondents.results
this.documentForm.get('correspondent').setValue(newCorrespondent.id)
})
}
discard() {
this.documentsService.get(this.documentId).pipe(first()).subscribe(doc => {
Object.assign(this.document, doc)
this.title = doc.title
this.documentForm.patchValue(doc)
}, error => {this.router.navigate(['404'])})
this.documentsService
.get(this.documentId)
.pipe(first())
.subscribe(
(doc) => {
Object.assign(this.document, doc)
this.title = doc.title
this.documentForm.patchValue(doc)
},
(error) => {
this.router.navigate(['404'])
}
)
}
save() {
this.networkActive = true
this.store.next(this.documentForm.value)
this.documentsService.update(this.document).pipe(first()).subscribe(result => {
this.close()
this.networkActive = false
this.error = null
}, error => {
this.networkActive = false
this.error = error.error
})
this.documentsService
.update(this.document)
.pipe(first())
.subscribe(
(result) => {
this.close()
this.networkActive = false
this.error = null
},
(error) => {
this.networkActive = false
this.error = error.error
}
)
}
saveEditNext() {
this.networkActive = true
this.store.next(this.documentForm.value)
this.documentsService.update(this.document).pipe(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())
.subscribe(({updateResult, nextDocId, closeResult}) => {
this.error = null
this.networkActive = false
if (closeResult && updateResult && nextDocId) {
this.router.navigate(['documents', nextDocId])
this.titleInput?.focus()
}
}, error => {
this.networkActive = false
this.error = error.error
})
this.documentsService
.update(this.document)
.pipe(
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())
.subscribe(
({ updateResult, nextDocId, closeResult }) => {
this.error = null
this.networkActive = false
if (closeResult && updateResult && nextDocId) {
this.router.navigate(['documents', nextDocId])
this.titleInput?.focus()
}
},
(error) => {
this.networkActive = false
this.error = error.error
}
)
}
close() {
this.openDocumentService.closeDocument(this.document).pipe(first()).subscribe(closed => {
if (!closed) return;
if (this.documentListViewService.activeSavedViewId) {
this.router.navigate(['view', this.documentListViewService.activeSavedViewId])
} else {
this.router.navigate(['documents'])
}
})
this.openDocumentService
.closeDocument(this.document)
.pipe(first())
.subscribe((closed) => {
if (!closed) return
if (this.documentListViewService.activeSavedViewId) {
this.router.navigate([
'view',
this.documentListViewService.activeSavedViewId,
])
} else {
this.router.navigate(['documents'])
}
})
}
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.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.btnClass = "btn-danger"
modal.componentInstance.btnClass = 'btn-danger'
modal.componentInstance.btnCaption = $localize`Delete document`
modal.componentInstance.confirmClicked.pipe(switchMap(() => {
modal.componentInstance.buttonsEnabled = false
return this.documentsService.delete(this.document)
}))
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(() => {
modal.close()
this.close()
}, error => {
this.toastService.showError($localize`Error deleting document: ${JSON.stringify(error)}`)
modal.componentInstance.buttonsEnabled = true
})
modal.componentInstance.confirmClicked
.pipe(
switchMap(() => {
modal.componentInstance.buttonsEnabled = false
return this.documentsService.delete(this.document)
})
)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(
() => {
modal.close()
this.close()
},
(error) => {
this.toastService.showError(
$localize`Error deleting document: ${JSON.stringify(error)}`
)
modal.componentInstance.buttonsEnabled = true
}
)
}
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() {
@ -311,5 +447,4 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
pdfPreviewLoaded(pdf: PDFDocumentProxy) {
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', () => {
let component: MetadataCollapseComponent;
let fixture: ComponentFixture<MetadataCollapseComponent>;
let component: MetadataCollapseComponent
let fixture: ComponentFixture<MetadataCollapseComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MetadataCollapseComponent ]
})
.compileComponents();
});
declarations: [MetadataCollapseComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(MetadataCollapseComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(MetadataCollapseComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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({
selector: 'app-metadata-collapse',
templateUrl: './metadata-collapse.component.html',
styleUrls: ['./metadata-collapse.component.scss']
styleUrls: ['./metadata-collapse.component.scss'],
})
export class MetadataCollapseComponent implements OnInit {
constructor() { }
constructor() {}
expand = false
@ -17,7 +16,5 @@ export class MetadataCollapseComponent implements OnInit {
@Input()
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', () => {
let component: BulkEditorComponent;
let fixture: ComponentFixture<BulkEditorComponent>;
let component: BulkEditorComponent
let fixture: ComponentFixture<BulkEditorComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ BulkEditorComponent ]
})
.compileComponents();
});
declarations: [BulkEditorComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(BulkEditorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(BulkEditorComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,29 +1,37 @@
import { Component } from '@angular/core';
import { PaperlessTag } from 'src/app/data/paperless-tag';
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
import { TagService } from 'src/app/services/rest/tag.service';
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { DocumentService, SelectionDataItem } from 'src/app/services/rest/document.service';
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog/confirm-dialog.component';
import { ChangedItems, 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';
import { Component } from '@angular/core'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { TagService } from 'src/app/services/rest/tag.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import {
DocumentService,
SelectionDataItem,
} from 'src/app/services/rest/document.service'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog/confirm-dialog.component'
import {
ChangedItems,
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({
selector: 'app-bulk-editor',
templateUrl: './bulk-editor.component.html',
styleUrls: ['./bulk-editor.component.scss']
styleUrls: ['./bulk-editor.component.scss'],
})
export class BulkEditorComponent {
tags: PaperlessTag[]
correspondents: PaperlessCorrespondent[]
documentTypes: PaperlessDocumentType[]
@ -42,43 +50,63 @@ export class BulkEditorComponent {
private openDocumentService: OpenDocumentsService,
private settings: SettingsService,
private toastService: ToastService
) { }
) {}
applyOnClose: boolean = this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE)
showConfirmationDialogs: boolean = this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS)
applyOnClose: boolean = this.settings.get(
SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE
)
showConfirmationDialogs: boolean = this.settings.get(
SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS
)
ngOnInit() {
this.tagService.listAll().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.tagService
.listAll()
.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) {
if (modal) {
modal.componentInstance.buttonsEnabled = false
}
this.documentService.bulkEdit(Array.from(this.list.selected), method, args).subscribe(
response => {
this.list.reload()
this.list.reduceSelectionToFilter()
this.list.selected.forEach(id => {
this.openDocumentService.refreshDocument(id)
})
if (modal) {
modal.close()
this.documentService
.bulkEdit(Array.from(this.list.selected), method, args)
.subscribe(
(response) => {
this.list.reload()
this.list.reduceSelectionToFilter()
this.list.selected.forEach((id) => {
this.openDocumentService.refreshDocument(id)
})
if (modal) {
modal.close()
}
},
(error) => {
if (modal) {
modal.componentInstance.buttonsEnabled = true
}
this.toastService.showError(
$localize`Error executing bulk operation: ${JSON.stringify(
error.error
)}`
)
}
}, error => {
if (modal) {
modal.componentInstance.buttonsEnabled = true
}
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>()
items.forEach(i => {
items.forEach((i) => {
if (i.document_count == this.list.selected.size) {
selectionData.set(i.id, ToggleableItemState.Selected)
} else if (i.document_count > 0) {
@ -89,129 +117,210 @@ export class BulkEditorComponent {
}
openTagsDropdown() {
this.documentService.getSelectionData(Array.from(this.list.selected)).subscribe(s => {
this.applySelectionData(s.selected_tags, this.tagSelectionModel)
})
this.documentService
.getSelectionData(Array.from(this.list.selected))
.subscribe((s) => {
this.applySelectionData(s.selected_tags, this.tagSelectionModel)
})
}
openDocumentTypeDropdown() {
this.documentService.getSelectionData(Array.from(this.list.selected)).subscribe(s => {
this.applySelectionData(s.selected_document_types, this.documentTypeSelectionModel)
})
this.documentService
.getSelectionData(Array.from(this.list.selected))
.subscribe((s) => {
this.applySelectionData(
s.selected_document_types,
this.documentTypeSelectionModel
)
})
}
openCorrespondentDropdown() {
this.documentService.getSelectionData(Array.from(this.list.selected)).subscribe(s => {
this.applySelectionData(s.selected_correspondents, this.correspondentSelectionModel)
})
this.documentService
.getSelectionData(Array.from(this.list.selected))
.subscribe((s) => {
this.applySelectionData(
s.selected_correspondents,
this.correspondentSelectionModel
)
})
}
private _localizeList(items: MatchingModel[]) {
if (items.length == 0) {
return ""
return ''
} else if (items.length == 1) {
return $localize`"${items[0].name}"`
} else if (items.length == 2) {
return $localize`:This is for messages like 'modify "tag1" and "tag2"':"${items[0].name}" and "${items[1].name}"`
} 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:, `)
return $localize`:this is for messages like 'modify "tag1", "tag2" and "tag3"':${list} and "${items[items.length - 1].name}"`
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:, `
)
return $localize`:this is for messages like 'modify "tag1", "tag2" and "tag3"':${list} and "${
items[items.length - 1].name
}"`
}
}
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) {
let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
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]
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) {
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) {
} else if (
changedTags.itemsToAdd.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]
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) {
modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(changedTags.itemsToRemove)} from ${this.list.selected.size} selected document(s).`
} else if (
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 {
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.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 {
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) {
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) {
let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.title = $localize`Confirm correspondent assignment`
if (correspondent) {
modal.componentInstance.message = $localize`This operation will assign the correspondent "${correspondent.name}" to ${this.list.selected.size} selected document(s).`
} else {
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.confirmClicked.subscribe(() => {
this.executeBulkOperation(modal, 'set_correspondent', {"correspondent": correspondent ? correspondent.id : null})
this.executeBulkOperation(modal, 'set_correspondent', {
correspondent: correspondent ? correspondent.id : null,
})
})
} else {
this.executeBulkOperation(null, 'set_correspondent', {"correspondent": correspondent ? correspondent.id : null})
this.executeBulkOperation(null, 'set_correspondent', {
correspondent: correspondent ? correspondent.id : null,
})
}
}
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) {
let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.title = $localize`Confirm document type assignment`
if (documentType) {
modal.componentInstance.message = $localize`This operation will assign the document type "${documentType.name}" to ${this.list.selected.size} selected document(s).`
} else {
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.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 {
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() {
let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.delayConfirm(5)
modal.componentInstance.title = $localize`Delete confirm`
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.btnClass = "btn-danger"
modal.componentInstance.btnClass = 'btn-danger'
modal.componentInstance.btnCaption = $localize`Delete document(s)`
modal.componentInstance.confirmClicked.subscribe(() => {
modal.componentInstance.buttonsEnabled = false
this.executeBulkOperation(modal, "delete", {})
this.executeBulkOperation(modal, 'delete', {})
})
}
downloadSelected(content = "archive") {
this.documentService.bulkDownload(Array.from(this.list.selected), content).subscribe((result: any) => {
saveAs(result, 'documents.zip');
})
downloadSelected(content = 'archive') {
this.documentService
.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', () => {
let component: DocumentCardLargeComponent;
let fixture: ComponentFixture<DocumentCardLargeComponent>;
let component: DocumentCardLargeComponent
let fixture: ComponentFixture<DocumentCardLargeComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DocumentCardLargeComponent ]
})
.compileComponents();
});
declarations: [DocumentCardLargeComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(DocumentCardLargeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(DocumentCardLargeComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { 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';
import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewChild,
} 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({
selector: 'app-document-card-large',
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 {
constructor(private documentService: DocumentService, private sanitizer: DomSanitizer, private settingsService: SettingsService) { }
constructor(
private documentService: DocumentService,
private sanitizer: DomSanitizer,
private settingsService: SettingsService
) {}
@Input()
selected = false
@ -39,7 +55,7 @@ export class DocumentCardLargeComponent implements OnInit {
clickDocumentType = new EventEmitter<number>()
@Output()
clickMoreLike= new EventEmitter()
clickMoreLike = new EventEmitter()
@ViewChild('popover') popover: NgbPopover
@ -49,17 +65,16 @@ export class DocumentCardLargeComponent implements OnInit {
get searchScoreClass() {
if (this.document.__search_hit__) {
if (this.document.__search_hit__.score > 0.7) {
return "success"
return 'success'
} else if (this.document.__search_hit__.score > 0.3) {
return "warning"
return 'warning'
} else {
return "danger"
return 'danger'
}
}
}
ngOnInit(): void {
}
ngOnInit(): void {}
getIsThumbInverted() {
return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
@ -90,7 +105,7 @@ export class DocumentCardLargeComponent implements OnInit {
} else {
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', () => {
let component: DocumentCardSmallComponent;
let fixture: ComponentFixture<DocumentCardSmallComponent>;
let component: DocumentCardSmallComponent
let fixture: ComponentFixture<DocumentCardSmallComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DocumentCardSmallComponent ]
})
.compileComponents();
});
declarations: [DocumentCardSmallComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(DocumentCardSmallComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(DocumentCardSmallComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { 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';
import {
Component,
EventEmitter,
Input,
OnInit,
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({
selector: 'app-document-card-small',
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 {
constructor(private documentService: DocumentService, private settingsService: SettingsService) { }
constructor(
private documentService: DocumentService,
private settingsService: SettingsService
) {}
@Input()
selected = false
@ -39,8 +54,7 @@ export class DocumentCardSmallComponent implements OnInit {
mouseOnPreview = false
popoverHidden = true
ngOnInit(): void {
}
ngOnInit(): void {}
getIsThumbInverted() {
return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
@ -60,7 +74,7 @@ export class DocumentCardSmallComponent implements OnInit {
getTagsLimited$() {
return this.document.tags$.pipe(
map(tags => {
map((tags) => {
if (tags.length > 7) {
this.moreTags = tags.length - 6
return tags.slice(0, 6)
@ -84,7 +98,7 @@ export class DocumentCardSmallComponent implements OnInit {
} else {
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', () => {
let component: DocumentListComponent;
let fixture: ComponentFixture<DocumentListComponent>;
let component: DocumentListComponent
let fixture: ComponentFixture<DocumentListComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DocumentListComponent ]
})
.compileComponents();
});
declarations: [DocumentListComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(DocumentListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(DocumentListComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { ActivatedRoute, Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subscription } from 'rxjs';
import { FilterRule, isFullTextFilterRule } from 'src/app/data/filter-rule';
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
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';
import {
Component,
OnDestroy,
OnInit,
QueryList,
ViewChild,
ViewChildren,
} from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subscription } from 'rxjs'
import { FilterRule, isFullTextFilterRule } from 'src/app/data/filter-rule'
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
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({
selector: 'app-document-list',
templateUrl: './document-list.component.html',
styleUrls: ['./document-list.component.scss']
styleUrls: ['./document-list.component.scss'],
})
export class DocumentListComponent implements OnInit, OnDestroy {
constructor(
public list: DocumentListViewService,
public savedViewService: SavedViewService,
@ -30,12 +42,12 @@ export class DocumentListComponent implements OnInit, OnDestroy {
private toastService: ToastService,
private modalService: NgbModal,
private consumerStatusService: ConsumerStatusService
) { }
) {}
@ViewChild("filterEditor")
@ViewChild('filterEditor')
private filterEditor: FilterEditorComponent
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>;
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
displayMode = 'smallCards' // largeCards, smallCards, details
@ -52,7 +64,9 @@ export class DocumentListComponent implements OnInit, OnDestroy {
}
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) {
@ -71,14 +85,16 @@ export class DocumentListComponent implements OnInit, OnDestroy {
if (localStorage.getItem('document-list:displayMode') != null) {
this.displayMode = localStorage.getItem('document-list:displayMode')
}
this.consumptionFinishedSubscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(() => {
this.list.reload()
})
this.route.paramMap.subscribe(params => {
this.consumptionFinishedSubscription = this.consumerStatusService
.onDocumentConsumptionFinished()
.subscribe(() => {
this.list.reload()
})
this.route.paramMap.subscribe((params) => {
if (params.has('id')) {
this.savedViewService.getCached(+params.get('id')).subscribe(view => {
this.savedViewService.getCached(+params.get('id')).subscribe((view) => {
if (!view) {
this.router.navigate(["404"])
this.router.navigate(['404'])
return
}
this.list.activateSavedView(view)
@ -110,19 +126,23 @@ export class DocumentListComponent implements OnInit, OnDestroy {
id: this.list.activeSavedViewId,
filter_rules: this.list.filterRules,
sort_field: this.list.sortField,
sort_reverse: this.list.sortReverse
sort_reverse: this.list.sortReverse,
}
this.savedViewService.patch(savedView).subscribe(result => {
this.toastService.showInfo($localize`View "${this.list.activeSavedViewTitle}" saved successfully.`)
this.savedViewService.patch(savedView).subscribe((result) => {
this.toastService.showInfo(
$localize`View "${this.list.activeSavedViewTitle}" saved successfully.`
)
this.unmodifiedFilterRules = this.list.filterRules
})
}
}
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.saveClicked.subscribe(formValue => {
modal.componentInstance.saveClicked.subscribe((formValue) => {
modal.componentInstance.buttonsEnabled = false
let savedView: PaperlessSavedView = {
name: formValue.name,
@ -130,16 +150,21 @@ export class DocumentListComponent implements OnInit, OnDestroy {
show_in_sidebar: formValue.showInSideBar,
filter_rules: this.list.filterRules,
sort_reverse: this.list.sortReverse,
sort_field: this.list.sortField
sort_field: this.list.sortField,
}
this.savedViewService.create(savedView).subscribe(() => {
modal.close()
this.toastService.showInfo($localize`View "${savedView.name}" created successfully.`)
}, error => {
modal.componentInstance.error = error.error
modal.componentInstance.buttonsEnabled = true
})
this.savedViewService.create(savedView).subscribe(
() => {
modal.close()
this.toastService.showInfo(
$localize`View "${savedView.name}" created successfully.`
)
},
(error) => {
modal.componentInstance.error = error.error
modal.componentInstance.buttonsEnabled = true
}
)
})
}
@ -170,7 +195,9 @@ export class DocumentListComponent implements OnInit, OnDestroy {
}
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) {

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', () => {
let component: FilterEditorComponent;
let fixture: ComponentFixture<FilterEditorComponent>;
let component: FilterEditorComponent
let fixture: ComponentFixture<FilterEditorComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FilterEditorComponent ]
})
.compileComponents();
});
declarations: [FilterEditorComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(FilterEditorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(FilterEditorComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { PaperlessTag } from 'src/app/data/paperless-tag';
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
import { Subject, Subscription } from 'rxjs';
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';
import {
Component,
EventEmitter,
Input,
Output,
OnInit,
OnDestroy,
ViewChild,
ElementRef,
} from '@angular/core'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { Subject, Subscription } from 'rxjs'
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_CONTENT = "title-content"
const TEXT_FILTER_TARGET_ASN = "asn"
const TEXT_FILTER_TARGET_FULLTEXT_QUERY = "fulltext-query"
const TEXT_FILTER_TARGET_FULLTEXT_MORELIKE = "fulltext-morelike"
const TEXT_FILTER_TARGET_TITLE = 'title'
const TEXT_FILTER_TARGET_TITLE_CONTENT = 'title-content'
const TEXT_FILTER_TARGET_ASN = 'asn'
const TEXT_FILTER_TARGET_FULLTEXT_QUERY = 'fulltext-query'
const TEXT_FILTER_TARGET_FULLTEXT_MORELIKE = 'fulltext-morelike'
@Component({
selector: 'app-filter-editor',
templateUrl: './filter-editor.component.html',
styleUrls: ['./filter-editor.component.scss']
styleUrls: ['./filter-editor.component.scss'],
})
export class FilterEditorComponent implements OnInit, OnDestroy {
generateFilterName() {
if (this.filterRules.length == 1) {
let rule = this.filterRules[0]
switch(this.filterRules[0].rule_type) {
switch (this.filterRules[0].rule_type) {
case FILTER_CORRESPONDENT:
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 {
return $localize`Without correspondent`
}
case FILTER_DOCUMENT_TYPE:
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 {
return $localize`Without document type`
}
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:
if (rule.value == "false") {
if (rule.value == 'false') {
return $localize`Without any tag`
}
@ -62,7 +91,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
}
}
return ""
return ''
}
constructor(
@ -70,28 +99,37 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
private tagService: TagService,
private correspondentService: CorrespondentService,
private documentService: DocumentService
) { }
) {}
@ViewChild("textFilterInput")
@ViewChild('textFilterInput')
textFilterInput: ElementRef
tags: PaperlessTag[] = []
correspondents: PaperlessCorrespondent[] = []
documentTypes: PaperlessDocumentType[] = []
_textFilter = ""
_textFilter = ''
_moreLikeId: number
_moreLikeDoc: PaperlessDocument
get textFilterTargets() {
let targets = [
{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_FULLTEXT_QUERY, name: $localize`Advanced search`}
{ 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_FULLTEXT_QUERY,
name: $localize`Advanced search`,
},
]
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
}
@ -99,10 +137,10 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
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()
correspondentSelectionModel = new FilterableDropdownSelectionModel()
documentTypeSelectionModel = new FilterableDropdownSelectionModel()
@ -126,7 +164,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
}
@Input()
set filterRules (value: FilterRule[]) {
set filterRules(value: FilterRule[]) {
this._filterRules = value
this.documentTypeSelectionModel.clear(false)
@ -139,7 +177,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.dateCreatedBefore = null
this.dateCreatedAfter = null
value.forEach(rule => {
value.forEach((rule) => {
switch (rule.rule_type) {
case FILTER_TITLE:
this._textFilter = rule.value
@ -160,7 +198,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
case FILTER_FULLTEXT_MORELIKE:
this._moreLikeId = +rule.value
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._textFilter = result.title
})
@ -178,23 +216,43 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.dateAddedBefore = rule.value
break
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
case FILTER_HAS_TAGS_ANY:
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
case FILTER_HAS_ANY_TAG:
this.tagSelectionModel.set(null, ToggleableItemState.Selected, false)
break
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
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
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
}
})
@ -203,49 +261,104 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
get filterRules(): FilterRule[] {
let filterRules: FilterRule[] = []
if (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_CONTENT
) {
filterRules.push({
rule_type: FILTER_TITLE_CONTENT,
value: this._textFilter,
})
}
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) {
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) {
filterRules.push({rule_type: FILTER_FULLTEXT_QUERY, value: this._textFilter})
if (
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) {
filterRules.push({rule_type: FILTER_FULLTEXT_MORELIKE, value: this._moreLikeId?.toString()})
if (
this._moreLikeId &&
this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE
) {
filterRules.push({
rule_type: FILTER_FULLTEXT_MORELIKE,
value: this._moreLikeId?.toString(),
})
}
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 {
const tagFilterType = this.tagSelectionModel.logicalOperator == 'and' ? 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()})
})
const tagFilterType =
this.tagSelectionModel.logicalOperator == 'and'
? 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.correspondentSelectionModel.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.correspondentSelectionModel
.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(),
})
})
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) {
filterRules.push({rule_type: FILTER_CREATED_AFTER, value: this.dateCreatedAfter})
filterRules.push({
rule_type: FILTER_CREATED_AFTER,
value: this.dateCreatedAfter,
})
}
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) {
filterRules.push({rule_type: FILTER_ADDED_AFTER, value: this.dateAddedAfter})
filterRules.push({
rule_type: FILTER_ADDED_AFTER,
value: this.dateAddedAfter,
})
}
return filterRules
}
@ -260,14 +373,20 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
if (this._unmodifiedFilterRules.length != this._filterRules.length) {
modified = true
} else {
modified = this._unmodifiedFilterRules.some(rule => {
return (this._filterRules.find(fri => fri.rule_type == rule.rule_type && fri.value == rule.value) == undefined)
modified = this._unmodifiedFilterRules.some((rule) => {
return (
this._filterRules.find(
(fri) => fri.rule_type == rule.rule_type && fri.value == rule.value
) == undefined
)
})
if (!modified) {
// only check other direction if we havent already determined is modified
modified = this._filterRules.some(rule => {
this._unmodifiedFilterRules.find(fr => fr.rule_type == rule.rule_type && fr.value == rule.value) == undefined
modified = this._filterRules.some((rule) => {
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
ngOnInit() {
this.tagService.listAll().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.tagService
.listAll()
.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.subscription = this.textFilterDebounce.pipe(
debounceTime(400),
distinctUntilChanged()
).subscribe(text => {
this._textFilter = text
this.documentService.searchQuery = text
this.updateRules()
})
this.subscription = this.textFilterDebounce
.pipe(debounceTime(400), distinctUntilChanged())
.subscribe((text) => {
this._textFilter = text
this.documentService.searchQuery = text
this.updateRules()
})
if (this._textFilter) this.documentService.searchQuery = this._textFilter
}
ngOnDestroy() {
@ -324,11 +447,17 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
}
addCorrespondent(correspondentId: number) {
this.correspondentSelectionModel.set(correspondentId, ToggleableItemState.Selected)
this.correspondentSelectionModel.set(
correspondentId,
ToggleableItemState.Selected
)
}
addDocumentType(documentTypeId: number) {
this.documentTypeSelectionModel.set(documentTypeId, ToggleableItemState.Selected)
this.documentTypeSelectionModel.set(
documentTypeId,
ToggleableItemState.Selected
)
}
onTagsDropdownOpen() {
@ -344,8 +473,11 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
}
changeTextFilterTarget(target) {
if (this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE && target != TEXT_FILTER_TARGET_FULLTEXT_MORELIKE) {
this._textFilter = ""
if (
this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE &&
target != TEXT_FILTER_TARGET_FULLTEXT_MORELIKE
) {
this._textFilter = ''
}
this.textFilterTarget = target
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', () => {
let component: SaveViewConfigDialogComponent;
let fixture: ComponentFixture<SaveViewConfigDialogComponent>;
let component: SaveViewConfigDialogComponent
let fixture: ComponentFixture<SaveViewConfigDialogComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SaveViewConfigDialogComponent ]
})
.compileComponents();
});
declarations: [SaveViewConfigDialogComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(SaveViewConfigDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(SaveViewConfigDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { FormControl, FormGroup } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'app-save-view-config-dialog',
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 {
constructor(private modal: NgbActiveModal) { }
constructor(private modal: NgbActiveModal) {}
@Output()
public saveClicked = new EventEmitter()
@ -22,7 +21,7 @@ export class SaveViewConfigDialogComponent implements OnInit {
closeEnabled = false
_defaultName = ""
_defaultName = ''
get defaultName() {
return this._defaultName
@ -31,7 +30,7 @@ export class SaveViewConfigDialogComponent implements OnInit {
@Input()
set defaultName(value: string) {
this._defaultName = value
this.saveViewConfigForm.patchValue({name: value})
this.saveViewConfigForm.patchValue({ name: value })
}
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
setTimeout(() => {
this.closeEnabled = true
});
})
}
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', () => {
let component: CorrespondentEditDialogComponent;
let fixture: ComponentFixture<CorrespondentEditDialogComponent>;
let component: CorrespondentEditDialogComponent
let fixture: ComponentFixture<CorrespondentEditDialogComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CorrespondentEditDialogComponent ]
})
.compileComponents();
});
declarations: [CorrespondentEditDialogComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(CorrespondentEditDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(CorrespondentEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,19 +1,22 @@
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
import { ToastService } from 'src/app/services/toast.service';
import { Component } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { ToastService } from 'src/app/services/toast.service'
@Component({
selector: 'app-correspondent-edit-dialog',
templateUrl: './correspondent-edit-dialog.component.html',
styleUrls: ['./correspondent-edit-dialog.component.scss']
styleUrls: ['./correspondent-edit-dialog.component.scss'],
})
export class CorrespondentEditDialogComponent extends EditDialogComponent<PaperlessCorrespondent> {
constructor(service: CorrespondentService, activeModal: NgbActiveModal, toastService: ToastService) {
constructor(
service: CorrespondentService,
activeModal: NgbActiveModal,
toastService: ToastService
) {
super(service, activeModal, toastService)
}
@ -29,9 +32,8 @@ export class CorrespondentEditDialogComponent extends EditDialogComponent<Paperl
return new FormGroup({
name: new FormControl(''),
matching_algorithm: new FormControl(1),
match: new FormControl(""),
is_insensitive: new FormControl(true)
match: new FormControl(''),
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', () => {
let component: CorrespondentListComponent;
let fixture: ComponentFixture<CorrespondentListComponent>;
let component: CorrespondentListComponent
let fixture: ComponentFixture<CorrespondentListComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CorrespondentListComponent ]
})
.compileComponents();
});
declarations: [CorrespondentListComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(CorrespondentListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(CorrespondentListComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,25 +1,31 @@
import { Component } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { FILTER_CORRESPONDENT } from 'src/app/data/filter-rule-type';
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
import { ToastService } from 'src/app/services/toast.service';
import { GenericListComponent } from '../generic-list/generic-list.component';
import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog/correspondent-edit-dialog.component';
import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { FILTER_CORRESPONDENT } from 'src/app/data/filter-rule-type'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { ToastService } from 'src/app/services/toast.service'
import { GenericListComponent } from '../generic-list/generic-list.component'
import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog/correspondent-edit-dialog.component'
@Component({
selector: 'app-correspondent-list',
templateUrl: './correspondent-list.component.html',
styleUrls: ['./correspondent-list.component.scss']
styleUrls: ['./correspondent-list.component.scss'],
})
export class CorrespondentListComponent extends GenericListComponent<PaperlessCorrespondent> {
constructor(correspondentsService: CorrespondentService, modalService: NgbModal,
constructor(
correspondentsService: CorrespondentService,
modalService: NgbModal,
private list: DocumentListViewService,
toastService: ToastService
) {
super(correspondentsService,modalService,CorrespondentEditDialogComponent, toastService)
super(
correspondentsService,
modalService,
CorrespondentEditDialogComponent,
toastService
)
}
getDeleteMessage(object: PaperlessCorrespondent) {
@ -27,6 +33,8 @@ export class CorrespondentListComponent extends GenericListComponent<PaperlessCo
}
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', () => {
let component: DocumentTypeEditDialogComponent;
let fixture: ComponentFixture<DocumentTypeEditDialogComponent>;
let component: DocumentTypeEditDialogComponent
let fixture: ComponentFixture<DocumentTypeEditDialogComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DocumentTypeEditDialogComponent ]
})
.compileComponents();
});
declarations: [DocumentTypeEditDialogComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(DocumentTypeEditDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(DocumentTypeEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,19 +1,22 @@
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
import { ToastService } from 'src/app/services/toast.service';
import { Component } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { ToastService } from 'src/app/services/toast.service'
@Component({
selector: 'app-document-type-edit-dialog',
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> {
constructor(service: DocumentTypeService, activeModal: NgbActiveModal, toastService: ToastService) {
constructor(
service: DocumentTypeService,
activeModal: NgbActiveModal,
toastService: ToastService
) {
super(service, activeModal, toastService)
}
@ -29,9 +32,8 @@ export class DocumentTypeEditDialogComponent extends EditDialogComponent<Paperle
return new FormGroup({
name: new FormControl(''),
matching_algorithm: new FormControl(1),
match: new FormControl(""),
is_insensitive: new FormControl(true)
match: new FormControl(''),
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', () => {
let component: DocumentTypeListComponent;
let fixture: ComponentFixture<DocumentTypeListComponent>;
let component: DocumentTypeListComponent
let fixture: ComponentFixture<DocumentTypeListComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DocumentTypeListComponent ]
})
.compileComponents();
});
declarations: [DocumentTypeListComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(DocumentTypeListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(DocumentTypeListComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,21 +1,22 @@
import { Component } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { FILTER_DOCUMENT_TYPE } from 'src/app/data/filter-rule-type';
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
import { ToastService } from 'src/app/services/toast.service';
import { GenericListComponent } from '../generic-list/generic-list.component';
import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog/document-type-edit-dialog.component';
import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { FILTER_DOCUMENT_TYPE } from 'src/app/data/filter-rule-type'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { ToastService } from 'src/app/services/toast.service'
import { GenericListComponent } from '../generic-list/generic-list.component'
import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog/document-type-edit-dialog.component'
@Component({
selector: 'app-document-type-list',
templateUrl: './document-type-list.component.html',
styleUrls: ['./document-type-list.component.scss']
styleUrls: ['./document-type-list.component.scss'],
})
export class DocumentTypeListComponent extends GenericListComponent<PaperlessDocumentType> {
constructor(service: DocumentTypeService, modalService: NgbModal,
constructor(
service: DocumentTypeService,
modalService: NgbModal,
private list: DocumentListViewService,
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}"?`
}
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', () => {
let component: GenericListComponent;
let fixture: ComponentFixture<GenericListComponent>;
let component: GenericListComponent
let fixture: ComponentFixture<GenericListComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ GenericListComponent ]
})
.compileComponents();
});
declarations: [GenericListComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(GenericListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(GenericListComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subject, Subscription } from 'rxjs';
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';
import {
Directive,
OnDestroy,
OnInit,
QueryList,
ViewChildren,
} from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subject, Subscription } from 'rxjs'
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()
export abstract class GenericListComponent<T extends ObjectWithId> implements OnInit, OnDestroy {
export abstract class GenericListComponent<T extends ObjectWithId>
implements OnInit, OnDestroy
{
constructor(
private service: AbstractNameFilterService<T>,
private modalService: NgbModal,
private editDialogComponent: any,
private toastService: ToastService) {
}
private toastService: ToastService
) {}
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>;
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
public data: T[] = []
@ -38,9 +52,11 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
if (o.matching_algorithm == MATCH_AUTO) {
return $localize`Automatic`
} 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 {
return "-"
return '-'
}
}
@ -50,20 +66,18 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
this.reloadData()
}
ngOnInit(): void {
this.reloadData()
this.nameFilterDebounce = new Subject<string>()
this.subscription = this.nameFilterDebounce.pipe(
debounceTime(400),
distinctUntilChanged()
).subscribe(title => {
this._nameFilter = title
this.page = 1
this.reloadData()
})
this.subscription = this.nameFilterDebounce
.pipe(debounceTime(400), distinctUntilChanged())
.subscribe((title) => {
this._nameFilter = title
this.page = 1
this.reloadData()
})
}
ngOnDestroy() {
@ -71,25 +85,37 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
}
reloadData() {
this.service.listFiltered(this.page, null, this.sortField, this.sortReverse, this._nameFilter).subscribe(c => {
this.data = c.results
this.collectionSize = c.count
});
this.service
.listFiltered(
this.page,
null,
this.sortField,
this.sortReverse,
this._nameFilter
)
.subscribe((c) => {
this.data = c.results
this.collectionSize = c.count
})
}
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.success.subscribe(o => {
activeModal.componentInstance.success.subscribe((o) => {
this.reloadData()
})
}
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.dialogMode = 'edit'
activeModal.componentInstance.success.subscribe(o => {
activeModal.componentInstance.success.subscribe((o) => {
this.reloadData()
})
}
@ -99,23 +125,31 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
}
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.messageBold = this.getDeleteMessage(object)
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.confirmClicked.subscribe(() => {
activeModal.componentInstance.buttonsEnabled = false
this.service.delete(object).subscribe(_ => {
activeModal.close()
this.reloadData()
}, error => {
activeModal.componentInstance.buttonsEnabled = true
this.toastService.showError($localize`Error while deleting element: ${JSON.stringify(error.error)}`)
})
}
)
this.service.delete(object).subscribe(
(_) => {
activeModal.close()
this.reloadData()
},
(error) => {
activeModal.componentInstance.buttonsEnabled = true
this.toastService.showError(
$localize`Error while deleting element: ${JSON.stringify(
error.error
)}`
)
}
)
})
}
get nameFilter() {
@ -125,7 +159,7 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
set nameFilter(nameFilter: string) {
this.nameFilterDebounce.next(nameFilter)
}
onNameFilterKeyUp(event: KeyboardEvent) {
if (event.code == 'Escape') this.nameFilterDebounce.next(null)
}

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', () => {
let component: LogsComponent;
let fixture: ComponentFixture<LogsComponent>;
let component: LogsComponent
let fixture: ComponentFixture<LogsComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LogsComponent ]
})
.compileComponents();
});
declarations: [LogsComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(LogsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(LogsComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { LogService } from 'src/app/services/rest/log.service';
import {
Component,
ElementRef,
OnInit,
AfterViewChecked,
ViewChild,
} from '@angular/core'
import { LogService } from 'src/app/services/rest/log.service'
@Component({
selector: 'app-logs',
templateUrl: './logs.component.html',
styleUrls: ['./logs.component.scss']
styleUrls: ['./logs.component.scss'],
})
export class LogsComponent implements OnInit, AfterViewChecked {
constructor(private logService: LogService) { }
constructor(private logService: LogService) {}
logs: string[] = []
@ -19,7 +24,7 @@ export class LogsComponent implements OnInit, AfterViewChecked {
@ViewChild('logContainer') logContainer: ElementRef
ngOnInit(): void {
this.logService.list().subscribe(result => {
this.logService.list().subscribe((result) => {
this.logFiles = result
if (this.logFiles.length > 0) {
this.activeLog = this.logFiles[0]
@ -29,25 +34,28 @@ export class LogsComponent implements OnInit, AfterViewChecked {
}
ngAfterViewChecked() {
this.scrollToBottom();
this.scrollToBottom()
}
reloadLogs() {
this.logService.get(this.activeLog).subscribe(result => {
this.logs = result
}, error => {
this.logs = []
})
this.logService.get(this.activeLog).subscribe(
(result) => {
this.logs = result
},
(error) => {
this.logs = []
}
)
}
getLogLevel(log: string) {
if (log.indexOf("[DEBUG]") != -1) {
if (log.indexOf('[DEBUG]') != -1) {
return 10
} else if (log.indexOf("[WARNING]") != -1) {
} else if (log.indexOf('[WARNING]') != -1) {
return 30
} else if (log.indexOf("[ERROR]") != -1) {
} else if (log.indexOf('[ERROR]') != -1) {
return 40
} else if (log.indexOf("[CRITICAL]") != -1) {
} else if (log.indexOf('[CRITICAL]') != -1) {
return 50
} else {
return 20
@ -58,8 +66,7 @@ export class LogsComponent implements OnInit, AfterViewChecked {
this.logContainer?.nativeElement.scroll({
top: this.logContainer.nativeElement.scrollHeight,
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', () => {
let component: SettingsComponent;
let fixture: ComponentFixture<SettingsComponent>;
let component: SettingsComponent
let fixture: ComponentFixture<SettingsComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SettingsComponent ]
})
.compileComponents();
});
declarations: [SettingsComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(SettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(SettingsComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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 { 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';
import {
Component,
Inject,
LOCALE_ID,
OnInit,
OnDestroy,
Renderer2,
} from '@angular/core'
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({
selector: 'app-settings',
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.scss']
styleUrls: ['./settings.component.scss'],
})
export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
savedViewGroup = new FormGroup({})
settingsForm = new FormGroup({
'bulkEditConfirmationDialogs': new FormControl(null),
'bulkEditApplyOnClose': new FormControl(null),
'documentListItemPerPage': new FormControl(null),
'darkModeUseSystem': new FormControl(null),
'darkModeEnabled': new FormControl(null),
'darkModeInvertThumbs': new FormControl(null),
'themeColor': new FormControl(null),
'useNativePdfViewer': new FormControl(null),
'savedViews': this.savedViewGroup,
'displayLanguage': new FormControl(null),
'dateLocale': new FormControl(null),
'dateFormat': new FormControl(null),
'notificationsConsumerNewDocument': new FormControl(null),
'notificationsConsumerSuccess': new FormControl(null),
'notificationsConsumerFailed': new FormControl(null),
'notificationsConsumerSuppressOnDashboard': new FormControl(null),
bulkEditConfirmationDialogs: new FormControl(null),
bulkEditApplyOnClose: new FormControl(null),
documentListItemPerPage: new FormControl(null),
darkModeUseSystem: new FormControl(null),
darkModeEnabled: new FormControl(null),
darkModeInvertThumbs: new FormControl(null),
themeColor: new FormControl(null),
useNativePdfViewer: new FormControl(null),
savedViews: this.savedViewGroup,
displayLanguage: new FormControl(null),
dateLocale: new FormControl(null),
dateFormat: new FormControl(null),
notificationsConsumerNewDocument: new FormControl(null),
notificationsConsumerSuccess: new FormControl(null),
notificationsConsumerFailed: new FormControl(null),
notificationsConsumerSuppressOnDashboard: new FormControl(null),
})
savedViews: PaperlessSavedView[]
@ -44,7 +54,11 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
isDirty: Boolean = false
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(
@ -53,48 +67,71 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
private toastService: ToastService,
private settings: SettingsService,
@Inject(LOCALE_ID) public currentLocale: string
) { }
) {}
ngOnInit() {
this.savedViewService.listAll().subscribe(r => {
this.savedViewService.listAll().subscribe((r) => {
this.savedViews = r.results
let storeData = {
'bulkEditConfirmationDialogs': this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS),
'bulkEditApplyOnClose': this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE),
'documentListItemPerPage': this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE),
'darkModeUseSystem': this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM),
'darkModeEnabled': this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED),
'darkModeInvertThumbs': this.settings.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED),
'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),
bulkEditConfirmationDialogs: this.settings.get(
SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS
),
bulkEditApplyOnClose: this.settings.get(
SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE
),
documentListItemPerPage: this.settings.get(
SETTINGS_KEYS.DOCUMENT_LIST_SIZE
),
darkModeUseSystem: this.settings.get(
SETTINGS_KEYS.DARK_MODE_USE_SYSTEM
),
darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED),
darkModeInvertThumbs: this.settings.get(
SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED
),
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) {
storeData.savedViews[view.id.toString()] = {
"id": view.id,
"name": view.name,
"show_on_dashboard": view.show_on_dashboard,
"show_in_sidebar": view.show_in_sidebar
id: view.id,
name: view.name,
show_on_dashboard: view.show_on_dashboard,
show_in_sidebar: view.show_in_sidebar,
}
this.savedViewGroup.addControl(view.id.toString(), new FormGroup({
"id": new FormControl(null),
"name": new FormControl(null),
"show_on_dashboard": new FormControl(null),
"show_in_sidebar": new FormControl(null)
}))
this.savedViewGroup.addControl(
view.id.toString(),
new FormGroup({
id: 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.storeSub = this.store.asObservable().subscribe(state => {
this.storeSub = this.store.asObservable().subscribe((state) => {
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())
// 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
})
// "Live" visual changes prior to save
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() {
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) {
this.savedViewService.delete(savedView).subscribe(() => {
this.savedViewGroup.removeControl(savedView.id.toString())
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() {
this.settings.set(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE, this.settingsForm.value.bulkEditApplyOnClose)
this.settings.set(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS, this.settingsForm.value.bulkEditConfirmationDialogs)
this.settings.set(SETTINGS_KEYS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage)
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(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.set(
SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE,
this.settingsForm.value.bulkEditApplyOnClose
)
this.settings.set(
SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS,
this.settingsForm.value.bulkEditConfirmationDialogs
)
this.settings.set(
SETTINGS_KEYS.DOCUMENT_LIST_SIZE,
this.settingsForm.value.documentListItemPerPage
)
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(
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.store.next(this.settingsForm.value)
this.documentListViewService.updatePageSize()
@ -149,14 +234,14 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
}
get displayLanguageOptions(): LanguageOption[] {
return [
{code: "", name: $localize`Use system language`}
].concat(this.settings.getLanguageOptions())
return [{ code: '', name: $localize`Use system language` }].concat(
this.settings.getLanguageOptions()
)
}
get dateLocaleOptions(): LanguageOption[] {
return [
{code: "", name: $localize`Use date format of display language`}
{ code: '', name: $localize`Use date format of display language` },
].concat(this.settings.getDateLocaleOptions())
}
@ -170,18 +255,24 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
x.push(this.savedViewGroup.value[id])
}
if (x.length > 0) {
this.savedViewService.patchMany(x).subscribe(s => {
this.saveLocalSettings()
}, error => {
this.toastService.showError($localize`Error while storing settings on server: ${JSON.stringify(error.error)}`)
})
this.savedViewService.patchMany(x).subscribe(
(s) => {
this.saveLocalSettings()
},
(error) => {
this.toastService.showError(
$localize`Error while storing settings on server: ${JSON.stringify(
error.error
)}`
)
}
)
} else {
this.saveLocalSettings()
}
}
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', () => {
let component: TagEditDialogComponent;
let fixture: ComponentFixture<TagEditDialogComponent>;
let component: TagEditDialogComponent
let fixture: ComponentFixture<TagEditDialogComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TagEditDialogComponent ]
})
.compileComponents();
});
declarations: [TagEditDialogComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(TagEditDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(TagEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,20 +1,23 @@
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
import { PaperlessTag } from 'src/app/data/paperless-tag';
import { TagService } from 'src/app/services/rest/tag.service';
import { ToastService } from 'src/app/services/toast.service';
import { randomColor } from 'src/app/utils/color';
import { Component } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { TagService } from 'src/app/services/rest/tag.service'
import { ToastService } from 'src/app/services/toast.service'
import { randomColor } from 'src/app/utils/color'
@Component({
selector: 'app-tag-edit-dialog',
templateUrl: './tag-edit-dialog.component.html',
styleUrls: ['./tag-edit-dialog.component.scss']
styleUrls: ['./tag-edit-dialog.component.scss'],
})
export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
constructor(service: TagService, activeModal: NgbActiveModal, toastService: ToastService) {
constructor(
service: TagService,
activeModal: NgbActiveModal,
toastService: ToastService
) {
super(service, activeModal, toastService)
}
@ -32,9 +35,8 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
color: new FormControl(randomColor()),
is_inbox_tag: new FormControl(false),
matching_algorithm: new FormControl(1),
match: new FormControl(""),
is_insensitive: new FormControl(true)
match: new FormControl(''),
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', () => {
let component: TagListComponent;
let fixture: ComponentFixture<TagListComponent>;
let component: TagListComponent
let fixture: ComponentFixture<TagListComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TagListComponent ]
})
.compileComponents();
});
declarations: [TagListComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(TagListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(TagListComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy();
});
});
expect(component).toBeTruthy()
})
})

View File

@ -1,21 +1,22 @@
import { Component } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type';
import { PaperlessTag } from 'src/app/data/paperless-tag';
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { TagService } from 'src/app/services/rest/tag.service';
import { ToastService } from 'src/app/services/toast.service';
import { GenericListComponent } from '../generic-list/generic-list.component';
import { TagEditDialogComponent } from './tag-edit-dialog/tag-edit-dialog.component';
import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { TagService } from 'src/app/services/rest/tag.service'
import { ToastService } from 'src/app/services/toast.service'
import { GenericListComponent } from '../generic-list/generic-list.component'
import { TagEditDialogComponent } from './tag-edit-dialog/tag-edit-dialog.component'
@Component({
selector: 'app-tag-list',
templateUrl: './tag-list.component.html',
styleUrls: ['./tag-list.component.scss']
styleUrls: ['./tag-list.component.scss'],
})
export class TagListComponent extends GenericListComponent<PaperlessTag> {
constructor(tagService: TagService, modalService: NgbModal,
constructor(
tagService: TagService,
modalService: NgbModal,
private list: DocumentListViewService,
toastService: ToastService
) {
@ -27,7 +28,8 @@ export class TagListComponent extends GenericListComponent<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', () => {
let component: NotFoundComponent;
let fixture: ComponentFixture<NotFoundComponent>;
let component: NotFoundComponent
let fixture: ComponentFixture<NotFoundComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NotFoundComponent ]
})
.compileComponents();
});
declarations: [NotFoundComponent],
}).compileComponents()
})
beforeEach(() => {
fixture = TestBed.createComponent(NotFoundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(NotFoundComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
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({
selector: 'app-not-found',
templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.scss']
styleUrls: ['./not-found.component.scss'],
})
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_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_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_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_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_CREATED_DAY, filtervar: "created__day", datatype: "number", 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_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_ASN_ISNULL, filtervar: "archive_serial_number__isnull", datatype: "boolean", multi: false},
{
id: FILTER_TITLE_CONTENT,
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, filtervar: "more_like_id", datatype: "number", multi: false},
{
id: FILTER_FULLTEXT_MORELIKE,
filtervar: 'more_like_id',
datatype: 'number',
multi: false,
},
]
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[] {
if (filterRules) {
let newRules: FilterRule[] = []
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
} else {
@ -13,7 +16,13 @@ export function cloneFilterRules(filterRules: FilterRule[]): FilterRule[] {
}
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 {

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_ALL = 2
@ -9,26 +8,48 @@ export const MATCH_FUZZY = 5
export const MATCH_AUTO = 6
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_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`},
{
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_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 {
name?: string
name?: string
slug?: string
slug?: string
match?: string
match?: string
matching_algorithm?: number
matching_algorithm?: number
is_insensitive?: boolean
document_count?: number
is_insensitive?: boolean
document_count?: number
}

View File

@ -1,5 +1,3 @@
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 {
last_correspondence?: Date
}

View File

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

View File

@ -1,9 +1,7 @@
export interface PaperlessDocumentSuggestions {
tags?: number[]
correspondents?: 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,50 +5,46 @@ import { PaperlessDocumentType } from './paperless-document-type'
import { Observable } from 'rxjs'
export interface SearchHit {
score?: number
rank?: number
highlights?: string
}
export interface PaperlessDocument extends ObjectWithId {
correspondent$?: Observable<PaperlessCorrespondent>
correspondent$?: Observable<PaperlessCorrespondent>
correspondent?: number
correspondent?: number
document_type$?: Observable<PaperlessDocumentType>
document_type$?: Observable<PaperlessDocumentType>
document_type?: number
document_type?: number
title?: string
title?: string
content?: string
content?: string
file_type?: string
file_type?: string
tags$?: Observable<PaperlessTag[]>
tags$?: Observable<PaperlessTag[]>
tags?: number[]
tags?: number[]
checksum?: string
checksum?: string
created?: Date
created?: Date
modified?: Date
modified?: Date
added?: Date
added?: Date
file_name?: string
file_name?: string
download_url?: string
download_url?: string
thumbnail_url?: string
thumbnail_url?: string
archive_serial_number?: number
__search_hit__?: SearchHit
archive_serial_number?: number
__search_hit__?: SearchHit
}

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