mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 19:17:13 -05:00 
			
		
		
		
	Merge pull request #276 from paperless-ngx/prettier-cleanup-ts-js-md
Prettier cleanup for .ts .js and .md files
This commit is contained in:
		
						commit
						ef2b4a7536
					
				
							
								
								
									
										16
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							@ -1,10 +1,9 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
name: Bug report
 | 
					name: Bug report
 | 
				
			||||||
about: Something is not working
 | 
					about: Something is not working
 | 
				
			||||||
title: "[BUG] Concise description of the issue"
 | 
					title: '[BUG] Concise description of the issue'
 | 
				
			||||||
labels: ''
 | 
					labels: ''
 | 
				
			||||||
assignees: ''
 | 
					assignees: ''
 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!---
 | 
					<!---
 | 
				
			||||||
@ -24,6 +23,7 @@ A clear and concise description of what the bug is.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
**To Reproduce**
 | 
					**To Reproduce**
 | 
				
			||||||
Steps to reproduce the behavior:
 | 
					Steps to reproduce the behavior:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
1. Go to '...'
 | 
					1. Go to '...'
 | 
				
			||||||
2. Click on '....'
 | 
					2. Click on '....'
 | 
				
			||||||
3. Scroll down to '....'
 | 
					3. Scroll down to '....'
 | 
				
			||||||
@ -36,13 +36,15 @@ A clear and concise description of what you expected to happen.
 | 
				
			|||||||
If applicable, add screenshots to help explain your problem.
 | 
					If applicable, add screenshots to help explain your problem.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Webserver logs**
 | 
					**Webserver logs**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
If available, post any logs from the web server related to your issue.
 | 
					If available, post any logs from the web server related to your issue.
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Relevant information**
 | 
					**Relevant information**
 | 
				
			||||||
 - Host OS of the machine running paperless: [e.g. Archlinux / Ubuntu 20.04]
 | 
					
 | 
				
			||||||
 - Browser [e.g. chrome, safari]
 | 
					- Host OS of the machine running paperless: [e.g. Archlinux / Ubuntu 20.04]
 | 
				
			||||||
 - Version [e.g. 1.0.0]
 | 
					- Browser [e.g. chrome, safari]
 | 
				
			||||||
 - Installation method: [docker / bare metal]
 | 
					- Version [e.g. 1.0.0]
 | 
				
			||||||
 - Any configuration changes you made in `docker-compose.yml`, `docker-compose.env` or `paperless.conf`.
 | 
					- Installation method: [docker / bare metal]
 | 
				
			||||||
 | 
					- Any configuration changes you made in `docker-compose.yml`, `docker-compose.env` or `paperless.conf`.
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								.github/ISSUE_TEMPLATE/other.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/ISSUE_TEMPLATE/other.md
									
									
									
									
										vendored
									
									
								
							@ -1,10 +1,9 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
name: Other
 | 
					name: Other
 | 
				
			||||||
about: Anything that is not a feature request or bug.
 | 
					about: Anything that is not a feature request or bug.
 | 
				
			||||||
title: "[Other] Title of your issue"
 | 
					title: '[Other] Title of your issue'
 | 
				
			||||||
labels: ''
 | 
					labels: ''
 | 
				
			||||||
assignees: ''
 | 
					assignees: ''
 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!--
 | 
					<!--
 | 
				
			||||||
 | 
				
			|||||||
@ -4,10 +4,10 @@ If you feel like contributing to the project, please do! Bug fixes and improveme
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
If you want to implement something big:
 | 
					If you want to implement something big:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Please start a discussion about that in the issues! Maybe something similar is already in development and we can make it happen together.
 | 
					- Please start a discussion about that in the issues! Maybe something similar is already in development and we can make it happen together.
 | 
				
			||||||
* When making additions to the project, consider if the majority of users will benefit from your change. If not, you're probably better of forking the project.
 | 
					- When making additions to the project, consider if the majority of users will benefit from your change. If not, you're probably better of forking the project.
 | 
				
			||||||
* Also consider if your change will get in the way of other users. A good change is a change that enhances the experience of some users who want that change and does not affect users who do not care about the change.
 | 
					- Also consider if your change will get in the way of other users. A good change is a change that enhances the experience of some users who want that change and does not affect users who do not care about the change.
 | 
				
			||||||
* Please see the [paperless-ngx merge process](#merging-prs) below.
 | 
					- Please see the [paperless-ngx merge process](#merging-prs) below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Python
 | 
					## Python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -41,9 +41,9 @@ PRs deemed `non-trivial` will go through a stricter review process before being
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Examples of `non-trivial` PRs might include:
 | 
					Examples of `non-trivial` PRs might include:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Additional features
 | 
					- Additional features
 | 
				
			||||||
* Large changes to many distinct files
 | 
					- Large changes to many distinct files
 | 
				
			||||||
* Breaking or depreciation of existing features
 | 
					- Breaking or depreciation of existing features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Our community review process for `non-trivial` PRs is the following:
 | 
					Our community review process for `non-trivial` PRs is the following:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -75,18 +75,18 @@ If a language has already been added, and you would like to contribute new trans
 | 
				
			|||||||
If you would like the project to be translated to another language, first head over to https://crwd.in/paperless-ngx to check if that language has already been enabled for translation.
 | 
					If you would like the project to be translated to another language, first head over to https://crwd.in/paperless-ngx to check if that language has already been enabled for translation.
 | 
				
			||||||
If not, please request the language to be added by creating an issue on GitHub. The issue should contain:
 | 
					If not, please request the language to be added by creating an issue on GitHub. The issue should contain:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* English name of the language (the localized name can be added on Crowdin).
 | 
					- English name of the language (the localized name can be added on Crowdin).
 | 
				
			||||||
* ISO language code. A list of those can be found here: https://support.crowdin.com/enterprise/language-codes/
 | 
					- ISO language code. A list of those can be found here: https://support.crowdin.com/enterprise/language-codes/
 | 
				
			||||||
* Date format commonly used for the language, e.g. dd/mm/yyyy, mm/dd/yyyy, etc.
 | 
					- Date format commonly used for the language, e.g. dd/mm/yyyy, mm/dd/yyyy, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
After the language has been added and some translations have been made on Crowdin, the language needs to be enabled in the code.
 | 
					After the language has been added and some translations have been made on Crowdin, the language needs to be enabled in the code.
 | 
				
			||||||
Note that there is no need to manually add a .po of .xlf file as those will be automatically generated and imported from Crowdin.
 | 
					Note that there is no need to manually add a .po of .xlf file as those will be automatically generated and imported from Crowdin.
 | 
				
			||||||
The following files need to be changed:
 | 
					The following files need to be changed:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* src-ui/angular.json (under the _projects/paperless-ui/i18n/locales_ JSON key)
 | 
					- src-ui/angular.json (under the _projects/paperless-ui/i18n/locales_ JSON key)
 | 
				
			||||||
* src/paperless/settings.py (in the _LANGUAGES_ array)
 | 
					- src/paperless/settings.py (in the _LANGUAGES_ array)
 | 
				
			||||||
* src-ui/src/app/services/settings.service.ts (inside the _getLanguageOptions_ method)
 | 
					- src-ui/src/app/services/settings.service.ts (inside the _getLanguageOptions_ method)
 | 
				
			||||||
* src-ui/src/app/app.module.ts (import locale from _angular/common/locales_ and call _registerLocaleData_)
 | 
					- src-ui/src/app/app.module.ts (import locale from _angular/common/locales_ and call _registerLocaleData_)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Please add the language in the correct order, alphabetically by locale.
 | 
					Please add the language in the correct order, alphabetically by locale.
 | 
				
			||||||
Note that _en-us_ needs to stay on top of the list, as it is the default project language
 | 
					Note that _en-us_ needs to stay on top of the list, as it is the default project language
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										61
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								README.md
									
									
									
									
									
								
							@ -10,15 +10,15 @@
 | 
				
			|||||||
</p>
 | 
					</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!-- omit in toc -->
 | 
					<!-- omit in toc -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Paperless-ngx
 | 
					# Paperless-ngx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Paperless-ngx is a document management system that transforms your physical documents into a searchable online archive so you can keep, well, *less paper*.
 | 
					Paperless-ngx is a document management system that transforms your physical documents into a searchable online archive so you can keep, well, _less paper_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Paperless-ngx forked from [paperless-ng](https://github.com/jonaswinkler/paperless-ng) to continue the great work and distribute responsibility of supporting and advancing the project among a team of people. [Consider joining us!](#community-support) Discussion of this transition can be found in issues
 | 
					Paperless-ngx forked from [paperless-ng](https://github.com/jonaswinkler/paperless-ng) to continue the great work and distribute responsibility of supporting and advancing the project among a team of people. [Consider joining us!](#community-support) Discussion of this transition can be found in issues
 | 
				
			||||||
[#1599](https://github.com/jonaswinkler/paperless-ng/issues/1599) and [#1632](https://github.com/jonaswinkler/paperless-ng/issues/1632).
 | 
					[#1599](https://github.com/jonaswinkler/paperless-ng/issues/1599) and [#1632](https://github.com/jonaswinkler/paperless-ng/issues/1632).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
A demo is available at [demo.paperless-ngx.com](https://demo.paperless-ngx.com) using login `demo` / `demo`. *Note: demo content is reset frequently and confidential information should not be uploaded.*
 | 
					A demo is available at [demo.paperless-ngx.com](https://demo.paperless-ngx.com) using login `demo` / `demo`. _Note: demo content is reset frequently and confidential information should not be uploaded._
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [Features](#features)
 | 
					- [Features](#features)
 | 
				
			||||||
- [Getting started](#getting-started)
 | 
					- [Getting started](#getting-started)
 | 
				
			||||||
@ -35,28 +35,28 @@ A demo is available at [demo.paperless-ngx.com](https://demo.paperless-ngx.com)
 | 
				
			|||||||

 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Organize and index your scanned documents with tags, correspondents, types, and more.
 | 
					- Organize and index your scanned documents with tags, correspondents, types, and more.
 | 
				
			||||||
* Performs OCR on your documents, adds selectable text to image only documents and adds tags, correspondents and document types to your documents.
 | 
					- Performs OCR on your documents, adds selectable text to image only documents and adds tags, correspondents and document types to your documents.
 | 
				
			||||||
* Supports PDF documents, images, plain text files, and Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents).
 | 
					- Supports PDF documents, images, plain text files, and Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents).
 | 
				
			||||||
	* Office document support is optional and provided by Apache Tika (see [configuration](https://paperless-ngx.readthedocs.io/en/latest/configuration.html#tika-settings))
 | 
					  - Office document support is optional and provided by Apache Tika (see [configuration](https://paperless-ngx.readthedocs.io/en/latest/configuration.html#tika-settings))
 | 
				
			||||||
* Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely.
 | 
					- Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely.
 | 
				
			||||||
* Single page application front end.
 | 
					- Single page application front end.
 | 
				
			||||||
	* Includes a dashboard that shows basic statistics and has document upload.
 | 
					  - Includes a dashboard that shows basic statistics and has document upload.
 | 
				
			||||||
	* Filtering by tags, correspondents, types, and more.
 | 
					  - Filtering by tags, correspondents, types, and more.
 | 
				
			||||||
	* Customizable views can be saved and displayed on the dashboard.
 | 
					  - Customizable views can be saved and displayed on the dashboard.
 | 
				
			||||||
* Full text search helps you find what you need.
 | 
					- Full text search helps you find what you need.
 | 
				
			||||||
	* Auto completion suggests relevant words from your documents.
 | 
					  - Auto completion suggests relevant words from your documents.
 | 
				
			||||||
	* Results are sorted by relevance to your search query.
 | 
					  - Results are sorted by relevance to your search query.
 | 
				
			||||||
	* Highlighting shows you which parts of the document matched the query.
 | 
					  - Highlighting shows you which parts of the document matched the query.
 | 
				
			||||||
	* Searching for similar documents ("More like this")
 | 
					  - Searching for similar documents ("More like this")
 | 
				
			||||||
* Email processing: Paperless adds documents from your email accounts.
 | 
					- Email processing: Paperless adds documents from your email accounts.
 | 
				
			||||||
	* Configure multiple accounts and filters for each account.
 | 
					  - Configure multiple accounts and filters for each account.
 | 
				
			||||||
	* When adding documents from mail, paperless can move these mail to a new folder, mark them as read, flag them as important or delete them.
 | 
					  - When adding documents from mail, paperless can move these mail to a new folder, mark them as read, flag them as important or delete them.
 | 
				
			||||||
* Machine learning powered document matching.
 | 
					- Machine learning powered document matching.
 | 
				
			||||||
	* Paperless-ngx learns from your documents and will be able to automatically assign tags, correspondents and types to documents once you've stored a few documents in paperless.
 | 
					  - Paperless-ngx learns from your documents and will be able to automatically assign tags, correspondents and types to documents once you've stored a few documents in paperless.
 | 
				
			||||||
* Optimized for multi core systems: Paperless-ngx consumes multiple documents in parallel.
 | 
					- Optimized for multi core systems: Paperless-ngx consumes multiple documents in parallel.
 | 
				
			||||||
* The integrated sanity checker makes sure that your document archive is in good health.
 | 
					- The integrated sanity checker makes sure that your document archive is in good health.
 | 
				
			||||||
* [More screenshots are available in the documentation](https://paperless-ngx.readthedocs.io/en/latest/screenshots.html).
 | 
					- [More screenshots are available in the documentation](https://paperless-ngx.readthedocs.io/en/latest/screenshots.html).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Getting started
 | 
					# Getting started
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -73,6 +73,7 @@ Alternatively, you can install the dependencies and setup apache and a database
 | 
				
			|||||||
Migrating from Paperless-ng is easy, just drop in the new docker image! See the [documentation on migrating](https://paperless-ngx.readthedocs.io/en/latest/setup.html#migrating-from-paperless-ng) for more details.
 | 
					Migrating from Paperless-ng is easy, just drop in the new docker image! See the [documentation on migrating](https://paperless-ngx.readthedocs.io/en/latest/setup.html#migrating-from-paperless-ng) for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!-- omit in toc -->
 | 
					<!-- omit in toc -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Documentation
 | 
					### Documentation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The documentation for Paperless-ngx is available on [ReadTheDocs](https://paperless-ngx.readthedocs.io/).
 | 
					The documentation for Paperless-ngx is available on [ReadTheDocs](https://paperless-ngx.readthedocs.io/).
 | 
				
			||||||
@ -101,17 +102,17 @@ For bugs please [open an issue](https://github.com/paperless-ngx/paperless-ngx/i
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Paperless has been around a while now, and people are starting to build stuff on top of it. If you're one of those people, we can add your project to this list:
 | 
					Paperless has been around a while now, and people are starting to build stuff on top of it. If you're one of those people, we can add your project to this list:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* [Paperless App](https://github.com/bauerj/paperless_app): An Android/iOS app for Paperless-ngx. Also works with the original Paperless and Paperless-ngx.
 | 
					- [Paperless App](https://github.com/bauerj/paperless_app): An Android/iOS app for Paperless-ngx. Also works with the original Paperless and Paperless-ngx.
 | 
				
			||||||
* [Paperless Share](https://github.com/qcasey/paperless_share). Share any files from your Android application with paperless. Very simple, but works with all of the mobile scanning apps out there that allow you to share scanned documents.
 | 
					- [Paperless Share](https://github.com/qcasey/paperless_share). Share any files from your Android application with paperless. Very simple, but works with all of the mobile scanning apps out there that allow you to share scanned documents.
 | 
				
			||||||
* [Scan to Paperless](https://github.com/sbrunner/scan-to-paperless): Scan and prepare (crop, deskew, OCR, ...) your documents for Paperless.
 | 
					- [Scan to Paperless](https://github.com/sbrunner/scan-to-paperless): Scan and prepare (crop, deskew, OCR, ...) your documents for Paperless.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
These projects also exist, but their status and compatibility with paperless-ngx is unknown.
 | 
					These projects also exist, but their status and compatibility with paperless-ngx is unknown.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* [paperless-cli](https://github.com/stgarf/paperless-cli): A golang command line binary to interact with a Paperless instance.
 | 
					- [paperless-cli](https://github.com/stgarf/paperless-cli): A golang command line binary to interact with a Paperless instance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This project also exists, but needs updates to be compatible with paperless-ngx.
 | 
					This project also exists, but needs updates to be compatible with paperless-ngx.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): A desktop UI for your Paperless installation. Runs on Mac, Linux, and Windows.
 | 
					- [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): A desktop UI for your Paperless installation. Runs on Mac, Linux, and Windows.
 | 
				
			||||||
  Known issues on Mac: (Could not load reminders and documents)
 | 
					  Known issues on Mac: (Could not load reminders and documents)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Important Note
 | 
					# Important Note
 | 
				
			||||||
 | 
				
			|||||||
@ -2,18 +2,16 @@
 | 
				
			|||||||
// Protractor configuration file, see link for more information
 | 
					// Protractor configuration file, see link for more information
 | 
				
			||||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
 | 
					// https://github.com/angular/protractor/blob/master/lib/config.ts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
 | 
					const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @type { import("protractor").Config }
 | 
					 * @type { import("protractor").Config }
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
exports.config = {
 | 
					exports.config = {
 | 
				
			||||||
  allScriptsTimeout: 11000,
 | 
					  allScriptsTimeout: 11000,
 | 
				
			||||||
  specs: [
 | 
					  specs: ['./src/**/*.e2e-spec.ts'],
 | 
				
			||||||
    './src/**/*.e2e-spec.ts'
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  capabilities: {
 | 
					  capabilities: {
 | 
				
			||||||
    browserName: 'chrome'
 | 
					    browserName: 'chrome',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  directConnect: true,
 | 
					  directConnect: true,
 | 
				
			||||||
  baseUrl: 'http://localhost:4200/',
 | 
					  baseUrl: 'http://localhost:4200/',
 | 
				
			||||||
@ -21,16 +19,18 @@ exports.config = {
 | 
				
			|||||||
  jasmineNodeOpts: {
 | 
					  jasmineNodeOpts: {
 | 
				
			||||||
    showColors: true,
 | 
					    showColors: true,
 | 
				
			||||||
    defaultTimeoutInterval: 30000,
 | 
					    defaultTimeoutInterval: 30000,
 | 
				
			||||||
    print: function() {}
 | 
					    print: function () {},
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  onPrepare() {
 | 
					  onPrepare() {
 | 
				
			||||||
    require('ts-node').register({
 | 
					    require('ts-node').register({
 | 
				
			||||||
      project: require('path').join(__dirname, './tsconfig.json')
 | 
					      project: require('path').join(__dirname, './tsconfig.json'),
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
    jasmine.getEnv().addReporter(new SpecReporter({
 | 
					    jasmine.getEnv().addReporter(
 | 
				
			||||||
 | 
					      new SpecReporter({
 | 
				
			||||||
        spec: {
 | 
					        spec: {
 | 
				
			||||||
        displayStacktrace: StacktraceOption.PRETTY
 | 
					          displayStacktrace: StacktraceOption.PRETTY,
 | 
				
			||||||
      }
 | 
					        },
 | 
				
			||||||
    }));
 | 
					      })
 | 
				
			||||||
  }
 | 
					    )
 | 
				
			||||||
};
 | 
					  },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,23 +1,25 @@
 | 
				
			|||||||
import { AppPage } from './app.po';
 | 
					import { AppPage } from './app.po'
 | 
				
			||||||
import { browser, logging } from 'protractor';
 | 
					import { browser, logging } from 'protractor'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('workspace-project App', () => {
 | 
					describe('workspace-project App', () => {
 | 
				
			||||||
  let page: AppPage;
 | 
					  let page: AppPage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    page = new AppPage();
 | 
					    page = new AppPage()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should display welcome message', () => {
 | 
					  it('should display welcome message', () => {
 | 
				
			||||||
    page.navigateTo();
 | 
					    page.navigateTo()
 | 
				
			||||||
    expect(page.getTitleText()).toEqual('paperless-ui app is running!');
 | 
					    expect(page.getTitleText()).toEqual('paperless-ui app is running!')
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  afterEach(async () => {
 | 
					  afterEach(async () => {
 | 
				
			||||||
    // Assert that there are no errors emitted from the browser
 | 
					    // Assert that there are no errors emitted from the browser
 | 
				
			||||||
    const logs = await browser.manage().logs().get(logging.Type.BROWSER);
 | 
					    const logs = await browser.manage().logs().get(logging.Type.BROWSER)
 | 
				
			||||||
    expect(logs).not.toContain(jasmine.objectContaining({
 | 
					    expect(logs).not.toContain(
 | 
				
			||||||
 | 
					      jasmine.objectContaining({
 | 
				
			||||||
        level: logging.Level.SEVERE,
 | 
					        level: logging.Level.SEVERE,
 | 
				
			||||||
    } as logging.Entry));
 | 
					      } as logging.Entry)
 | 
				
			||||||
  });
 | 
					    )
 | 
				
			||||||
});
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,13 @@
 | 
				
			|||||||
import { browser, by, element } from 'protractor';
 | 
					import { browser, by, element } from 'protractor'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class AppPage {
 | 
					export class AppPage {
 | 
				
			||||||
  navigateTo(): Promise<unknown> {
 | 
					  navigateTo(): Promise<unknown> {
 | 
				
			||||||
    return browser.get(browser.baseUrl) as Promise<unknown>;
 | 
					    return browser.get(browser.baseUrl) as Promise<unknown>
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getTitleText(): Promise<string> {
 | 
					  getTitleText(): Promise<string> {
 | 
				
			||||||
    return element(by.css('app-root .content span')).getText() as Promise<string>;
 | 
					    return element(
 | 
				
			||||||
 | 
					      by.css('app-root .content span')
 | 
				
			||||||
 | 
					    ).getText() as Promise<string>
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -10,15 +10,15 @@ module.exports = function (config) {
 | 
				
			|||||||
      require('karma-chrome-launcher'),
 | 
					      require('karma-chrome-launcher'),
 | 
				
			||||||
      require('karma-jasmine-html-reporter'),
 | 
					      require('karma-jasmine-html-reporter'),
 | 
				
			||||||
      require('karma-coverage-istanbul-reporter'),
 | 
					      require('karma-coverage-istanbul-reporter'),
 | 
				
			||||||
      require('@angular-devkit/build-angular/plugins/karma')
 | 
					      require('@angular-devkit/build-angular/plugins/karma'),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    client: {
 | 
					    client: {
 | 
				
			||||||
      clearContext: false // leave Jasmine Spec Runner output visible in browser
 | 
					      clearContext: false, // leave Jasmine Spec Runner output visible in browser
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    coverageIstanbulReporter: {
 | 
					    coverageIstanbulReporter: {
 | 
				
			||||||
      dir: require('path').join(__dirname, './coverage/paperless-ui'),
 | 
					      dir: require('path').join(__dirname, './coverage/paperless-ui'),
 | 
				
			||||||
      reports: ['html', 'lcovonly', 'text-summary'],
 | 
					      reports: ['html', 'lcovonly', 'text-summary'],
 | 
				
			||||||
      fixWebpackSourcePaths: true
 | 
					      fixWebpackSourcePaths: true,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    reporters: ['progress', 'kjhtml'],
 | 
					    reporters: ['progress', 'kjhtml'],
 | 
				
			||||||
    port: 9876,
 | 
					    port: 9876,
 | 
				
			||||||
@ -27,6 +27,6 @@ module.exports = function (config) {
 | 
				
			|||||||
    autoWatch: true,
 | 
					    autoWatch: true,
 | 
				
			||||||
    browsers: ['Chrome'],
 | 
					    browsers: ['Chrome'],
 | 
				
			||||||
    singleRun: false,
 | 
					    singleRun: false,
 | 
				
			||||||
    restartOnFileChange: true
 | 
					    restartOnFileChange: true,
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,39 +1,47 @@
 | 
				
			|||||||
import { NgModule } from '@angular/core';
 | 
					import { NgModule } from '@angular/core'
 | 
				
			||||||
import { Routes, RouterModule } from '@angular/router';
 | 
					import { Routes, RouterModule } from '@angular/router'
 | 
				
			||||||
import { AppFrameComponent } from './components/app-frame/app-frame.component';
 | 
					import { AppFrameComponent } from './components/app-frame/app-frame.component'
 | 
				
			||||||
import { DashboardComponent } from './components/dashboard/dashboard.component';
 | 
					import { DashboardComponent } from './components/dashboard/dashboard.component'
 | 
				
			||||||
import { DocumentDetailComponent } from './components/document-detail/document-detail.component';
 | 
					import { DocumentDetailComponent } from './components/document-detail/document-detail.component'
 | 
				
			||||||
import { DocumentListComponent } from './components/document-list/document-list.component';
 | 
					import { DocumentListComponent } from './components/document-list/document-list.component'
 | 
				
			||||||
import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component';
 | 
					import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component'
 | 
				
			||||||
import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component';
 | 
					import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component'
 | 
				
			||||||
import { LogsComponent } from './components/manage/logs/logs.component';
 | 
					import { LogsComponent } from './components/manage/logs/logs.component'
 | 
				
			||||||
import { SettingsComponent } from './components/manage/settings/settings.component';
 | 
					import { SettingsComponent } from './components/manage/settings/settings.component'
 | 
				
			||||||
import { TagListComponent } from './components/manage/tag-list/tag-list.component';
 | 
					import { TagListComponent } from './components/manage/tag-list/tag-list.component'
 | 
				
			||||||
import { NotFoundComponent } from './components/not-found/not-found.component';
 | 
					import { NotFoundComponent } from './components/not-found/not-found.component'
 | 
				
			||||||
import {DocumentAsnComponent} from "./components/document-asn/document-asn.component";
 | 
					import { DocumentAsnComponent } from './components/document-asn/document-asn.component'
 | 
				
			||||||
import { DirtyFormGuard } from './guards/dirty-form.guard';
 | 
					import { DirtyFormGuard } from './guards/dirty-form.guard'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const routes: Routes = [
 | 
					const routes: Routes = [
 | 
				
			||||||
  {path: '', redirectTo: 'dashboard', pathMatch: 'full'},
 | 
					  { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
 | 
				
			||||||
  {path: '', component: AppFrameComponent, children: [
 | 
					  {
 | 
				
			||||||
    {path: 'dashboard', component: DashboardComponent },
 | 
					    path: '',
 | 
				
			||||||
    {path: 'documents', component: DocumentListComponent },
 | 
					    component: AppFrameComponent,
 | 
				
			||||||
    {path: 'view/:id', component: DocumentListComponent },
 | 
					    children: [
 | 
				
			||||||
    {path: 'documents/:id', component: DocumentDetailComponent },
 | 
					      { path: 'dashboard', component: DashboardComponent },
 | 
				
			||||||
    {path: 'asn/:id', component: DocumentAsnComponent },
 | 
					      { path: 'documents', component: DocumentListComponent },
 | 
				
			||||||
    {path: 'tags', component: TagListComponent },
 | 
					      { path: 'view/:id', component: DocumentListComponent },
 | 
				
			||||||
    {path: 'documenttypes', component: DocumentTypeListComponent },
 | 
					      { path: 'documents/:id', component: DocumentDetailComponent },
 | 
				
			||||||
    {path: 'correspondents', component: CorrespondentListComponent },
 | 
					      { path: 'asn/:id', component: DocumentAsnComponent },
 | 
				
			||||||
    {path: 'logs', component: LogsComponent },
 | 
					      { path: 'tags', component: TagListComponent },
 | 
				
			||||||
    {path: 'settings', component: SettingsComponent, canDeactivate: [DirtyFormGuard] },
 | 
					      { path: 'documenttypes', component: DocumentTypeListComponent },
 | 
				
			||||||
  ]},
 | 
					      { path: 'correspondents', component: CorrespondentListComponent },
 | 
				
			||||||
 | 
					      { path: 'logs', component: LogsComponent },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        path: 'settings',
 | 
				
			||||||
 | 
					        component: SettingsComponent,
 | 
				
			||||||
 | 
					        canDeactivate: [DirtyFormGuard],
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {path: '404', component: NotFoundComponent},
 | 
					  { path: '404', component: NotFoundComponent },
 | 
				
			||||||
  {path: '**', redirectTo: '/404', pathMatch: 'full'}
 | 
					  { path: '**', redirectTo: '/404', pathMatch: 'full' },
 | 
				
			||||||
];
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
  imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
 | 
					  imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
 | 
				
			||||||
  exports: [RouterModule]
 | 
					  exports: [RouterModule],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AppRoutingModule { }
 | 
					export class AppRoutingModule {}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,35 +1,33 @@
 | 
				
			|||||||
import { TestBed } from '@angular/core/testing';
 | 
					import { TestBed } from '@angular/core/testing'
 | 
				
			||||||
import { RouterTestingModule } from '@angular/router/testing';
 | 
					import { RouterTestingModule } from '@angular/router/testing'
 | 
				
			||||||
import { AppComponent } from './app.component';
 | 
					import { AppComponent } from './app.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('AppComponent', () => {
 | 
					describe('AppComponent', () => {
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      imports: [
 | 
					      imports: [RouterTestingModule],
 | 
				
			||||||
        RouterTestingModule
 | 
					      declarations: [AppComponent],
 | 
				
			||||||
      ],
 | 
					    }).compileComponents()
 | 
				
			||||||
      declarations: [
 | 
					  })
 | 
				
			||||||
        AppComponent
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
    }).compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create the app', () => {
 | 
					  it('should create the app', () => {
 | 
				
			||||||
    const fixture = TestBed.createComponent(AppComponent);
 | 
					    const fixture = TestBed.createComponent(AppComponent)
 | 
				
			||||||
    const app = fixture.componentInstance;
 | 
					    const app = fixture.componentInstance
 | 
				
			||||||
    expect(app).toBeTruthy();
 | 
					    expect(app).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it(`should have as title 'paperless-ui'`, () => {
 | 
					  it(`should have as title 'paperless-ui'`, () => {
 | 
				
			||||||
    const fixture = TestBed.createComponent(AppComponent);
 | 
					    const fixture = TestBed.createComponent(AppComponent)
 | 
				
			||||||
    const app = fixture.componentInstance;
 | 
					    const app = fixture.componentInstance
 | 
				
			||||||
    expect(app.title).toEqual('paperless-ui');
 | 
					    expect(app.title).toEqual('paperless-ui')
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should render title', () => {
 | 
					  it('should render title', () => {
 | 
				
			||||||
    const fixture = TestBed.createComponent(AppComponent);
 | 
					    const fixture = TestBed.createComponent(AppComponent)
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
    const compiled = fixture.nativeElement;
 | 
					    const compiled = fixture.nativeElement
 | 
				
			||||||
    expect(compiled.querySelector('.content span').textContent).toContain('paperless-ui app is running!');
 | 
					    expect(compiled.querySelector('.content span').textContent).toContain(
 | 
				
			||||||
  });
 | 
					      'paperless-ui app is running!'
 | 
				
			||||||
});
 | 
					    )
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,24 +1,28 @@
 | 
				
			|||||||
import { SettingsService, SETTINGS_KEYS } from './services/settings.service';
 | 
					import { SettingsService, SETTINGS_KEYS } from './services/settings.service'
 | 
				
			||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
 | 
					import { Component, OnDestroy, OnInit } from '@angular/core'
 | 
				
			||||||
import { Router } from '@angular/router';
 | 
					import { Router } from '@angular/router'
 | 
				
			||||||
import { Subscription } from 'rxjs';
 | 
					import { Subscription } from 'rxjs'
 | 
				
			||||||
import { ConsumerStatusService } from './services/consumer-status.service';
 | 
					import { ConsumerStatusService } from './services/consumer-status.service'
 | 
				
			||||||
import { ToastService } from './services/toast.service';
 | 
					import { ToastService } from './services/toast.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-root',
 | 
					  selector: 'app-root',
 | 
				
			||||||
  templateUrl: './app.component.html',
 | 
					  templateUrl: './app.component.html',
 | 
				
			||||||
  styleUrls: ['./app.component.scss']
 | 
					  styleUrls: ['./app.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AppComponent implements OnInit, OnDestroy {
 | 
					export class AppComponent implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					  newDocumentSubscription: Subscription
 | 
				
			||||||
 | 
					  successSubscription: Subscription
 | 
				
			||||||
 | 
					  failedSubscription: Subscription
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  newDocumentSubscription: Subscription;
 | 
					  constructor(
 | 
				
			||||||
  successSubscription: Subscription;
 | 
					    private settings: SettingsService,
 | 
				
			||||||
  failedSubscription: Subscription;
 | 
					    private consumerStatusService: ConsumerStatusService,
 | 
				
			||||||
 | 
					    private toastService: ToastService,
 | 
				
			||||||
  constructor (private settings: SettingsService, private consumerStatusService: ConsumerStatusService, private toastService: ToastService, private router: Router) {
 | 
					    private router: Router
 | 
				
			||||||
    let anyWindow = (window as any)
 | 
					  ) {
 | 
				
			||||||
    anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.js';
 | 
					    let anyWindow = window as any
 | 
				
			||||||
 | 
					    anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.js'
 | 
				
			||||||
    this.settings.updateAppearanceSettings()
 | 
					    this.settings.updateAppearanceSettings()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -36,7 +40,12 @@ export class AppComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private showNotification(key) {
 | 
					  private showNotification(key) {
 | 
				
			||||||
    if (this.router.url == '/dashboard' && this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD)) {
 | 
					    if (
 | 
				
			||||||
 | 
					      this.router.url == '/dashboard' &&
 | 
				
			||||||
 | 
					      this.settings.get(
 | 
				
			||||||
 | 
					        SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
      return false
 | 
					      return false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return this.settings.get(key)
 | 
					    return this.settings.get(key)
 | 
				
			||||||
@ -45,26 +54,50 @@ export class AppComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.consumerStatusService.connect()
 | 
					    this.consumerStatusService.connect()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.successSubscription = this.consumerStatusService
 | 
				
			||||||
    this.successSubscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
 | 
					      .onDocumentConsumptionFinished()
 | 
				
			||||||
      if (this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS)) {
 | 
					      .subscribe((status) => {
 | 
				
			||||||
        this.toastService.show({title: $localize`Document added`, delay: 10000, content: $localize`Document ${status.filename} was added to paperless.`, actionName: $localize`Open document`, action: () => {
 | 
					        if (
 | 
				
			||||||
 | 
					          this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS)
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          this.toastService.show({
 | 
				
			||||||
 | 
					            title: $localize`Document added`,
 | 
				
			||||||
 | 
					            delay: 10000,
 | 
				
			||||||
 | 
					            content: $localize`Document ${status.filename} was added to paperless.`,
 | 
				
			||||||
 | 
					            actionName: $localize`Open document`,
 | 
				
			||||||
 | 
					            action: () => {
 | 
				
			||||||
              this.router.navigate(['documents', status.documentId])
 | 
					              this.router.navigate(['documents', status.documentId])
 | 
				
			||||||
        }})
 | 
					            },
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.failedSubscription = this.consumerStatusService.onDocumentConsumptionFailed().subscribe(status => {
 | 
					    this.failedSubscription = this.consumerStatusService
 | 
				
			||||||
      if (this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED)) {
 | 
					      .onDocumentConsumptionFailed()
 | 
				
			||||||
        this.toastService.showError($localize`Could not add ${status.filename}\: ${status.message}`)
 | 
					      .subscribe((status) => {
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED)
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          this.toastService.showError(
 | 
				
			||||||
 | 
					            $localize`Could not add ${status.filename}\: ${status.message}`
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.newDocumentSubscription = this.consumerStatusService.onDocumentDetected().subscribe(status => {
 | 
					    this.newDocumentSubscription = this.consumerStatusService
 | 
				
			||||||
      if (this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT)) {
 | 
					      .onDocumentDetected()
 | 
				
			||||||
        this.toastService.show({title: $localize`New document detected`, delay: 5000, content: $localize`Document ${status.filename} is being processed by paperless.`})
 | 
					      .subscribe((status) => {
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          this.showNotification(
 | 
				
			||||||
 | 
					            SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          this.toastService.show({
 | 
				
			||||||
 | 
					            title: $localize`New document detected`,
 | 
				
			||||||
 | 
					            delay: 5000,
 | 
				
			||||||
 | 
					            content: $localize`Document ${status.filename} is being processed by paperless.`,
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,85 +1,88 @@
 | 
				
			|||||||
import { BrowserModule } from '@angular/platform-browser';
 | 
					import { BrowserModule } from '@angular/platform-browser'
 | 
				
			||||||
import { NgModule } from '@angular/core';
 | 
					import { NgModule } from '@angular/core'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { AppRoutingModule } from './app-routing.module';
 | 
					import { AppRoutingModule } from './app-routing.module'
 | 
				
			||||||
import { AppComponent } from './app.component';
 | 
					import { AppComponent } from './app.component'
 | 
				
			||||||
import { NgbDateAdapter, NgbDateParserFormatter, NgbModule } from '@ng-bootstrap/ng-bootstrap';
 | 
					import {
 | 
				
			||||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
 | 
					  NgbDateAdapter,
 | 
				
			||||||
import { DocumentListComponent } from './components/document-list/document-list.component';
 | 
					  NgbDateParserFormatter,
 | 
				
			||||||
import { DocumentDetailComponent } from './components/document-detail/document-detail.component';
 | 
					  NgbModule,
 | 
				
			||||||
import { DashboardComponent } from './components/dashboard/dashboard.component';
 | 
					} from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { TagListComponent } from './components/manage/tag-list/tag-list.component';
 | 
					import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'
 | 
				
			||||||
import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component';
 | 
					import { DocumentListComponent } from './components/document-list/document-list.component'
 | 
				
			||||||
import { LogsComponent } from './components/manage/logs/logs.component';
 | 
					import { DocumentDetailComponent } from './components/document-detail/document-detail.component'
 | 
				
			||||||
import { SettingsComponent } from './components/manage/settings/settings.component';
 | 
					import { DashboardComponent } from './components/dashboard/dashboard.component'
 | 
				
			||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 | 
					import { TagListComponent } from './components/manage/tag-list/tag-list.component'
 | 
				
			||||||
import { DatePipe, registerLocaleData } from '@angular/common';
 | 
					import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component'
 | 
				
			||||||
import { NotFoundComponent } from './components/not-found/not-found.component';
 | 
					import { LogsComponent } from './components/manage/logs/logs.component'
 | 
				
			||||||
import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component';
 | 
					import { SettingsComponent } from './components/manage/settings/settings.component'
 | 
				
			||||||
import { ConfirmDialogComponent } from './components/common/confirm-dialog/confirm-dialog.component';
 | 
					import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
				
			||||||
import { CorrespondentEditDialogComponent } from './components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component';
 | 
					import { DatePipe, registerLocaleData } from '@angular/common'
 | 
				
			||||||
import { TagEditDialogComponent } from './components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component';
 | 
					import { NotFoundComponent } from './components/not-found/not-found.component'
 | 
				
			||||||
import { DocumentTypeEditDialogComponent } from './components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component';
 | 
					import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component'
 | 
				
			||||||
import { TagComponent } from './components/common/tag/tag.component';
 | 
					import { ConfirmDialogComponent } from './components/common/confirm-dialog/confirm-dialog.component'
 | 
				
			||||||
import { PageHeaderComponent } from './components/common/page-header/page-header.component';
 | 
					import { CorrespondentEditDialogComponent } from './components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'
 | 
				
			||||||
import { AppFrameComponent } from './components/app-frame/app-frame.component';
 | 
					import { TagEditDialogComponent } from './components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component'
 | 
				
			||||||
import { ToastsComponent } from './components/common/toasts/toasts.component';
 | 
					import { DocumentTypeEditDialogComponent } from './components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'
 | 
				
			||||||
import { FilterEditorComponent } from './components/document-list/filter-editor/filter-editor.component';
 | 
					import { TagComponent } from './components/common/tag/tag.component'
 | 
				
			||||||
import { FilterableDropdownComponent } from './components/common/filterable-dropdown/filterable-dropdown.component';
 | 
					import { PageHeaderComponent } from './components/common/page-header/page-header.component'
 | 
				
			||||||
import { ToggleableDropdownButtonComponent } from './components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component';
 | 
					import { AppFrameComponent } from './components/app-frame/app-frame.component'
 | 
				
			||||||
import { DateDropdownComponent } from './components/common/date-dropdown/date-dropdown.component';
 | 
					import { ToastsComponent } from './components/common/toasts/toasts.component'
 | 
				
			||||||
import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component';
 | 
					import { FilterEditorComponent } from './components/document-list/filter-editor/filter-editor.component'
 | 
				
			||||||
import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component';
 | 
					import { FilterableDropdownComponent } from './components/common/filterable-dropdown/filterable-dropdown.component'
 | 
				
			||||||
import { BulkEditorComponent } from './components/document-list/bulk-editor/bulk-editor.component';
 | 
					import { ToggleableDropdownButtonComponent } from './components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
 | 
				
			||||||
import { NgxFileDropModule } from 'ngx-file-drop';
 | 
					import { DateDropdownComponent } from './components/common/date-dropdown/date-dropdown.component'
 | 
				
			||||||
import { TextComponent } from './components/common/input/text/text.component';
 | 
					import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component'
 | 
				
			||||||
import { SelectComponent } from './components/common/input/select/select.component';
 | 
					import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component'
 | 
				
			||||||
import { CheckComponent } from './components/common/input/check/check.component';
 | 
					import { BulkEditorComponent } from './components/document-list/bulk-editor/bulk-editor.component'
 | 
				
			||||||
import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component';
 | 
					import { NgxFileDropModule } from 'ngx-file-drop'
 | 
				
			||||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
 | 
					import { TextComponent } from './components/common/input/text/text.component'
 | 
				
			||||||
import { TagsComponent } from './components/common/input/tags/tags.component';
 | 
					import { SelectComponent } from './components/common/input/select/select.component'
 | 
				
			||||||
import { SortableDirective } from './directives/sortable.directive';
 | 
					import { CheckComponent } from './components/common/input/check/check.component'
 | 
				
			||||||
import { CookieService } from 'ngx-cookie-service';
 | 
					import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component'
 | 
				
			||||||
import { CsrfInterceptor } from './interceptors/csrf.interceptor';
 | 
					import { InfiniteScrollModule } from 'ngx-infinite-scroll'
 | 
				
			||||||
import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-view-widget/saved-view-widget.component';
 | 
					import { TagsComponent } from './components/common/input/tags/tags.component'
 | 
				
			||||||
import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component';
 | 
					import { SortableDirective } from './directives/sortable.directive'
 | 
				
			||||||
import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component';
 | 
					import { CookieService } from 'ngx-cookie-service'
 | 
				
			||||||
import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component';
 | 
					import { CsrfInterceptor } from './interceptors/csrf.interceptor'
 | 
				
			||||||
import { PdfViewerModule } from 'ng2-pdf-viewer';
 | 
					import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-view-widget/saved-view-widget.component'
 | 
				
			||||||
import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component';
 | 
					import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component'
 | 
				
			||||||
import { YesNoPipe } from './pipes/yes-no.pipe';
 | 
					import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component'
 | 
				
			||||||
import { FileSizePipe } from './pipes/file-size.pipe';
 | 
					import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component'
 | 
				
			||||||
import { FilterPipe } from './pipes/filter.pipe';
 | 
					import { PdfViewerModule } from 'ng2-pdf-viewer'
 | 
				
			||||||
import { DocumentTitlePipe } from './pipes/document-title.pipe';
 | 
					import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component'
 | 
				
			||||||
import { MetadataCollapseComponent } from './components/document-detail/metadata-collapse/metadata-collapse.component';
 | 
					import { YesNoPipe } from './pipes/yes-no.pipe'
 | 
				
			||||||
import { SelectDialogComponent } from './components/common/select-dialog/select-dialog.component';
 | 
					import { FileSizePipe } from './pipes/file-size.pipe'
 | 
				
			||||||
import { NgSelectModule } from '@ng-select/ng-select';
 | 
					import { FilterPipe } from './pipes/filter.pipe'
 | 
				
			||||||
import { NumberComponent } from './components/common/input/number/number.component';
 | 
					import { DocumentTitlePipe } from './pipes/document-title.pipe'
 | 
				
			||||||
import { SafePipe } from './pipes/safe.pipe';
 | 
					import { MetadataCollapseComponent } from './components/document-detail/metadata-collapse/metadata-collapse.component'
 | 
				
			||||||
import { CustomDatePipe } from './pipes/custom-date.pipe';
 | 
					import { SelectDialogComponent } from './components/common/select-dialog/select-dialog.component'
 | 
				
			||||||
import { DateComponent } from './components/common/input/date/date.component';
 | 
					import { NgSelectModule } from '@ng-select/ng-select'
 | 
				
			||||||
import { ISODateTimeAdapter } from './utils/ngb-iso-date-time-adapter';
 | 
					import { NumberComponent } from './components/common/input/number/number.component'
 | 
				
			||||||
import { LocalizedDateParserFormatter } from './utils/ngb-date-parser-formatter';
 | 
					import { SafePipe } from './pipes/safe.pipe'
 | 
				
			||||||
import { ApiVersionInterceptor } from './interceptors/api-version.interceptor';
 | 
					import { CustomDatePipe } from './pipes/custom-date.pipe'
 | 
				
			||||||
import { ColorSliderModule } from 'ngx-color/slider';
 | 
					import { DateComponent } from './components/common/input/date/date.component'
 | 
				
			||||||
import { ColorComponent } from './components/common/input/color/color.component';
 | 
					import { ISODateTimeAdapter } from './utils/ngb-iso-date-time-adapter'
 | 
				
			||||||
import { DocumentAsnComponent } from './components/document-asn/document-asn.component';
 | 
					import { LocalizedDateParserFormatter } from './utils/ngb-date-parser-formatter'
 | 
				
			||||||
 | 
					import { ApiVersionInterceptor } from './interceptors/api-version.interceptor'
 | 
				
			||||||
import localeCs from '@angular/common/locales/cs';
 | 
					import { ColorSliderModule } from 'ngx-color/slider'
 | 
				
			||||||
import localeDa from '@angular/common/locales/da';
 | 
					import { ColorComponent } from './components/common/input/color/color.component'
 | 
				
			||||||
import localeDe from '@angular/common/locales/de';
 | 
					import { DocumentAsnComponent } from './components/document-asn/document-asn.component'
 | 
				
			||||||
import localeEnGb from '@angular/common/locales/en-GB';
 | 
					 | 
				
			||||||
import localeEs from '@angular/common/locales/es';
 | 
					 | 
				
			||||||
import localeFr from '@angular/common/locales/fr';
 | 
					 | 
				
			||||||
import localeIt from '@angular/common/locales/it';
 | 
					 | 
				
			||||||
import localeLb from '@angular/common/locales/lb';
 | 
					 | 
				
			||||||
import localeNl from '@angular/common/locales/nl';
 | 
					 | 
				
			||||||
import localePl from '@angular/common/locales/pl';
 | 
					 | 
				
			||||||
import localePt from '@angular/common/locales/pt';
 | 
					 | 
				
			||||||
import localeSv from '@angular/common/locales/sv';
 | 
					 | 
				
			||||||
import localeRo from '@angular/common/locales/ro';
 | 
					 | 
				
			||||||
import localeRu from '@angular/common/locales/ru';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import localeCs from '@angular/common/locales/cs'
 | 
				
			||||||
 | 
					import localeDa from '@angular/common/locales/da'
 | 
				
			||||||
 | 
					import localeDe from '@angular/common/locales/de'
 | 
				
			||||||
 | 
					import localeEnGb from '@angular/common/locales/en-GB'
 | 
				
			||||||
 | 
					import localeEs from '@angular/common/locales/es'
 | 
				
			||||||
 | 
					import localeFr from '@angular/common/locales/fr'
 | 
				
			||||||
 | 
					import localeIt from '@angular/common/locales/it'
 | 
				
			||||||
 | 
					import localeLb from '@angular/common/locales/lb'
 | 
				
			||||||
 | 
					import localeNl from '@angular/common/locales/nl'
 | 
				
			||||||
 | 
					import localePl from '@angular/common/locales/pl'
 | 
				
			||||||
 | 
					import localePt from '@angular/common/locales/pt'
 | 
				
			||||||
 | 
					import localeSv from '@angular/common/locales/sv'
 | 
				
			||||||
 | 
					import localeRo from '@angular/common/locales/ro'
 | 
				
			||||||
 | 
					import localeRu from '@angular/common/locales/ru'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
registerLocaleData(localeCs)
 | 
					registerLocaleData(localeCs)
 | 
				
			||||||
registerLocaleData(localeDa)
 | 
					registerLocaleData(localeDa)
 | 
				
			||||||
@ -91,8 +94,8 @@ registerLocaleData(localeIt)
 | 
				
			|||||||
registerLocaleData(localeLb)
 | 
					registerLocaleData(localeLb)
 | 
				
			||||||
registerLocaleData(localeNl)
 | 
					registerLocaleData(localeNl)
 | 
				
			||||||
registerLocaleData(localePl)
 | 
					registerLocaleData(localePl)
 | 
				
			||||||
registerLocaleData(localePt, "pt-BR")
 | 
					registerLocaleData(localePt, 'pt-BR')
 | 
				
			||||||
registerLocaleData(localePt, "pt-PT")
 | 
					registerLocaleData(localePt, 'pt-PT')
 | 
				
			||||||
registerLocaleData(localeRo)
 | 
					registerLocaleData(localeRo)
 | 
				
			||||||
registerLocaleData(localeRu)
 | 
					registerLocaleData(localeRu)
 | 
				
			||||||
registerLocaleData(localeSv)
 | 
					registerLocaleData(localeSv)
 | 
				
			||||||
@ -146,7 +149,7 @@ registerLocaleData(localeSv)
 | 
				
			|||||||
    CustomDatePipe,
 | 
					    CustomDatePipe,
 | 
				
			||||||
    DateComponent,
 | 
					    DateComponent,
 | 
				
			||||||
    ColorComponent,
 | 
					    ColorComponent,
 | 
				
			||||||
    DocumentAsnComponent
 | 
					    DocumentAsnComponent,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  imports: [
 | 
					  imports: [
 | 
				
			||||||
    BrowserModule,
 | 
					    BrowserModule,
 | 
				
			||||||
@ -159,24 +162,26 @@ registerLocaleData(localeSv)
 | 
				
			|||||||
    InfiniteScrollModule,
 | 
					    InfiniteScrollModule,
 | 
				
			||||||
    PdfViewerModule,
 | 
					    PdfViewerModule,
 | 
				
			||||||
    NgSelectModule,
 | 
					    NgSelectModule,
 | 
				
			||||||
    ColorSliderModule
 | 
					    ColorSliderModule,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  providers: [
 | 
					  providers: [
 | 
				
			||||||
    DatePipe,
 | 
					    DatePipe,
 | 
				
			||||||
    CookieService, {
 | 
					    CookieService,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      provide: HTTP_INTERCEPTORS,
 | 
					      provide: HTTP_INTERCEPTORS,
 | 
				
			||||||
      useClass: CsrfInterceptor,
 | 
					      useClass: CsrfInterceptor,
 | 
				
			||||||
      multi: true
 | 
					      multi: true,
 | 
				
			||||||
    },{
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      provide: HTTP_INTERCEPTORS,
 | 
					      provide: HTTP_INTERCEPTORS,
 | 
				
			||||||
      useClass: ApiVersionInterceptor,
 | 
					      useClass: ApiVersionInterceptor,
 | 
				
			||||||
      multi: true
 | 
					      multi: true,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    FilterPipe,
 | 
					    FilterPipe,
 | 
				
			||||||
    DocumentTitlePipe,
 | 
					    DocumentTitlePipe,
 | 
				
			||||||
    {provide: NgbDateAdapter, useClass: ISODateTimeAdapter},
 | 
					    { provide: NgbDateAdapter, useClass: ISODateTimeAdapter },
 | 
				
			||||||
    {provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter}
 | 
					    { provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter },
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  bootstrap: [AppComponent]
 | 
					  bootstrap: [AppComponent],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AppModule { }
 | 
					export class AppModule {}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { AppFrameComponent } from './app-frame.component';
 | 
					import { AppFrameComponent } from './app-frame.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('AppFrameComponent', () => {
 | 
					describe('AppFrameComponent', () => {
 | 
				
			||||||
  let component: AppFrameComponent;
 | 
					  let component: AppFrameComponent
 | 
				
			||||||
  let fixture: ComponentFixture<AppFrameComponent>;
 | 
					  let fixture: ComponentFixture<AppFrameComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ AppFrameComponent ]
 | 
					      declarations: [AppFrameComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(AppFrameComponent);
 | 
					    fixture = TestBed.createComponent(AppFrameComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,26 +1,31 @@
 | 
				
			|||||||
import { Component } from '@angular/core';
 | 
					import { Component } from '@angular/core'
 | 
				
			||||||
import { FormControl } from '@angular/forms';
 | 
					import { FormControl } from '@angular/forms'
 | 
				
			||||||
import { ActivatedRoute, Router, Params } from '@angular/router';
 | 
					import { ActivatedRoute, Router, Params } from '@angular/router'
 | 
				
			||||||
import { from, Observable, Subscription, BehaviorSubject } from 'rxjs';
 | 
					import { from, Observable, Subscription, BehaviorSubject } from 'rxjs'
 | 
				
			||||||
import { debounceTime, distinctUntilChanged, map, switchMap, first } from 'rxjs/operators';
 | 
					import {
 | 
				
			||||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
 | 
					  debounceTime,
 | 
				
			||||||
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
 | 
					  distinctUntilChanged,
 | 
				
			||||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
 | 
					  map,
 | 
				
			||||||
import { SearchService } from 'src/app/services/rest/search.service';
 | 
					  switchMap,
 | 
				
			||||||
import { environment } from 'src/environments/environment';
 | 
					  first,
 | 
				
			||||||
import { DocumentDetailComponent } from '../document-detail/document-detail.component';
 | 
					} from 'rxjs/operators'
 | 
				
			||||||
import { Meta } from '@angular/platform-browser';
 | 
					import { PaperlessDocument } from 'src/app/data/paperless-document'
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
 | 
					import { OpenDocumentsService } from 'src/app/services/open-documents.service'
 | 
				
			||||||
import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type';
 | 
					import { SavedViewService } from 'src/app/services/rest/saved-view.service'
 | 
				
			||||||
 | 
					import { SearchService } from 'src/app/services/rest/search.service'
 | 
				
			||||||
 | 
					import { environment } from 'src/environments/environment'
 | 
				
			||||||
 | 
					import { DocumentDetailComponent } from '../document-detail/document-detail.component'
 | 
				
			||||||
 | 
					import { Meta } from '@angular/platform-browser'
 | 
				
			||||||
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
 | 
					import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-app-frame',
 | 
					  selector: 'app-app-frame',
 | 
				
			||||||
  templateUrl: './app-frame.component.html',
 | 
					  templateUrl: './app-frame.component.html',
 | 
				
			||||||
  styleUrls: ['./app-frame.component.scss']
 | 
					  styleUrls: ['./app-frame.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AppFrameComponent {
 | 
					export class AppFrameComponent {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
  constructor (
 | 
					 | 
				
			||||||
    public router: Router,
 | 
					    public router: Router,
 | 
				
			||||||
    private activatedRoute: ActivatedRoute,
 | 
					    private activatedRoute: ActivatedRoute,
 | 
				
			||||||
    private openDocumentsService: OpenDocumentsService,
 | 
					    private openDocumentsService: OpenDocumentsService,
 | 
				
			||||||
@ -28,7 +33,7 @@ export class AppFrameComponent {
 | 
				
			|||||||
    public savedViewService: SavedViewService,
 | 
					    public savedViewService: SavedViewService,
 | 
				
			||||||
    private list: DocumentListViewService,
 | 
					    private list: DocumentListViewService,
 | 
				
			||||||
    private meta: Meta
 | 
					    private meta: Meta
 | 
				
			||||||
    ) { }
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  versionString = `${environment.appTitle} ${environment.version}`
 | 
					  versionString = `${environment.appTitle} ${environment.version}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -48,14 +53,14 @@ export class AppFrameComponent {
 | 
				
			|||||||
    text$.pipe(
 | 
					    text$.pipe(
 | 
				
			||||||
      debounceTime(200),
 | 
					      debounceTime(200),
 | 
				
			||||||
      distinctUntilChanged(),
 | 
					      distinctUntilChanged(),
 | 
				
			||||||
      map(term => {
 | 
					      map((term) => {
 | 
				
			||||||
        if (term.lastIndexOf(' ') != -1) {
 | 
					        if (term.lastIndexOf(' ') != -1) {
 | 
				
			||||||
          return term.substring(term.lastIndexOf(' ') + 1)
 | 
					          return term.substring(term.lastIndexOf(' ') + 1)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          return term
 | 
					          return term
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
      switchMap(term =>
 | 
					      switchMap((term) =>
 | 
				
			||||||
        term.length < 2 ? from([[]]) : this.searchService.autocomplete(term)
 | 
					        term.length < 2 ? from([[]]) : this.searchService.autocomplete(term)
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@ -66,28 +71,36 @@ export class AppFrameComponent {
 | 
				
			|||||||
    let lastSpaceIndex = currentSearch.lastIndexOf(' ')
 | 
					    let lastSpaceIndex = currentSearch.lastIndexOf(' ')
 | 
				
			||||||
    if (lastSpaceIndex != -1) {
 | 
					    if (lastSpaceIndex != -1) {
 | 
				
			||||||
      currentSearch = currentSearch.substring(0, lastSpaceIndex + 1)
 | 
					      currentSearch = currentSearch.substring(0, lastSpaceIndex + 1)
 | 
				
			||||||
      currentSearch += event.item + " "
 | 
					      currentSearch += event.item + ' '
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      currentSearch = event.item + " "
 | 
					      currentSearch = event.item + ' '
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.searchField.patchValue(currentSearch)
 | 
					    this.searchField.patchValue(currentSearch)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  search() {
 | 
					  search() {
 | 
				
			||||||
    this.closeMenu()
 | 
					    this.closeMenu()
 | 
				
			||||||
    this.list.quickFilter([{rule_type: FILTER_FULLTEXT_QUERY, value: this.searchField.value}])
 | 
					    this.list.quickFilter([
 | 
				
			||||||
 | 
					      { rule_type: FILTER_FULLTEXT_QUERY, value: this.searchField.value },
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  closeDocument(d: PaperlessDocument) {
 | 
					  closeDocument(d: PaperlessDocument) {
 | 
				
			||||||
    this.openDocumentsService.closeDocument(d).pipe(first()).subscribe(confirmed => {
 | 
					    this.openDocumentsService
 | 
				
			||||||
 | 
					      .closeDocument(d)
 | 
				
			||||||
 | 
					      .pipe(first())
 | 
				
			||||||
 | 
					      .subscribe((confirmed) => {
 | 
				
			||||||
        if (confirmed) {
 | 
					        if (confirmed) {
 | 
				
			||||||
          this.closeMenu()
 | 
					          this.closeMenu()
 | 
				
			||||||
          let route = this.activatedRoute.snapshot
 | 
					          let route = this.activatedRoute.snapshot
 | 
				
			||||||
          while (route.firstChild) {
 | 
					          while (route.firstChild) {
 | 
				
			||||||
            route = route.firstChild
 | 
					            route = route.firstChild
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        if (route.component == DocumentDetailComponent && route.params['id'] == d.id) {
 | 
					          if (
 | 
				
			||||||
          this.router.navigate([""])
 | 
					            route.component == DocumentDetailComponent &&
 | 
				
			||||||
 | 
					            route.params['id'] == d.id
 | 
				
			||||||
 | 
					          ) {
 | 
				
			||||||
 | 
					            this.router.navigate([''])
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
@ -95,7 +108,10 @@ export class AppFrameComponent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  closeAll() {
 | 
					  closeAll() {
 | 
				
			||||||
    // user may need to confirm losing unsaved changes
 | 
					    // user may need to confirm losing unsaved changes
 | 
				
			||||||
    this.openDocumentsService.closeAll().pipe(first()).subscribe(confirmed => {
 | 
					    this.openDocumentsService
 | 
				
			||||||
 | 
					      .closeAll()
 | 
				
			||||||
 | 
					      .pipe(first())
 | 
				
			||||||
 | 
					      .subscribe((confirmed) => {
 | 
				
			||||||
        if (confirmed) {
 | 
					        if (confirmed) {
 | 
				
			||||||
          this.closeMenu()
 | 
					          this.closeMenu()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -105,7 +121,7 @@ export class AppFrameComponent {
 | 
				
			|||||||
            route = route.firstChild
 | 
					            route = route.firstChild
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (route.component === DocumentDetailComponent) {
 | 
					          if (route.component === DocumentDetailComponent) {
 | 
				
			||||||
          this.router.navigate([""])
 | 
					            this.router.navigate([''])
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
@ -123,5 +139,4 @@ export class AppFrameComponent {
 | 
				
			|||||||
      return null
 | 
					      return null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ConfirmDialogComponent } from './confirm-dialog.component';
 | 
					import { ConfirmDialogComponent } from './confirm-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('ConfirmDialogComponent', () => {
 | 
					describe('ConfirmDialogComponent', () => {
 | 
				
			||||||
  let component: ConfirmDialogComponent;
 | 
					  let component: ConfirmDialogComponent
 | 
				
			||||||
  let fixture: ComponentFixture<ConfirmDialogComponent>;
 | 
					  let fixture: ComponentFixture<ConfirmDialogComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ ConfirmDialogComponent ]
 | 
					      declarations: [ConfirmDialogComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(ConfirmDialogComponent);
 | 
					    fixture = TestBed.createComponent(ConfirmDialogComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,14 @@
 | 
				
			|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
 | 
					import { Component, EventEmitter, Input, Output } from '@angular/core'
 | 
				
			||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { Subject } from 'rxjs';
 | 
					import { Subject } from 'rxjs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-confirm-dialog',
 | 
					  selector: 'app-confirm-dialog',
 | 
				
			||||||
  templateUrl: './confirm-dialog.component.html',
 | 
					  templateUrl: './confirm-dialog.component.html',
 | 
				
			||||||
  styleUrls: ['./confirm-dialog.component.scss']
 | 
					  styleUrls: ['./confirm-dialog.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class ConfirmDialogComponent {
 | 
					export class ConfirmDialogComponent {
 | 
				
			||||||
 | 
					  constructor(public activeModal: NgbActiveModal) {}
 | 
				
			||||||
  constructor(public activeModal: NgbActiveModal) { }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Output()
 | 
					  @Output()
 | 
				
			||||||
  public confirmClicked = new EventEmitter()
 | 
					  public confirmClicked = new EventEmitter()
 | 
				
			||||||
@ -24,7 +23,7 @@ export class ConfirmDialogComponent {
 | 
				
			|||||||
  message
 | 
					  message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  btnClass = "btn-primary"
 | 
					  btnClass = 'btn-primary'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  btnCaption = $localize`Confirm`
 | 
					  btnCaption = $localize`Confirm`
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DateDropdownComponent } from './date-dropdown.component';
 | 
					import { DateDropdownComponent } from './date-dropdown.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DateDropdownComponent', () => {
 | 
					describe('DateDropdownComponent', () => {
 | 
				
			||||||
  let component: DateDropdownComponent;
 | 
					  let component: DateDropdownComponent
 | 
				
			||||||
  let fixture: ComponentFixture<DateDropdownComponent>;
 | 
					  let fixture: ComponentFixture<DateDropdownComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ DateDropdownComponent ]
 | 
					      declarations: [DateDropdownComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(DateDropdownComponent);
 | 
					    fixture = TestBed.createComponent(DateDropdownComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,17 @@
 | 
				
			|||||||
import { formatDate } from '@angular/common';
 | 
					import { formatDate } from '@angular/common'
 | 
				
			||||||
import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap';
 | 
					  Component,
 | 
				
			||||||
import { Subject, Subscription } from 'rxjs';
 | 
					  EventEmitter,
 | 
				
			||||||
import { debounceTime } from 'rxjs/operators';
 | 
					  Input,
 | 
				
			||||||
import { SettingsService } from 'src/app/services/settings.service';
 | 
					  Output,
 | 
				
			||||||
import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter';
 | 
					  OnInit,
 | 
				
			||||||
 | 
					  OnDestroy,
 | 
				
			||||||
 | 
					} from '@angular/core'
 | 
				
			||||||
 | 
					import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
 | 
					import { Subject, Subscription } from 'rxjs'
 | 
				
			||||||
 | 
					import { debounceTime } from 'rxjs/operators'
 | 
				
			||||||
 | 
					import { SettingsService } from 'src/app/services/settings.service'
 | 
				
			||||||
 | 
					import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface DateSelection {
 | 
					export interface DateSelection {
 | 
				
			||||||
  before?: string
 | 
					  before?: string
 | 
				
			||||||
@ -20,21 +27,18 @@ const LAST_YEAR = 3
 | 
				
			|||||||
  selector: 'app-date-dropdown',
 | 
					  selector: 'app-date-dropdown',
 | 
				
			||||||
  templateUrl: './date-dropdown.component.html',
 | 
					  templateUrl: './date-dropdown.component.html',
 | 
				
			||||||
  styleUrls: ['./date-dropdown.component.scss'],
 | 
					  styleUrls: ['./date-dropdown.component.scss'],
 | 
				
			||||||
  providers: [
 | 
					  providers: [{ provide: NgbDateAdapter, useClass: ISODateAdapter }],
 | 
				
			||||||
    {provide: NgbDateAdapter, useClass: ISODateAdapter},
 | 
					 | 
				
			||||||
  ]
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DateDropdownComponent implements OnInit, OnDestroy {
 | 
					export class DateDropdownComponent implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(settings: SettingsService) {
 | 
					  constructor(settings: SettingsService) {
 | 
				
			||||||
    this.datePlaceHolder = settings.getLocalizedDateInputFormat()
 | 
					    this.datePlaceHolder = settings.getLocalizedDateInputFormat()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  quickFilters = [
 | 
					  quickFilters = [
 | 
				
			||||||
    {id: LAST_7_DAYS, name: $localize`Last 7 days`},
 | 
					    { id: LAST_7_DAYS, name: $localize`Last 7 days` },
 | 
				
			||||||
    {id: LAST_MONTH, name: $localize`Last month`},
 | 
					    { id: LAST_MONTH, name: $localize`Last month` },
 | 
				
			||||||
    {id: LAST_3_MONTHS, name: $localize`Last 3 months`},
 | 
					    { id: LAST_3_MONTHS, name: $localize`Last 3 months` },
 | 
				
			||||||
    {id: LAST_YEAR, name: $localize`Last year`}
 | 
					    { id: LAST_YEAR, name: $localize`Last year` },
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  datePlaceHolder: string
 | 
					  datePlaceHolder: string
 | 
				
			||||||
@ -62,9 +66,7 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  private sub: Subscription
 | 
					  private sub: Subscription
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit() {
 | 
					  ngOnInit() {
 | 
				
			||||||
    this.sub = this.datesSetDebounce$.pipe(
 | 
					    this.sub = this.datesSetDebounce$.pipe(debounceTime(400)).subscribe(() => {
 | 
				
			||||||
      debounceTime(400)
 | 
					 | 
				
			||||||
    ).subscribe(() => {
 | 
					 | 
				
			||||||
      this.onChange()
 | 
					      this.onChange()
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -81,11 +83,11 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    switch (qf) {
 | 
					    switch (qf) {
 | 
				
			||||||
      case LAST_7_DAYS:
 | 
					      case LAST_7_DAYS:
 | 
				
			||||||
        date.setDate(date.getDate() - 7)
 | 
					        date.setDate(date.getDate() - 7)
 | 
				
			||||||
        break;
 | 
					        break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case LAST_MONTH:
 | 
					      case LAST_MONTH:
 | 
				
			||||||
        date.setMonth(date.getMonth() - 1)
 | 
					        date.setMonth(date.getMonth() - 1)
 | 
				
			||||||
        break;
 | 
					        break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case LAST_3_MONTHS:
 | 
					      case LAST_3_MONTHS:
 | 
				
			||||||
        date.setMonth(date.getMonth() - 3)
 | 
					        date.setMonth(date.getMonth() - 3)
 | 
				
			||||||
@ -94,20 +96,22 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
      case LAST_YEAR:
 | 
					      case LAST_YEAR:
 | 
				
			||||||
        date.setFullYear(date.getFullYear() - 1)
 | 
					        date.setFullYear(date.getFullYear() - 1)
 | 
				
			||||||
        break
 | 
					        break
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.dateAfter = formatDate(date, 'yyyy-MM-dd', "en-us", "UTC")
 | 
					    this.dateAfter = formatDate(date, 'yyyy-MM-dd', 'en-us', 'UTC')
 | 
				
			||||||
    this.onChange()
 | 
					    this.onChange()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onChange() {
 | 
					  onChange() {
 | 
				
			||||||
    this.dateAfterChange.emit(this.dateAfter)
 | 
					    this.dateAfterChange.emit(this.dateAfter)
 | 
				
			||||||
    this.dateBeforeChange.emit(this.dateBefore)
 | 
					    this.dateBeforeChange.emit(this.dateBefore)
 | 
				
			||||||
    this.datesSet.emit({after: this.dateAfter, before: this.dateBefore})
 | 
					    this.datesSet.emit({ after: this.dateAfter, before: this.dateBefore })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onChangeDebounce() {
 | 
					  onChangeDebounce() {
 | 
				
			||||||
    this.datesSetDebounce$.next({after: this.dateAfter, before: this.dateBefore})
 | 
					    this.datesSetDebounce$.next({
 | 
				
			||||||
 | 
					      after: this.dateAfter,
 | 
				
			||||||
 | 
					      before: this.dateBefore,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clearBefore() {
 | 
					  clearBefore() {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { EditDialogComponent } from './edit-dialog.component';
 | 
					import { EditDialogComponent } from './edit-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('EditDialogComponent', () => {
 | 
					describe('EditDialogComponent', () => {
 | 
				
			||||||
  let component: EditDialogComponent;
 | 
					  let component: EditDialogComponent
 | 
				
			||||||
  let fixture: ComponentFixture<EditDialogComponent>;
 | 
					  let fixture: ComponentFixture<EditDialogComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ EditDialogComponent ]
 | 
					      declarations: [EditDialogComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(EditDialogComponent);
 | 
					    fixture = TestBed.createComponent(EditDialogComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,22 @@
 | 
				
			|||||||
import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core';
 | 
					import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core'
 | 
				
			||||||
import { FormGroup } from '@angular/forms';
 | 
					import { FormGroup } from '@angular/forms'
 | 
				
			||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { Observable } from 'rxjs';
 | 
					import { Observable } from 'rxjs'
 | 
				
			||||||
import { map } from 'rxjs/operators';
 | 
					import { map } from 'rxjs/operators'
 | 
				
			||||||
import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model';
 | 
					import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model'
 | 
				
			||||||
import { ObjectWithId } from 'src/app/data/object-with-id';
 | 
					import { ObjectWithId } from 'src/app/data/object-with-id'
 | 
				
			||||||
import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service';
 | 
					import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Directive()
 | 
					@Directive()
 | 
				
			||||||
export abstract class EditDialogComponent<T extends ObjectWithId> implements OnInit {
 | 
					export abstract class EditDialogComponent<T extends ObjectWithId>
 | 
				
			||||||
 | 
					  implements OnInit
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private service: AbstractPaperlessService<T>,
 | 
					    private service: AbstractPaperlessService<T>,
 | 
				
			||||||
    private activeModal: NgbActiveModal,
 | 
					    private activeModal: NgbActiveModal,
 | 
				
			||||||
    private toastService: ToastService) { }
 | 
					    private toastService: ToastService
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  dialogMode: string = 'create'
 | 
					  dialogMode: string = 'create'
 | 
				
			||||||
@ -43,7 +45,7 @@ export abstract class EditDialogComponent<T extends ObjectWithId> implements OnI
 | 
				
			|||||||
    // wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM
 | 
					    // wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM
 | 
				
			||||||
    setTimeout(() => {
 | 
					    setTimeout(() => {
 | 
				
			||||||
      this.closeEnabled = true
 | 
					      this.closeEnabled = true
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getCreateTitle() {
 | 
					  getCreateTitle() {
 | 
				
			||||||
@ -65,7 +67,7 @@ export abstract class EditDialogComponent<T extends ObjectWithId> implements OnI
 | 
				
			|||||||
      case 'edit':
 | 
					      case 'edit':
 | 
				
			||||||
        return this.getEditTitle()
 | 
					        return this.getEditTitle()
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -78,25 +80,31 @@ export abstract class EditDialogComponent<T extends ObjectWithId> implements OnI
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  save() {
 | 
					  save() {
 | 
				
			||||||
    var newObject = Object.assign(Object.assign({}, this.object), this.objectForm.value)
 | 
					    var newObject = Object.assign(
 | 
				
			||||||
 | 
					      Object.assign({}, this.object),
 | 
				
			||||||
 | 
					      this.objectForm.value
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    var serverResponse: Observable<T>
 | 
					    var serverResponse: Observable<T>
 | 
				
			||||||
    switch (this.dialogMode) {
 | 
					    switch (this.dialogMode) {
 | 
				
			||||||
      case 'create':
 | 
					      case 'create':
 | 
				
			||||||
        serverResponse = this.service.create(newObject)
 | 
					        serverResponse = this.service.create(newObject)
 | 
				
			||||||
        break;
 | 
					        break
 | 
				
			||||||
      case 'edit':
 | 
					      case 'edit':
 | 
				
			||||||
        serverResponse = this.service.update(newObject)
 | 
					        serverResponse = this.service.update(newObject)
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.networkActive = true
 | 
					    this.networkActive = true
 | 
				
			||||||
    serverResponse.subscribe(result => {
 | 
					    serverResponse.subscribe(
 | 
				
			||||||
 | 
					      (result) => {
 | 
				
			||||||
        this.activeModal.close()
 | 
					        this.activeModal.close()
 | 
				
			||||||
        this.success.emit(result)
 | 
					        this.success.emit(result)
 | 
				
			||||||
    }, error => {
 | 
					      },
 | 
				
			||||||
 | 
					      (error) => {
 | 
				
			||||||
        this.error = error.error
 | 
					        this.error = error.error
 | 
				
			||||||
        this.networkActive = false
 | 
					        this.networkActive = false
 | 
				
			||||||
    })
 | 
					      }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  cancel() {
 | 
					  cancel() {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FilterableDropodownComponent } from './filterable-dropdown.component';
 | 
					import { FilterableDropodownComponent } from './filterable-dropdown.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('FilterableDropodownComponent', () => {
 | 
					describe('FilterableDropodownComponent', () => {
 | 
				
			||||||
  let component: FilterableDropodownComponent;
 | 
					  let component: FilterableDropodownComponent
 | 
				
			||||||
  let fixture: ComponentFixture<FilterableDropodownComponent>;
 | 
					  let fixture: ComponentFixture<FilterableDropodownComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ FilterableDropodownComponent ]
 | 
					      declarations: [FilterableDropodownComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(FilterableDropodownComponent);
 | 
					    fixture = TestBed.createComponent(FilterableDropodownComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,17 +1,23 @@
 | 
				
			|||||||
import { Component, EventEmitter, Input, Output, ElementRef, ViewChild } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { FilterPipe } from  'src/app/pipes/filter.pipe';
 | 
					  Component,
 | 
				
			||||||
 | 
					  EventEmitter,
 | 
				
			||||||
 | 
					  Input,
 | 
				
			||||||
 | 
					  Output,
 | 
				
			||||||
 | 
					  ElementRef,
 | 
				
			||||||
 | 
					  ViewChild,
 | 
				
			||||||
 | 
					} from '@angular/core'
 | 
				
			||||||
 | 
					import { FilterPipe } from 'src/app/pipes/filter.pipe'
 | 
				
			||||||
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
 | 
					import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { ToggleableItemState } from './toggleable-dropdown-button/toggleable-dropdown-button.component';
 | 
					import { ToggleableItemState } from './toggleable-dropdown-button/toggleable-dropdown-button.component'
 | 
				
			||||||
import { MatchingModel } from 'src/app/data/matching-model';
 | 
					import { MatchingModel } from 'src/app/data/matching-model'
 | 
				
			||||||
import { Subject } from 'rxjs';
 | 
					import { Subject } from 'rxjs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ChangedItems {
 | 
					export interface ChangedItems {
 | 
				
			||||||
  itemsToAdd: MatchingModel[],
 | 
					  itemsToAdd: MatchingModel[]
 | 
				
			||||||
  itemsToRemove: MatchingModel[]
 | 
					  itemsToRemove: MatchingModel[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class FilterableDropdownSelectionModel {
 | 
					export class FilterableDropdownSelectionModel {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  changed = new Subject<FilterableDropdownSelectionModel>()
 | 
					  changed = new Subject<FilterableDropdownSelectionModel>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  multiple = false
 | 
					  multiple = false
 | 
				
			||||||
@ -22,14 +28,20 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  get itemsSorted(): MatchingModel[] {
 | 
					  get itemsSorted(): MatchingModel[] {
 | 
				
			||||||
    // TODO: this is getting called very often
 | 
					    // TODO: this is getting called very often
 | 
				
			||||||
    return this.items.sort((a,b) => {
 | 
					    return this.items.sort((a, b) => {
 | 
				
			||||||
      if (a.id == null && b.id != null) {
 | 
					      if (a.id == null && b.id != null) {
 | 
				
			||||||
        return -1
 | 
					        return -1
 | 
				
			||||||
      } else if (a.id != null && b.id == null) {
 | 
					      } else if (a.id != null && b.id == null) {
 | 
				
			||||||
        return 1
 | 
					        return 1
 | 
				
			||||||
      } else if (this.getNonTemporary(a.id) == ToggleableItemState.NotSelected && this.getNonTemporary(b.id) != ToggleableItemState.NotSelected) {
 | 
					      } else if (
 | 
				
			||||||
 | 
					        this.getNonTemporary(a.id) == ToggleableItemState.NotSelected &&
 | 
				
			||||||
 | 
					        this.getNonTemporary(b.id) != ToggleableItemState.NotSelected
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
        return 1
 | 
					        return 1
 | 
				
			||||||
      } else if (this.getNonTemporary(a.id) != ToggleableItemState.NotSelected && this.getNonTemporary(b.id) == ToggleableItemState.NotSelected) {
 | 
					      } else if (
 | 
				
			||||||
 | 
					        this.getNonTemporary(a.id) != ToggleableItemState.NotSelected &&
 | 
				
			||||||
 | 
					        this.getNonTemporary(b.id) == ToggleableItemState.NotSelected
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
        return -1
 | 
					        return -1
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        return a.name.localeCompare(b.name)
 | 
					        return a.name.localeCompare(b.name)
 | 
				
			||||||
@ -42,11 +54,17 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
  private temporarySelectionStates = new Map<number, ToggleableItemState>()
 | 
					  private temporarySelectionStates = new Map<number, ToggleableItemState>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getSelectedItems() {
 | 
					  getSelectedItems() {
 | 
				
			||||||
    return this.items.filter(i => this.temporarySelectionStates.get(i.id) == ToggleableItemState.Selected)
 | 
					    return this.items.filter(
 | 
				
			||||||
 | 
					      (i) =>
 | 
				
			||||||
 | 
					        this.temporarySelectionStates.get(i.id) == ToggleableItemState.Selected
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getExcludedItems() {
 | 
					  getExcludedItems() {
 | 
				
			||||||
    return this.items.filter(i => this.temporarySelectionStates.get(i.id) == ToggleableItemState.Excluded)
 | 
					    return this.items.filter(
 | 
				
			||||||
 | 
					      (i) =>
 | 
				
			||||||
 | 
					        this.temporarySelectionStates.get(i.id) == ToggleableItemState.Excluded
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  set(id: number, state: ToggleableItemState, fireEvent = true) {
 | 
					  set(id: number, state: ToggleableItemState, fireEvent = true) {
 | 
				
			||||||
@ -62,9 +80,16 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  toggle(id: number, fireEvent = true) {
 | 
					  toggle(id: number, fireEvent = true) {
 | 
				
			||||||
    let state = this.temporarySelectionStates.get(id)
 | 
					    let state = this.temporarySelectionStates.get(id)
 | 
				
			||||||
    if (state == null || (state != ToggleableItemState.Selected && state != ToggleableItemState.Excluded)) {
 | 
					    if (
 | 
				
			||||||
 | 
					      state == null ||
 | 
				
			||||||
 | 
					      (state != ToggleableItemState.Selected &&
 | 
				
			||||||
 | 
					        state != ToggleableItemState.Excluded)
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
      this.temporarySelectionStates.set(id, ToggleableItemState.Selected)
 | 
					      this.temporarySelectionStates.set(id, ToggleableItemState.Selected)
 | 
				
			||||||
    } else if (state == ToggleableItemState.Selected || state == ToggleableItemState.Excluded) {
 | 
					    } else if (
 | 
				
			||||||
 | 
					      state == ToggleableItemState.Selected ||
 | 
				
			||||||
 | 
					      state == ToggleableItemState.Excluded
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
      this.temporarySelectionStates.delete(id)
 | 
					      this.temporarySelectionStates.delete(id)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -91,7 +116,7 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  exclude(id: number, fireEvent:boolean = true) {
 | 
					  exclude(id: number, fireEvent: boolean = true) {
 | 
				
			||||||
    let state = this.temporarySelectionStates.get(id)
 | 
					    let state = this.temporarySelectionStates.get(id)
 | 
				
			||||||
    if (state == null || state != ToggleableItemState.Excluded) {
 | 
					    if (state == null || state != ToggleableItemState.Excluded) {
 | 
				
			||||||
      this.temporarySelectionStates.set(id, ToggleableItemState.Excluded)
 | 
					      this.temporarySelectionStates.set(id, ToggleableItemState.Excluded)
 | 
				
			||||||
@ -130,7 +155,9 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get(id: number) {
 | 
					  get(id: number) {
 | 
				
			||||||
    return this.temporarySelectionStates.get(id) || ToggleableItemState.NotSelected
 | 
					    return (
 | 
				
			||||||
 | 
					      this.temporarySelectionStates.get(id) || ToggleableItemState.NotSelected
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  selectionSize() {
 | 
					  selectionSize() {
 | 
				
			||||||
@ -150,9 +177,19 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  isDirty() {
 | 
					  isDirty() {
 | 
				
			||||||
    if (!Array.from(this.temporarySelectionStates.keys()).every(id => this.temporarySelectionStates.get(id) == this.selectionStates.get(id))) {
 | 
					    if (
 | 
				
			||||||
 | 
					      !Array.from(this.temporarySelectionStates.keys()).every(
 | 
				
			||||||
 | 
					        (id) =>
 | 
				
			||||||
 | 
					          this.temporarySelectionStates.get(id) == this.selectionStates.get(id)
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
      return true
 | 
					      return true
 | 
				
			||||||
    } else if (!Array.from(this.selectionStates.keys()).every(id => this.selectionStates.get(id) == this.temporarySelectionStates.get(id))) {
 | 
					    } else if (
 | 
				
			||||||
 | 
					      !Array.from(this.selectionStates.keys()).every(
 | 
				
			||||||
 | 
					        (id) =>
 | 
				
			||||||
 | 
					          this.selectionStates.get(id) == this.temporarySelectionStates.get(id)
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
      return true
 | 
					      return true
 | 
				
			||||||
    } else if (this.temporaryLogicalOperator !== this._logicalOperator) {
 | 
					    } else if (this.temporaryLogicalOperator !== this._logicalOperator) {
 | 
				
			||||||
      return true
 | 
					      return true
 | 
				
			||||||
@ -162,7 +199,10 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  isNoneSelected() {
 | 
					  isNoneSelected() {
 | 
				
			||||||
    return this.selectionSize() == 1 && this.get(null) == ToggleableItemState.Selected
 | 
					    return (
 | 
				
			||||||
 | 
					      this.selectionSize() == 1 &&
 | 
				
			||||||
 | 
					      this.get(null) == ToggleableItemState.Selected
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  init(map) {
 | 
					  init(map) {
 | 
				
			||||||
@ -187,8 +227,17 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  diff(): ChangedItems {
 | 
					  diff(): ChangedItems {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      itemsToAdd: this.items.filter(item => this.temporarySelectionStates.get(item.id) == ToggleableItemState.Selected && this.selectionStates.get(item.id) != ToggleableItemState.Selected),
 | 
					      itemsToAdd: this.items.filter(
 | 
				
			||||||
      itemsToRemove: this.items.filter(item => !this.temporarySelectionStates.has(item.id) && this.selectionStates.has(item.id)),
 | 
					        (item) =>
 | 
				
			||||||
 | 
					          this.temporarySelectionStates.get(item.id) ==
 | 
				
			||||||
 | 
					            ToggleableItemState.Selected &&
 | 
				
			||||||
 | 
					          this.selectionStates.get(item.id) != ToggleableItemState.Selected
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      itemsToRemove: this.items.filter(
 | 
				
			||||||
 | 
					        (item) =>
 | 
				
			||||||
 | 
					          !this.temporarySelectionStates.has(item.id) &&
 | 
				
			||||||
 | 
					          this.selectionStates.has(item.id)
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -196,10 +245,9 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-filterable-dropdown',
 | 
					  selector: 'app-filterable-dropdown',
 | 
				
			||||||
  templateUrl: './filterable-dropdown.component.html',
 | 
					  templateUrl: './filterable-dropdown.component.html',
 | 
				
			||||||
  styleUrls: ['./filterable-dropdown.component.scss']
 | 
					  styleUrls: ['./filterable-dropdown.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class FilterableDropdownComponent {
 | 
					export class FilterableDropdownComponent {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
 | 
					  @ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
 | 
				
			||||||
  @ViewChild('dropdown') dropdown: NgbDropdown
 | 
					  @ViewChild('dropdown') dropdown: NgbDropdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -211,7 +259,7 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
      this._selectionModel.items = Array.from(items)
 | 
					      this._selectionModel.items = Array.from(items)
 | 
				
			||||||
      this._selectionModel.items.unshift({
 | 
					      this._selectionModel.items.unshift({
 | 
				
			||||||
        name: $localize`:Filter drop down element to filter for documents with no correspondent/type/tag assigned:Not assigned`,
 | 
					        name: $localize`:Filter drop down element to filter for documents with no correspondent/type/tag assigned:Not assigned`,
 | 
				
			||||||
        id: null
 | 
					        id: null,
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -229,7 +277,7 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
      model.items = this.selectionModel.items
 | 
					      model.items = this.selectionModel.items
 | 
				
			||||||
      model.multiple = this.selectionModel.multiple
 | 
					      model.multiple = this.selectionModel.multiple
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    model.changed.subscribe(updatedModel => {
 | 
					    model.changed.subscribe((updatedModel) => {
 | 
				
			||||||
      this.selectionModelChange.next(updatedModel)
 | 
					      this.selectionModelChange.next(updatedModel)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    this._selectionModel = model
 | 
					    this._selectionModel = model
 | 
				
			||||||
@ -255,7 +303,7 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
  title: string
 | 
					  title: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  filterPlaceholder: string = ""
 | 
					  filterPlaceholder: string = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  icon: string
 | 
					  icon: string
 | 
				
			||||||
@ -276,14 +324,17 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
  open = new EventEmitter()
 | 
					  open = new EventEmitter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get operatorToggleEnabled(): boolean {
 | 
					  get operatorToggleEnabled(): boolean {
 | 
				
			||||||
    return this.selectionModel.selectionSize() > 1 && this.selectionModel.getExcludedItems().length == 0
 | 
					    return (
 | 
				
			||||||
 | 
					      this.selectionModel.selectionSize() > 1 &&
 | 
				
			||||||
 | 
					      this.selectionModel.getExcludedItems().length == 0
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  modelIsDirty: boolean = false
 | 
					  modelIsDirty: boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(private filterPipe: FilterPipe) {
 | 
					  constructor(private filterPipe: FilterPipe) {
 | 
				
			||||||
    this.selectionModel = new FilterableDropdownSelectionModel()
 | 
					    this.selectionModel = new FilterableDropdownSelectionModel()
 | 
				
			||||||
    this.selectionModelChange.subscribe(updatedModel => {
 | 
					    this.selectionModelChange.subscribe((updatedModel) => {
 | 
				
			||||||
      this.modelIsDirty = updatedModel.isDirty()
 | 
					      this.modelIsDirty = updatedModel.isDirty()
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -300,7 +351,7 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
  dropdownOpenChange(open: boolean): void {
 | 
					  dropdownOpenChange(open: boolean): void {
 | 
				
			||||||
    if (open) {
 | 
					    if (open) {
 | 
				
			||||||
      setTimeout(() => {
 | 
					      setTimeout(() => {
 | 
				
			||||||
        this.listFilterTextInput.nativeElement.focus();
 | 
					        this.listFilterTextInput.nativeElement.focus()
 | 
				
			||||||
      }, 0)
 | 
					      }, 0)
 | 
				
			||||||
      if (this.editing) {
 | 
					      if (this.editing) {
 | 
				
			||||||
        this.selectionModel.reset()
 | 
					        this.selectionModel.reset()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ToggleableDropdownButtonComponent } from './toggleable-dropdown-button.component';
 | 
					import { ToggleableDropdownButtonComponent } from './toggleable-dropdown-button.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('ToggleableDropdownButtonComponent', () => {
 | 
					describe('ToggleableDropdownButtonComponent', () => {
 | 
				
			||||||
  let component: ToggleableDropdownButtonComponent;
 | 
					  let component: ToggleableDropdownButtonComponent
 | 
				
			||||||
  let fixture: ComponentFixture<ToggleableDropdownButtonComponent>;
 | 
					  let fixture: ComponentFixture<ToggleableDropdownButtonComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ ToggleableDropdownButtonComponent ]
 | 
					      declarations: [ToggleableDropdownButtonComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(ToggleableDropdownButtonComponent);
 | 
					    fixture = TestBed.createComponent(ToggleableDropdownButtonComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,19 @@
 | 
				
			|||||||
import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core';
 | 
					import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core'
 | 
				
			||||||
import { MatchingModel } from 'src/app/data/matching-model';
 | 
					import { MatchingModel } from 'src/app/data/matching-model'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum ToggleableItemState {
 | 
					export enum ToggleableItemState {
 | 
				
			||||||
  NotSelected = 0,
 | 
					  NotSelected = 0,
 | 
				
			||||||
  Selected = 1,
 | 
					  Selected = 1,
 | 
				
			||||||
  PartiallySelected = 2,
 | 
					  PartiallySelected = 2,
 | 
				
			||||||
  Excluded = 3
 | 
					  Excluded = 3,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-toggleable-dropdown-button',
 | 
					  selector: 'app-toggleable-dropdown-button',
 | 
				
			||||||
  templateUrl: './toggleable-dropdown-button.component.html',
 | 
					  templateUrl: './toggleable-dropdown-button.component.html',
 | 
				
			||||||
  styleUrls: ['./toggleable-dropdown-button.component.scss']
 | 
					  styleUrls: ['./toggleable-dropdown-button.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class ToggleableDropdownButtonComponent {
 | 
					export class ToggleableDropdownButtonComponent {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  item: MatchingModel
 | 
					  item: MatchingModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,30 +1,29 @@
 | 
				
			|||||||
import { Directive, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
 | 
					import { Directive, ElementRef, Input, OnInit, ViewChild } from '@angular/core'
 | 
				
			||||||
import { ControlValueAccessor } from '@angular/forms';
 | 
					import { ControlValueAccessor } from '@angular/forms'
 | 
				
			||||||
import { v4 as uuidv4 } from 'uuid';
 | 
					import { v4 as uuidv4 } from 'uuid'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Directive()
 | 
					@Directive()
 | 
				
			||||||
export class AbstractInputComponent<T> implements OnInit, ControlValueAccessor {
 | 
					export class AbstractInputComponent<T> implements OnInit, ControlValueAccessor {
 | 
				
			||||||
 | 
					  @ViewChild('inputField')
 | 
				
			||||||
  @ViewChild("inputField")
 | 
					 | 
				
			||||||
  inputField: ElementRef
 | 
					  inputField: ElementRef
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() { }
 | 
					  constructor() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onChange = (newValue: T) => {};
 | 
					  onChange = (newValue: T) => {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onTouched = () => {};
 | 
					  onTouched = () => {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  writeValue(newValue: any): void {
 | 
					  writeValue(newValue: any): void {
 | 
				
			||||||
    this.value = newValue
 | 
					    this.value = newValue
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  registerOnChange(fn: any): void {
 | 
					  registerOnChange(fn: any): void {
 | 
				
			||||||
    this.onChange = fn;
 | 
					    this.onChange = fn
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  registerOnTouched(fn: any): void {
 | 
					  registerOnTouched(fn: any): void {
 | 
				
			||||||
    this.onTouched = fn;
 | 
					    this.onTouched = fn
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  setDisabledState?(isDisabled: boolean): void {
 | 
					  setDisabledState?(isDisabled: boolean): void {
 | 
				
			||||||
    this.disabled = isDisabled;
 | 
					    this.disabled = isDisabled
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  focus() {
 | 
					  focus() {
 | 
				
			||||||
@ -37,7 +36,7 @@ export class AbstractInputComponent<T> implements OnInit, ControlValueAccessor {
 | 
				
			|||||||
  title: string
 | 
					  title: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  disabled = false;
 | 
					  disabled = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  error: string
 | 
					  error: string
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { CheckComponent } from './check.component';
 | 
					import { CheckComponent } from './check.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('CheckComponent', () => {
 | 
					describe('CheckComponent', () => {
 | 
				
			||||||
  let component: CheckComponent;
 | 
					  let component: CheckComponent
 | 
				
			||||||
  let fixture: ComponentFixture<CheckComponent>;
 | 
					  let fixture: ComponentFixture<CheckComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ CheckComponent ]
 | 
					      declarations: [CheckComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(CheckComponent);
 | 
					    fixture = TestBed.createComponent(CheckComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,22 +1,22 @@
 | 
				
			|||||||
import { Component, forwardRef, Input, OnInit } from '@angular/core';
 | 
					import { Component, forwardRef, Input, OnInit } from '@angular/core'
 | 
				
			||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
 | 
					import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
 | 
				
			||||||
import { v4 as uuidv4 } from 'uuid';
 | 
					import { v4 as uuidv4 } from 'uuid'
 | 
				
			||||||
import { AbstractInputComponent } from '../abstract-input';
 | 
					import { AbstractInputComponent } from '../abstract-input'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  providers: [{
 | 
					  providers: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      provide: NG_VALUE_ACCESSOR,
 | 
					      provide: NG_VALUE_ACCESSOR,
 | 
				
			||||||
      useExisting: forwardRef(() => CheckComponent),
 | 
					      useExisting: forwardRef(() => CheckComponent),
 | 
				
			||||||
    multi: true
 | 
					      multi: true,
 | 
				
			||||||
  }],
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  selector: 'app-input-check',
 | 
					  selector: 'app-input-check',
 | 
				
			||||||
  templateUrl: './check.component.html',
 | 
					  templateUrl: './check.component.html',
 | 
				
			||||||
  styleUrls: ['./check.component.scss']
 | 
					  styleUrls: ['./check.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class CheckComponent extends AbstractInputComponent<boolean> {
 | 
					export class CheckComponent extends AbstractInputComponent<boolean> {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ColorComponent } from './color.component';
 | 
					import { ColorComponent } from './color.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('ColorComponent', () => {
 | 
					describe('ColorComponent', () => {
 | 
				
			||||||
  let component: ColorComponent;
 | 
					  let component: ColorComponent
 | 
				
			||||||
  let fixture: ComponentFixture<ColorComponent>;
 | 
					  let fixture: ComponentFixture<ColorComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ ColorComponent ]
 | 
					      declarations: [ColorComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(ColorComponent);
 | 
					    fixture = TestBed.createComponent(ColorComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,21 @@
 | 
				
			|||||||
import { Component, forwardRef } from '@angular/core';
 | 
					import { Component, forwardRef } from '@angular/core'
 | 
				
			||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
 | 
					import { NG_VALUE_ACCESSOR } from '@angular/forms'
 | 
				
			||||||
import { randomColor } from 'src/app/utils/color';
 | 
					import { randomColor } from 'src/app/utils/color'
 | 
				
			||||||
import { AbstractInputComponent } from '../abstract-input';
 | 
					import { AbstractInputComponent } from '../abstract-input'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  providers: [{
 | 
					  providers: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      provide: NG_VALUE_ACCESSOR,
 | 
					      provide: NG_VALUE_ACCESSOR,
 | 
				
			||||||
      useExisting: forwardRef(() => ColorComponent),
 | 
					      useExisting: forwardRef(() => ColorComponent),
 | 
				
			||||||
    multi: true
 | 
					      multi: true,
 | 
				
			||||||
  }],
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  selector: 'app-input-color',
 | 
					  selector: 'app-input-color',
 | 
				
			||||||
  templateUrl: './color.component.html',
 | 
					  templateUrl: './color.component.html',
 | 
				
			||||||
  styleUrls: ['./color.component.scss']
 | 
					  styleUrls: ['./color.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class ColorComponent extends AbstractInputComponent<string> {
 | 
					export class ColorComponent extends AbstractInputComponent<string> {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DateComponent } from './date.component';
 | 
					import { DateComponent } from './date.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DateComponent', () => {
 | 
					describe('DateComponent', () => {
 | 
				
			||||||
  let component: DateComponent;
 | 
					  let component: DateComponent
 | 
				
			||||||
  let fixture: ComponentFixture<DateComponent>;
 | 
					  let fixture: ComponentFixture<DateComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ DateComponent ]
 | 
					      declarations: [DateComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(DateComponent);
 | 
					    fixture = TestBed.createComponent(DateComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,24 @@
 | 
				
			|||||||
import { Component, forwardRef, OnInit } from '@angular/core';
 | 
					import { Component, forwardRef, OnInit } from '@angular/core'
 | 
				
			||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
 | 
					import { NG_VALUE_ACCESSOR } from '@angular/forms'
 | 
				
			||||||
import { SettingsService } from 'src/app/services/settings.service';
 | 
					import { SettingsService } from 'src/app/services/settings.service'
 | 
				
			||||||
import { AbstractInputComponent } from '../abstract-input';
 | 
					import { AbstractInputComponent } from '../abstract-input'
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  providers: [{
 | 
					  providers: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      provide: NG_VALUE_ACCESSOR,
 | 
					      provide: NG_VALUE_ACCESSOR,
 | 
				
			||||||
      useExisting: forwardRef(() => DateComponent),
 | 
					      useExisting: forwardRef(() => DateComponent),
 | 
				
			||||||
    multi: true
 | 
					      multi: true,
 | 
				
			||||||
  }],
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  selector: 'app-input-date',
 | 
					  selector: 'app-input-date',
 | 
				
			||||||
  templateUrl: './date.component.html',
 | 
					  templateUrl: './date.component.html',
 | 
				
			||||||
  styleUrls: ['./date.component.scss']
 | 
					  styleUrls: ['./date.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DateComponent extends AbstractInputComponent<string> implements OnInit {
 | 
					export class DateComponent
 | 
				
			||||||
 | 
					  extends AbstractInputComponent<string>
 | 
				
			||||||
 | 
					  implements OnInit
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
  constructor(private settings: SettingsService) {
 | 
					  constructor(private settings: SettingsService) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { NumberComponent } from './number.component';
 | 
					import { NumberComponent } from './number.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('NumberComponent', () => {
 | 
					describe('NumberComponent', () => {
 | 
				
			||||||
  let component: NumberComponent;
 | 
					  let component: NumberComponent
 | 
				
			||||||
  let fixture: ComponentFixture<NumberComponent>;
 | 
					  let fixture: ComponentFixture<NumberComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ NumberComponent ]
 | 
					      declarations: [NumberComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(NumberComponent);
 | 
					    fixture = TestBed.createComponent(NumberComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,22 @@
 | 
				
			|||||||
import { Component, forwardRef } from '@angular/core';
 | 
					import { Component, forwardRef } from '@angular/core'
 | 
				
			||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
 | 
					import { NG_VALUE_ACCESSOR } from '@angular/forms'
 | 
				
			||||||
import { FILTER_ASN_ISNULL } from 'src/app/data/filter-rule-type';
 | 
					import { FILTER_ASN_ISNULL } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
import { DocumentService } from 'src/app/services/rest/document.service';
 | 
					import { DocumentService } from 'src/app/services/rest/document.service'
 | 
				
			||||||
import { AbstractInputComponent } from '../abstract-input';
 | 
					import { AbstractInputComponent } from '../abstract-input'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  providers: [{
 | 
					  providers: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      provide: NG_VALUE_ACCESSOR,
 | 
					      provide: NG_VALUE_ACCESSOR,
 | 
				
			||||||
      useExisting: forwardRef(() => NumberComponent),
 | 
					      useExisting: forwardRef(() => NumberComponent),
 | 
				
			||||||
    multi: true
 | 
					      multi: true,
 | 
				
			||||||
  }],
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  selector: 'app-input-number',
 | 
					  selector: 'app-input-number',
 | 
				
			||||||
  templateUrl: './number.component.html',
 | 
					  templateUrl: './number.component.html',
 | 
				
			||||||
  styleUrls: ['./number.component.scss']
 | 
					  styleUrls: ['./number.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class NumberComponent extends AbstractInputComponent<number> {
 | 
					export class NumberComponent extends AbstractInputComponent<number> {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(private documentService: DocumentService) {
 | 
					  constructor(private documentService: DocumentService) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -24,16 +25,17 @@ export class NumberComponent extends AbstractInputComponent<number> {
 | 
				
			|||||||
    if (this.value) {
 | 
					    if (this.value) {
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.documentService.listFiltered(1, 1, "archive_serial_number", true, [{rule_type: FILTER_ASN_ISNULL, value: "false"}]).subscribe(
 | 
					    this.documentService
 | 
				
			||||||
      results => {
 | 
					      .listFiltered(1, 1, 'archive_serial_number', true, [
 | 
				
			||||||
 | 
					        { rule_type: FILTER_ASN_ISNULL, value: 'false' },
 | 
				
			||||||
 | 
					      ])
 | 
				
			||||||
 | 
					      .subscribe((results) => {
 | 
				
			||||||
        if (results.count > 0) {
 | 
					        if (results.count > 0) {
 | 
				
			||||||
          this.value = results.results[0].archive_serial_number + 1
 | 
					          this.value = results.results[0].archive_serial_number + 1
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          this.value = 1
 | 
					          this.value = 1
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.onChange(this.value)
 | 
					        this.onChange(this.value)
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { SelectComponent } from './select.component';
 | 
					import { SelectComponent } from './select.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('SelectComponent', () => {
 | 
					describe('SelectComponent', () => {
 | 
				
			||||||
  let component: SelectComponent;
 | 
					  let component: SelectComponent
 | 
				
			||||||
  let fixture: ComponentFixture<SelectComponent>;
 | 
					  let fixture: ComponentFixture<SelectComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ SelectComponent ]
 | 
					      declarations: [SelectComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(SelectComponent);
 | 
					    fixture = TestBed.createComponent(SelectComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +1,26 @@
 | 
				
			|||||||
import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
 | 
					  Component,
 | 
				
			||||||
import { AbstractInputComponent } from '../abstract-input';
 | 
					  EventEmitter,
 | 
				
			||||||
 | 
					  forwardRef,
 | 
				
			||||||
 | 
					  Input,
 | 
				
			||||||
 | 
					  Output,
 | 
				
			||||||
 | 
					} from '@angular/core'
 | 
				
			||||||
 | 
					import { NG_VALUE_ACCESSOR } from '@angular/forms'
 | 
				
			||||||
 | 
					import { AbstractInputComponent } from '../abstract-input'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  providers: [{
 | 
					  providers: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      provide: NG_VALUE_ACCESSOR,
 | 
					      provide: NG_VALUE_ACCESSOR,
 | 
				
			||||||
      useExisting: forwardRef(() => SelectComponent),
 | 
					      useExisting: forwardRef(() => SelectComponent),
 | 
				
			||||||
    multi: true
 | 
					      multi: true,
 | 
				
			||||||
  }],
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  selector: 'app-input-select',
 | 
					  selector: 'app-input-select',
 | 
				
			||||||
  templateUrl: './select.component.html',
 | 
					  templateUrl: './select.component.html',
 | 
				
			||||||
  styleUrls: ['./select.component.scss']
 | 
					  styleUrls: ['./select.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class SelectComponent extends AbstractInputComponent<number> {
 | 
					export class SelectComponent extends AbstractInputComponent<number> {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
    this.addItemRef = this.addItem.bind(this)
 | 
					    this.addItemRef = this.addItem.bind(this)
 | 
				
			||||||
@ -47,7 +54,9 @@ export class SelectComponent extends AbstractInputComponent<number> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  getSuggestions() {
 | 
					  getSuggestions() {
 | 
				
			||||||
    if (this.suggestions && this.items) {
 | 
					    if (this.suggestions && this.items) {
 | 
				
			||||||
      return this.suggestions.filter(id => id != this.value).map(id => this.items.find(item => item.id == id))
 | 
					      return this.suggestions
 | 
				
			||||||
 | 
					        .filter((id) => id != this.value)
 | 
				
			||||||
 | 
					        .map((id) => this.items.find((item) => item.id == id))
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return []
 | 
					      return []
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -75,7 +84,6 @@ export class SelectComponent extends AbstractInputComponent<number> {
 | 
				
			|||||||
  onBlur() {
 | 
					  onBlur() {
 | 
				
			||||||
    setTimeout(() => {
 | 
					    setTimeout(() => {
 | 
				
			||||||
      this.clearLastSearchTerm()
 | 
					      this.clearLastSearchTerm()
 | 
				
			||||||
    }, 3000);
 | 
					    }, 3000)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { TagsComponent } from './tags.component';
 | 
					import { TagsComponent } from './tags.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('TagsComponent', () => {
 | 
					describe('TagsComponent', () => {
 | 
				
			||||||
  let component: TagsComponent;
 | 
					  let component: TagsComponent
 | 
				
			||||||
  let fixture: ComponentFixture<TagsComponent>;
 | 
					  let fixture: ComponentFixture<TagsComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ TagsComponent ]
 | 
					      declarations: [TagsComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(TagsComponent);
 | 
					    fixture = TestBed.createComponent(TagsComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,45 +1,46 @@
 | 
				
			|||||||
import { Component, forwardRef, Input, OnInit } from '@angular/core';
 | 
					import { Component, forwardRef, Input, OnInit } from '@angular/core'
 | 
				
			||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
 | 
					import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
 | 
				
			||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { TagEditDialogComponent } from 'src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component';
 | 
					import { TagEditDialogComponent } from 'src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component'
 | 
				
			||||||
import { PaperlessTag } from 'src/app/data/paperless-tag';
 | 
					import { PaperlessTag } from 'src/app/data/paperless-tag'
 | 
				
			||||||
import { TagService } from 'src/app/services/rest/tag.service';
 | 
					import { TagService } from 'src/app/services/rest/tag.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  providers: [{
 | 
					  providers: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      provide: NG_VALUE_ACCESSOR,
 | 
					      provide: NG_VALUE_ACCESSOR,
 | 
				
			||||||
      useExisting: forwardRef(() => TagsComponent),
 | 
					      useExisting: forwardRef(() => TagsComponent),
 | 
				
			||||||
    multi: true
 | 
					      multi: true,
 | 
				
			||||||
  }],
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  selector: 'app-input-tags',
 | 
					  selector: 'app-input-tags',
 | 
				
			||||||
  templateUrl: './tags.component.html',
 | 
					  templateUrl: './tags.component.html',
 | 
				
			||||||
  styleUrls: ['./tags.component.scss']
 | 
					  styleUrls: ['./tags.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class TagsComponent implements OnInit, ControlValueAccessor {
 | 
					export class TagsComponent implements OnInit, ControlValueAccessor {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(private tagService: TagService, private modalService: NgbModal) {
 | 
					  constructor(private tagService: TagService, private modalService: NgbModal) {
 | 
				
			||||||
    this.createTagRef = this.createTag.bind(this)
 | 
					    this.createTagRef = this.createTag.bind(this)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onChange = (newValue: number[]) => {};
 | 
					  onChange = (newValue: number[]) => {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onTouched = () => {};
 | 
					  onTouched = () => {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  writeValue(newValue: number[]): void {
 | 
					  writeValue(newValue: number[]): void {
 | 
				
			||||||
    this.value = newValue
 | 
					    this.value = newValue
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  registerOnChange(fn: any): void {
 | 
					  registerOnChange(fn: any): void {
 | 
				
			||||||
    this.onChange = fn;
 | 
					    this.onChange = fn
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  registerOnTouched(fn: any): void {
 | 
					  registerOnTouched(fn: any): void {
 | 
				
			||||||
    this.onTouched = fn;
 | 
					    this.onTouched = fn
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  setDisabledState?(isDisabled: boolean): void {
 | 
					  setDisabledState?(isDisabled: boolean): void {
 | 
				
			||||||
    this.disabled = isDisabled;
 | 
					    this.disabled = isDisabled
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.tagService.listAll().subscribe(result => {
 | 
					    this.tagService.listAll().subscribe((result) => {
 | 
				
			||||||
      this.tags = result.results
 | 
					      this.tags = result.results
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -63,7 +64,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  getTag(id) {
 | 
					  getTag(id) {
 | 
				
			||||||
    if (this.tags) {
 | 
					    if (this.tags) {
 | 
				
			||||||
      return this.tags.find(tag => tag.id == id)
 | 
					      return this.tags.find((tag) => tag.id == id)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return null
 | 
					      return null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -80,12 +81,15 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  createTag(name: string = null) {
 | 
					  createTag(name: string = null) {
 | 
				
			||||||
    var modal = this.modalService.open(TagEditDialogComponent, {backdrop: 'static'})
 | 
					    var modal = this.modalService.open(TagEditDialogComponent, {
 | 
				
			||||||
 | 
					      backdrop: 'static',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    modal.componentInstance.dialogMode = 'create'
 | 
					    modal.componentInstance.dialogMode = 'create'
 | 
				
			||||||
    if (name) modal.componentInstance.object = { name: name }
 | 
					    if (name) modal.componentInstance.object = { name: name }
 | 
				
			||||||
    else if (this._lastSearchTerm) modal.componentInstance.object = { name: this._lastSearchTerm }
 | 
					    else if (this._lastSearchTerm)
 | 
				
			||||||
    modal.componentInstance.success.subscribe(newTag => {
 | 
					      modal.componentInstance.object = { name: this._lastSearchTerm }
 | 
				
			||||||
      this.tagService.listAll().subscribe(tags => {
 | 
					    modal.componentInstance.success.subscribe((newTag) => {
 | 
				
			||||||
 | 
					      this.tagService.listAll().subscribe((tags) => {
 | 
				
			||||||
        this.tags = tags.results
 | 
					        this.tags = tags.results
 | 
				
			||||||
        this.value = [...this.value, newTag.id]
 | 
					        this.value = [...this.value, newTag.id]
 | 
				
			||||||
        this.onChange(this.value)
 | 
					        this.onChange(this.value)
 | 
				
			||||||
@ -95,7 +99,9 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  getSuggestions() {
 | 
					  getSuggestions() {
 | 
				
			||||||
    if (this.suggestions && this.tags) {
 | 
					    if (this.suggestions && this.tags) {
 | 
				
			||||||
      return this.suggestions.filter(id => !this.value.includes(id)).map(id => this.tags.find(tag => tag.id == id))
 | 
					      return this.suggestions
 | 
				
			||||||
 | 
					        .filter((id) => !this.value.includes(id))
 | 
				
			||||||
 | 
					        .map((id) => this.tags.find((tag) => tag.id == id))
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return []
 | 
					      return []
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -117,7 +123,6 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
 | 
				
			|||||||
  onBlur() {
 | 
					  onBlur() {
 | 
				
			||||||
    setTimeout(() => {
 | 
					    setTimeout(() => {
 | 
				
			||||||
      this.clearLastSearchTerm()
 | 
					      this.clearLastSearchTerm()
 | 
				
			||||||
    }, 3000);
 | 
					    }, 3000)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { TextComponent } from './text.component';
 | 
					import { TextComponent } from './text.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('TextComponent', () => {
 | 
					describe('TextComponent', () => {
 | 
				
			||||||
  let component: TextComponent;
 | 
					  let component: TextComponent
 | 
				
			||||||
  let fixture: ComponentFixture<TextComponent>;
 | 
					  let fixture: ComponentFixture<TextComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ TextComponent ]
 | 
					      declarations: [TextComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(TextComponent);
 | 
					    fixture = TestBed.createComponent(TextComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,21 @@
 | 
				
			|||||||
import { Component, forwardRef } from '@angular/core';
 | 
					import { Component, forwardRef } from '@angular/core'
 | 
				
			||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
 | 
					import { NG_VALUE_ACCESSOR } from '@angular/forms'
 | 
				
			||||||
import { AbstractInputComponent } from '../abstract-input';
 | 
					import { AbstractInputComponent } from '../abstract-input'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  providers: [{
 | 
					  providers: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      provide: NG_VALUE_ACCESSOR,
 | 
					      provide: NG_VALUE_ACCESSOR,
 | 
				
			||||||
      useExisting: forwardRef(() => TextComponent),
 | 
					      useExisting: forwardRef(() => TextComponent),
 | 
				
			||||||
    multi: true
 | 
					      multi: true,
 | 
				
			||||||
  }],
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  selector: 'app-input-text',
 | 
					  selector: 'app-input-text',
 | 
				
			||||||
  templateUrl: './text.component.html',
 | 
					  templateUrl: './text.component.html',
 | 
				
			||||||
  styleUrls: ['./text.component.scss']
 | 
					  styleUrls: ['./text.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class TextComponent extends AbstractInputComponent<string> {
 | 
					export class TextComponent extends AbstractInputComponent<string> {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { PageHeaderComponent } from './page-header.component';
 | 
					import { PageHeaderComponent } from './page-header.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('PageHeaderComponent', () => {
 | 
					describe('PageHeaderComponent', () => {
 | 
				
			||||||
  let component: PageHeaderComponent;
 | 
					  let component: PageHeaderComponent
 | 
				
			||||||
  let fixture: ComponentFixture<PageHeaderComponent>;
 | 
					  let fixture: ComponentFixture<PageHeaderComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ PageHeaderComponent ]
 | 
					      declarations: [PageHeaderComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(PageHeaderComponent);
 | 
					    fixture = TestBed.createComponent(PageHeaderComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,17 +1,16 @@
 | 
				
			|||||||
import { Component, Input } from '@angular/core';
 | 
					import { Component, Input } from '@angular/core'
 | 
				
			||||||
import { Title } from '@angular/platform-browser';
 | 
					import { Title } from '@angular/platform-browser'
 | 
				
			||||||
import { environment } from 'src/environments/environment';
 | 
					import { environment } from 'src/environments/environment'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-page-header',
 | 
					  selector: 'app-page-header',
 | 
				
			||||||
  templateUrl: './page-header.component.html',
 | 
					  templateUrl: './page-header.component.html',
 | 
				
			||||||
  styleUrls: ['./page-header.component.scss']
 | 
					  styleUrls: ['./page-header.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class PageHeaderComponent {
 | 
					export class PageHeaderComponent {
 | 
				
			||||||
 | 
					  constructor(private titleService: Title) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(private titleService: Title) { }
 | 
					  _title = ''
 | 
				
			||||||
 | 
					 | 
				
			||||||
  _title = ""
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  set title(title: string) {
 | 
					  set title(title: string) {
 | 
				
			||||||
@ -24,6 +23,5 @@ export class PageHeaderComponent {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  subTitle: string = ""
 | 
					  subTitle: string = ''
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { SelectDialogComponent } from './select-dialog.component';
 | 
					import { SelectDialogComponent } from './select-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('SelectDialogComponent', () => {
 | 
					describe('SelectDialogComponent', () => {
 | 
				
			||||||
  let component: SelectDialogComponent;
 | 
					  let component: SelectDialogComponent
 | 
				
			||||||
  let fixture: ComponentFixture<SelectDialogComponent>;
 | 
					  let fixture: ComponentFixture<SelectDialogComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ SelectDialogComponent ]
 | 
					      declarations: [SelectDialogComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(SelectDialogComponent);
 | 
					    fixture = TestBed.createComponent(SelectDialogComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,14 @@
 | 
				
			|||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 | 
					import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
 | 
				
			||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { ObjectWithId } from 'src/app/data/object-with-id';
 | 
					import { ObjectWithId } from 'src/app/data/object-with-id'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-select-dialog',
 | 
					  selector: 'app-select-dialog',
 | 
				
			||||||
  templateUrl: './select-dialog.component.html',
 | 
					  templateUrl: './select-dialog.component.html',
 | 
				
			||||||
  styleUrls: ['./select-dialog.component.scss']
 | 
					  styleUrls: ['./select-dialog.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					 | 
				
			||||||
export class SelectDialogComponent implements OnInit {
 | 
					export class SelectDialogComponent implements OnInit {
 | 
				
			||||||
  constructor(public activeModal: NgbActiveModal) { }
 | 
					  constructor(public activeModal: NgbActiveModal) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Output()
 | 
					  @Output()
 | 
				
			||||||
  public selectClicked = new EventEmitter()
 | 
					  public selectClicked = new EventEmitter()
 | 
				
			||||||
@ -25,8 +24,7 @@ export class SelectDialogComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  selected: number
 | 
					  selected: number
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  cancelClicked() {
 | 
					  cancelClicked() {
 | 
				
			||||||
    this.activeModal.close()
 | 
					    this.activeModal.close()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { TagComponent } from './tag.component';
 | 
					import { TagComponent } from './tag.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('TagComponent', () => {
 | 
					describe('TagComponent', () => {
 | 
				
			||||||
  let component: TagComponent;
 | 
					  let component: TagComponent
 | 
				
			||||||
  let fixture: ComponentFixture<TagComponent>;
 | 
					  let fixture: ComponentFixture<TagComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ TagComponent ]
 | 
					      declarations: [TagComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(TagComponent);
 | 
					    fixture = TestBed.createComponent(TagComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,22 @@
 | 
				
			|||||||
import { Component, Input, OnInit } from '@angular/core';
 | 
					import { Component, Input, OnInit } from '@angular/core'
 | 
				
			||||||
import { PaperlessTag } from 'src/app/data/paperless-tag';
 | 
					import { PaperlessTag } from 'src/app/data/paperless-tag'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-tag',
 | 
					  selector: 'app-tag',
 | 
				
			||||||
  templateUrl: './tag.component.html',
 | 
					  templateUrl: './tag.component.html',
 | 
				
			||||||
  styleUrls: ['./tag.component.scss']
 | 
					  styleUrls: ['./tag.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class TagComponent implements OnInit {
 | 
					export class TagComponent implements OnInit {
 | 
				
			||||||
 | 
					  constructor() {}
 | 
				
			||||||
  constructor() { }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  tag: PaperlessTag
 | 
					  tag: PaperlessTag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  linkTitle: string = ""
 | 
					  linkTitle: string = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  clickable: boolean = false
 | 
					  clickable: boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ToastsComponent } from './toasts.component';
 | 
					import { ToastsComponent } from './toasts.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('ToastsComponent', () => {
 | 
					describe('ToastsComponent', () => {
 | 
				
			||||||
  let component: ToastsComponent;
 | 
					  let component: ToastsComponent
 | 
				
			||||||
  let fixture: ComponentFixture<ToastsComponent>;
 | 
					  let fixture: ComponentFixture<ToastsComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ ToastsComponent ]
 | 
					      declarations: [ToastsComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(ToastsComponent);
 | 
					    fixture = TestBed.createComponent(ToastsComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,14 @@
 | 
				
			|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
 | 
					import { Component, OnDestroy, OnInit } from '@angular/core'
 | 
				
			||||||
import { Subscription } from 'rxjs';
 | 
					import { Subscription } from 'rxjs'
 | 
				
			||||||
import { Toast, ToastService } from 'src/app/services/toast.service';
 | 
					import { Toast, ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-toasts',
 | 
					  selector: 'app-toasts',
 | 
				
			||||||
  templateUrl: './toasts.component.html',
 | 
					  templateUrl: './toasts.component.html',
 | 
				
			||||||
  styleUrls: ['./toasts.component.scss']
 | 
					  styleUrls: ['./toasts.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class ToastsComponent implements OnInit, OnDestroy {
 | 
					export class ToastsComponent implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					  constructor(private toastService: ToastService) {}
 | 
				
			||||||
  constructor(private toastService: ToastService) { }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  subscription: Subscription
 | 
					  subscription: Subscription
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,7 +19,8 @@ export class ToastsComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.subscription = this.toastService.getToasts().subscribe(toasts => this.toasts = toasts)
 | 
					    this.subscription = this.toastService
 | 
				
			||||||
 | 
					      .getToasts()
 | 
				
			||||||
 | 
					      .subscribe((toasts) => (this.toasts = toasts))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DashboardComponent } from './dashboard.component';
 | 
					import { DashboardComponent } from './dashboard.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DashboardComponent', () => {
 | 
					describe('DashboardComponent', () => {
 | 
				
			||||||
  let component: DashboardComponent;
 | 
					  let component: DashboardComponent
 | 
				
			||||||
  let fixture: ComponentFixture<DashboardComponent>;
 | 
					  let fixture: ComponentFixture<DashboardComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ DashboardComponent ]
 | 
					      declarations: [DashboardComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(DashboardComponent);
 | 
					    fixture = TestBed.createComponent(DashboardComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,15 @@
 | 
				
			|||||||
import { Component, OnInit } from '@angular/core';
 | 
					import { Component, OnInit } from '@angular/core'
 | 
				
			||||||
import { Meta } from '@angular/platform-browser';
 | 
					import { Meta } from '@angular/platform-browser'
 | 
				
			||||||
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
 | 
					import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
 | 
				
			||||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
 | 
					import { SavedViewService } from 'src/app/services/rest/saved-view.service'
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-dashboard',
 | 
					  selector: 'app-dashboard',
 | 
				
			||||||
  templateUrl: './dashboard.component.html',
 | 
					  templateUrl: './dashboard.component.html',
 | 
				
			||||||
  styleUrls: ['./dashboard.component.scss']
 | 
					  styleUrls: ['./dashboard.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DashboardComponent implements OnInit {
 | 
					export class DashboardComponent implements OnInit {
 | 
				
			||||||
 | 
					  constructor(private savedViewService: SavedViewService, private meta: Meta) {}
 | 
				
			||||||
  constructor(
 | 
					 | 
				
			||||||
    private savedViewService: SavedViewService,
 | 
					 | 
				
			||||||
    private meta: Meta
 | 
					 | 
				
			||||||
  ) { }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get displayName() {
 | 
					  get displayName() {
 | 
				
			||||||
    let tagFullName = this.meta.getTag('name=full_name')
 | 
					    let tagFullName = this.meta.getTag('name=full_name')
 | 
				
			||||||
@ -39,9 +34,10 @@ export class DashboardComponent implements OnInit {
 | 
				
			|||||||
  savedViews: PaperlessSavedView[] = []
 | 
					  savedViews: PaperlessSavedView[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.savedViewService.listAll().subscribe(results => {
 | 
					    this.savedViewService.listAll().subscribe((results) => {
 | 
				
			||||||
      this.savedViews = results.results.filter(savedView => savedView.show_on_dashboard)
 | 
					      this.savedViews = results.results.filter(
 | 
				
			||||||
 | 
					        (savedView) => savedView.show_on_dashboard
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { SavedViewWidgetComponent } from './saved-view-widget.component';
 | 
					import { SavedViewWidgetComponent } from './saved-view-widget.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('SavedViewWidgetComponent', () => {
 | 
					describe('SavedViewWidgetComponent', () => {
 | 
				
			||||||
  let component: SavedViewWidgetComponent;
 | 
					  let component: SavedViewWidgetComponent
 | 
				
			||||||
  let fixture: ComponentFixture<SavedViewWidgetComponent>;
 | 
					  let fixture: ComponentFixture<SavedViewWidgetComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ SavedViewWidgetComponent ]
 | 
					      declarations: [SavedViewWidgetComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(SavedViewWidgetComponent);
 | 
					    fixture = TestBed.createComponent(SavedViewWidgetComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,24 +1,24 @@
 | 
				
			|||||||
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
 | 
					import { Component, Input, OnDestroy, OnInit } from '@angular/core'
 | 
				
			||||||
import { Router } from '@angular/router';
 | 
					import { Router } from '@angular/router'
 | 
				
			||||||
import { Subscription } from 'rxjs';
 | 
					import { Subscription } from 'rxjs'
 | 
				
			||||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
 | 
					import { PaperlessDocument } from 'src/app/data/paperless-document'
 | 
				
			||||||
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
 | 
					import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
import { ConsumerStatusService } from 'src/app/services/consumer-status.service';
 | 
					import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
 | 
				
			||||||
import { DocumentService } from 'src/app/services/rest/document.service';
 | 
					import { DocumentService } from 'src/app/services/rest/document.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-saved-view-widget',
 | 
					  selector: 'app-saved-view-widget',
 | 
				
			||||||
  templateUrl: './saved-view-widget.component.html',
 | 
					  templateUrl: './saved-view-widget.component.html',
 | 
				
			||||||
  styleUrls: ['./saved-view-widget.component.scss']
 | 
					  styleUrls: ['./saved-view-widget.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class SavedViewWidgetComponent implements OnInit, OnDestroy {
 | 
					export class SavedViewWidgetComponent implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private documentService: DocumentService,
 | 
					    private documentService: DocumentService,
 | 
				
			||||||
    private router: Router,
 | 
					    private router: Router,
 | 
				
			||||||
    private list: DocumentListViewService,
 | 
					    private list: DocumentListViewService,
 | 
				
			||||||
    private consumerStatusService: ConsumerStatusService) { }
 | 
					    private consumerStatusService: ConsumerStatusService
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  savedView: PaperlessSavedView
 | 
					  savedView: PaperlessSavedView
 | 
				
			||||||
@ -29,7 +29,9 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.reload()
 | 
					    this.reload()
 | 
				
			||||||
    this.subscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
 | 
					    this.subscription = this.consumerStatusService
 | 
				
			||||||
 | 
					      .onDocumentConsumptionFinished()
 | 
				
			||||||
 | 
					      .subscribe((status) => {
 | 
				
			||||||
        this.reload()
 | 
					        this.reload()
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -39,7 +41,15 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reload() {
 | 
					  reload() {
 | 
				
			||||||
    this.documentService.listFiltered(1,10,this.savedView.sort_field, this.savedView.sort_reverse, this.savedView.filter_rules).subscribe(result => {
 | 
					    this.documentService
 | 
				
			||||||
 | 
					      .listFiltered(
 | 
				
			||||||
 | 
					        1,
 | 
				
			||||||
 | 
					        10,
 | 
				
			||||||
 | 
					        this.savedView.sort_field,
 | 
				
			||||||
 | 
					        this.savedView.sort_reverse,
 | 
				
			||||||
 | 
					        this.savedView.filter_rules
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      .subscribe((result) => {
 | 
				
			||||||
        this.documents = result.results
 | 
					        this.documents = result.results
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -49,8 +59,7 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
      this.router.navigate(['view', this.savedView.id])
 | 
					      this.router.navigate(['view', this.savedView.id])
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      this.list.loadSavedView(this.savedView, true)
 | 
					      this.list.loadSavedView(this.savedView, true)
 | 
				
			||||||
      this.router.navigate(["documents"])
 | 
					      this.router.navigate(['documents'])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { StatisticsWidgetComponent } from './statistics-widget.component';
 | 
					import { StatisticsWidgetComponent } from './statistics-widget.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('StatisticsWidgetComponent', () => {
 | 
					describe('StatisticsWidgetComponent', () => {
 | 
				
			||||||
  let component: StatisticsWidgetComponent;
 | 
					  let component: StatisticsWidgetComponent
 | 
				
			||||||
  let fixture: ComponentFixture<StatisticsWidgetComponent>;
 | 
					  let fixture: ComponentFixture<StatisticsWidgetComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ StatisticsWidgetComponent ]
 | 
					      declarations: [StatisticsWidgetComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(StatisticsWidgetComponent);
 | 
					    fixture = TestBed.createComponent(StatisticsWidgetComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,24 +1,24 @@
 | 
				
			|||||||
import { HttpClient } from '@angular/common/http';
 | 
					import { HttpClient } from '@angular/common/http'
 | 
				
			||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
 | 
					import { Component, OnDestroy, OnInit } from '@angular/core'
 | 
				
			||||||
import { Observable, Subscription } from 'rxjs';
 | 
					import { Observable, Subscription } from 'rxjs'
 | 
				
			||||||
import { ConsumerStatusService } from 'src/app/services/consumer-status.service';
 | 
					import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
 | 
				
			||||||
import { environment } from 'src/environments/environment';
 | 
					import { environment } from 'src/environments/environment'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Statistics {
 | 
					export interface Statistics {
 | 
				
			||||||
  documents_total?: number
 | 
					  documents_total?: number
 | 
				
			||||||
  documents_inbox?: number
 | 
					  documents_inbox?: number
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-statistics-widget',
 | 
					  selector: 'app-statistics-widget',
 | 
				
			||||||
  templateUrl: './statistics-widget.component.html',
 | 
					  templateUrl: './statistics-widget.component.html',
 | 
				
			||||||
  styleUrls: ['./statistics-widget.component.scss']
 | 
					  styleUrls: ['./statistics-widget.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class StatisticsWidgetComponent implements OnInit, OnDestroy {
 | 
					export class StatisticsWidgetComponent implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
  constructor(private http: HttpClient,
 | 
					    private http: HttpClient,
 | 
				
			||||||
    private consumerStatusService: ConsumerStatusService) { }
 | 
					    private consumerStatusService: ConsumerStatusService
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  statistics: Statistics = {}
 | 
					  statistics: Statistics = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -29,14 +29,16 @@ export class StatisticsWidgetComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reload() {
 | 
					  reload() {
 | 
				
			||||||
    this.getStatistics().subscribe(statistics => {
 | 
					    this.getStatistics().subscribe((statistics) => {
 | 
				
			||||||
      this.statistics = statistics
 | 
					      this.statistics = statistics
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.reload()
 | 
					    this.reload()
 | 
				
			||||||
    this.subscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
 | 
					    this.subscription = this.consumerStatusService
 | 
				
			||||||
 | 
					      .onDocumentConsumptionFinished()
 | 
				
			||||||
 | 
					      .subscribe((status) => {
 | 
				
			||||||
        this.reload()
 | 
					        this.reload()
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -44,5 +46,4 @@ export class StatisticsWidgetComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  ngOnDestroy(): void {
 | 
					  ngOnDestroy(): void {
 | 
				
			||||||
    this.subscription.unsubscribe()
 | 
					    this.subscription.unsubscribe()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { UploadFileWidgetComponent } from './upload-file-widget.component';
 | 
					import { UploadFileWidgetComponent } from './upload-file-widget.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('UploadFileWidgetComponent', () => {
 | 
					describe('UploadFileWidgetComponent', () => {
 | 
				
			||||||
  let component: UploadFileWidgetComponent;
 | 
					  let component: UploadFileWidgetComponent
 | 
				
			||||||
  let fixture: ComponentFixture<UploadFileWidgetComponent>;
 | 
					  let fixture: ComponentFixture<UploadFileWidgetComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ UploadFileWidgetComponent ]
 | 
					      declarations: [UploadFileWidgetComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(UploadFileWidgetComponent);
 | 
					    fixture = TestBed.createComponent(UploadFileWidgetComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,19 @@
 | 
				
			|||||||
import { HttpEventType } from '@angular/common/http';
 | 
					import { HttpEventType } from '@angular/common/http'
 | 
				
			||||||
import { Component, OnInit } from '@angular/core';
 | 
					import { Component, OnInit } from '@angular/core'
 | 
				
			||||||
import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop';
 | 
					import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'
 | 
				
			||||||
import { ConsumerStatusService, FileStatus, FileStatusPhase } from 'src/app/services/consumer-status.service';
 | 
					import {
 | 
				
			||||||
import { DocumentService } from 'src/app/services/rest/document.service';
 | 
					  ConsumerStatusService,
 | 
				
			||||||
 | 
					  FileStatus,
 | 
				
			||||||
 | 
					  FileStatusPhase,
 | 
				
			||||||
 | 
					} from 'src/app/services/consumer-status.service'
 | 
				
			||||||
 | 
					import { DocumentService } from 'src/app/services/rest/document.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MAX_ALERTS = 5
 | 
					const MAX_ALERTS = 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-upload-file-widget',
 | 
					  selector: 'app-upload-file-widget',
 | 
				
			||||||
  templateUrl: './upload-file-widget.component.html',
 | 
					  templateUrl: './upload-file-widget.component.html',
 | 
				
			||||||
  styleUrls: ['./upload-file-widget.component.scss']
 | 
					  styleUrls: ['./upload-file-widget.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class UploadFileWidgetComponent implements OnInit {
 | 
					export class UploadFileWidgetComponent implements OnInit {
 | 
				
			||||||
  alertsExpanded = false
 | 
					  alertsExpanded = false
 | 
				
			||||||
@ -17,7 +21,7 @@ export class UploadFileWidgetComponent implements OnInit {
 | 
				
			|||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private documentService: DocumentService,
 | 
					    private documentService: DocumentService,
 | 
				
			||||||
    private consumerStatusService: ConsumerStatusService
 | 
					    private consumerStatusService: ConsumerStatusService
 | 
				
			||||||
  ) { }
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getStatus() {
 | 
					  getStatus() {
 | 
				
			||||||
    return this.consumerStatusService.getConsumerStatus().slice(0, MAX_ALERTS)
 | 
					    return this.consumerStatusService.getConsumerStatus().slice(0, MAX_ALERTS)
 | 
				
			||||||
@ -25,7 +29,8 @@ export class UploadFileWidgetComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  getStatusSummary() {
 | 
					  getStatusSummary() {
 | 
				
			||||||
    let strings = []
 | 
					    let strings = []
 | 
				
			||||||
    let countUploadingAndProcessing =  this.consumerStatusService.getConsumerStatusNotCompleted().length
 | 
					    let countUploadingAndProcessing =
 | 
				
			||||||
 | 
					      this.consumerStatusService.getConsumerStatusNotCompleted().length
 | 
				
			||||||
    let countFailed = this.getStatusFailed().length
 | 
					    let countFailed = this.getStatusFailed().length
 | 
				
			||||||
    let countSuccess = this.getStatusSuccess().length
 | 
					    let countSuccess = this.getStatusSuccess().length
 | 
				
			||||||
    if (countUploadingAndProcessing > 0) {
 | 
					    if (countUploadingAndProcessing > 0) {
 | 
				
			||||||
@ -37,16 +42,21 @@ export class UploadFileWidgetComponent implements OnInit {
 | 
				
			|||||||
    if (countSuccess > 0) {
 | 
					    if (countSuccess > 0) {
 | 
				
			||||||
      strings.push($localize`Added: ${countSuccess}`)
 | 
					      strings.push($localize`Added: ${countSuccess}`)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return strings.join($localize`:this string is used to separate processing, failed and added on the file upload widget:, `)
 | 
					    return strings.join(
 | 
				
			||||||
 | 
					      $localize`:this string is used to separate processing, failed and added on the file upload widget:, `
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getStatusHidden() {
 | 
					  getStatusHidden() {
 | 
				
			||||||
    if (this.consumerStatusService.getConsumerStatus().length < MAX_ALERTS) return []
 | 
					    if (this.consumerStatusService.getConsumerStatus().length < MAX_ALERTS)
 | 
				
			||||||
 | 
					      return []
 | 
				
			||||||
    else return this.consumerStatusService.getConsumerStatus().slice(MAX_ALERTS)
 | 
					    else return this.consumerStatusService.getConsumerStatus().slice(MAX_ALERTS)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getStatusUploading() {
 | 
					  getStatusUploading() {
 | 
				
			||||||
    return this.consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING)
 | 
					    return this.consumerStatusService.getConsumerStatus(
 | 
				
			||||||
 | 
					      FileStatusPhase.UPLOADING
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getStatusFailed() {
 | 
					  getStatusFailed() {
 | 
				
			||||||
@ -64,7 +74,7 @@ export class UploadFileWidgetComponent implements OnInit {
 | 
				
			|||||||
    let current = 0
 | 
					    let current = 0
 | 
				
			||||||
    let max = 0
 | 
					    let max = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.getStatusUploading().forEach(status => {
 | 
					    this.getStatusUploading().forEach((status) => {
 | 
				
			||||||
      current += status.currentPhaseProgress
 | 
					      current += status.currentPhaseProgress
 | 
				
			||||||
      max += status.currentPhaseMaxProgress
 | 
					      max += status.currentPhaseMaxProgress
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
@ -73,18 +83,21 @@ export class UploadFileWidgetComponent implements OnInit {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  isFinished(status: FileStatus) {
 | 
					  isFinished(status: FileStatus) {
 | 
				
			||||||
    return status.phase == FileStatusPhase.FAILED || status.phase == FileStatusPhase.SUCCESS
 | 
					    return (
 | 
				
			||||||
 | 
					      status.phase == FileStatusPhase.FAILED ||
 | 
				
			||||||
 | 
					      status.phase == FileStatusPhase.SUCCESS
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getStatusColor(status: FileStatus) {
 | 
					  getStatusColor(status: FileStatus) {
 | 
				
			||||||
    switch (status.phase) {
 | 
					    switch (status.phase) {
 | 
				
			||||||
      case FileStatusPhase.PROCESSING:
 | 
					      case FileStatusPhase.PROCESSING:
 | 
				
			||||||
      case FileStatusPhase.UPLOADING:
 | 
					      case FileStatusPhase.UPLOADING:
 | 
				
			||||||
          return "primary"
 | 
					        return 'primary'
 | 
				
			||||||
      case FileStatusPhase.FAILED:
 | 
					      case FileStatusPhase.FAILED:
 | 
				
			||||||
        return "danger"
 | 
					        return 'danger'
 | 
				
			||||||
      case FileStatusPhase.SUCCESS:
 | 
					      case FileStatusPhase.SUCCESS:
 | 
				
			||||||
        return "success"
 | 
					        return 'success'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -96,20 +109,16 @@ export class UploadFileWidgetComponent implements OnInit {
 | 
				
			|||||||
    this.consumerStatusService.dismissCompleted()
 | 
					    this.consumerStatusService.dismissCompleted()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public fileOver(event){
 | 
					  public fileOver(event) {}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public fileLeave(event){
 | 
					  public fileLeave(event) {}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public dropped(files: NgxFileDropEntry[]) {
 | 
					  public dropped(files: NgxFileDropEntry[]) {
 | 
				
			||||||
    for (const droppedFile of files) {
 | 
					    for (const droppedFile of files) {
 | 
				
			||||||
      if (droppedFile.fileEntry.isFile) {
 | 
					      if (droppedFile.fileEntry.isFile) {
 | 
				
			||||||
 | 
					        const fileEntry = droppedFile.fileEntry as FileSystemFileEntry
 | 
				
			||||||
      const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
 | 
					 | 
				
			||||||
        fileEntry.file((file: File) => {
 | 
					        fileEntry.file((file: File) => {
 | 
				
			||||||
          let formData = new FormData()
 | 
					          let formData = new FormData()
 | 
				
			||||||
          formData.append('document', file, file.name)
 | 
					          formData.append('document', file, file.name)
 | 
				
			||||||
@ -117,29 +126,37 @@ export class UploadFileWidgetComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          status.message = $localize`Connecting...`
 | 
					          status.message = $localize`Connecting...`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          this.documentService.uploadDocument(formData).subscribe(event => {
 | 
					          this.documentService.uploadDocument(formData).subscribe(
 | 
				
			||||||
 | 
					            (event) => {
 | 
				
			||||||
              if (event.type == HttpEventType.UploadProgress) {
 | 
					              if (event.type == HttpEventType.UploadProgress) {
 | 
				
			||||||
              status.updateProgress(FileStatusPhase.UPLOADING, event.loaded, event.total)
 | 
					                status.updateProgress(
 | 
				
			||||||
 | 
					                  FileStatusPhase.UPLOADING,
 | 
				
			||||||
 | 
					                  event.loaded,
 | 
				
			||||||
 | 
					                  event.total
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
                status.message = $localize`Uploading...`
 | 
					                status.message = $localize`Uploading...`
 | 
				
			||||||
              } else if (event.type == HttpEventType.Response) {
 | 
					              } else if (event.type == HttpEventType.Response) {
 | 
				
			||||||
              status.taskId = event.body["task_id"]
 | 
					                status.taskId = event.body['task_id']
 | 
				
			||||||
                status.message = $localize`Upload complete, waiting...`
 | 
					                status.message = $localize`Upload complete, waiting...`
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
          }, error => {
 | 
					            (error) => {
 | 
				
			||||||
              switch (error.status) {
 | 
					              switch (error.status) {
 | 
				
			||||||
                case 400: {
 | 
					                case 400: {
 | 
				
			||||||
                  this.consumerStatusService.fail(status, error.error.document)
 | 
					                  this.consumerStatusService.fail(status, error.error.document)
 | 
				
			||||||
                break;
 | 
					                  break
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                default: {
 | 
					                default: {
 | 
				
			||||||
                this.consumerStatusService.fail(status, $localize`HTTP error: ${error.status} ${error.statusText}`)
 | 
					                  this.consumerStatusService.fail(
 | 
				
			||||||
                break;
 | 
					                    status,
 | 
				
			||||||
 | 
					                    $localize`HTTP error: ${error.status} ${error.statusText}`
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					                  break
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { WelcomeWidgetComponent } from './welcome-widget.component';
 | 
					import { WelcomeWidgetComponent } from './welcome-widget.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('WelcomeWidgetComponent', () => {
 | 
					describe('WelcomeWidgetComponent', () => {
 | 
				
			||||||
  let component: WelcomeWidgetComponent;
 | 
					  let component: WelcomeWidgetComponent
 | 
				
			||||||
  let fixture: ComponentFixture<WelcomeWidgetComponent>;
 | 
					  let fixture: ComponentFixture<WelcomeWidgetComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ WelcomeWidgetComponent ]
 | 
					      declarations: [WelcomeWidgetComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(WelcomeWidgetComponent);
 | 
					    fixture = TestBed.createComponent(WelcomeWidgetComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,12 @@
 | 
				
			|||||||
import { Component, OnInit } from '@angular/core';
 | 
					import { Component, OnInit } from '@angular/core'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-welcome-widget',
 | 
					  selector: 'app-welcome-widget',
 | 
				
			||||||
  templateUrl: './welcome-widget.component.html',
 | 
					  templateUrl: './welcome-widget.component.html',
 | 
				
			||||||
  styleUrls: ['./welcome-widget.component.scss']
 | 
					  styleUrls: ['./welcome-widget.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class WelcomeWidgetComponent implements OnInit {
 | 
					export class WelcomeWidgetComponent implements OnInit {
 | 
				
			||||||
 | 
					  constructor() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() { }
 | 
					  ngOnInit(): void {}
 | 
				
			||||||
 | 
					 | 
				
			||||||
  ngOnInit(): void {
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { WidgetFrameComponent } from './widget-frame.component';
 | 
					import { WidgetFrameComponent } from './widget-frame.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('WidgetFrameComponent', () => {
 | 
					describe('WidgetFrameComponent', () => {
 | 
				
			||||||
  let component: WidgetFrameComponent;
 | 
					  let component: WidgetFrameComponent
 | 
				
			||||||
  let fixture: ComponentFixture<WidgetFrameComponent>;
 | 
					  let fixture: ComponentFixture<WidgetFrameComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ WidgetFrameComponent ]
 | 
					      declarations: [WidgetFrameComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(WidgetFrameComponent);
 | 
					    fixture = TestBed.createComponent(WidgetFrameComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,18 +1,15 @@
 | 
				
			|||||||
import { Component, Input, OnInit } from '@angular/core';
 | 
					import { Component, Input, OnInit } from '@angular/core'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-widget-frame',
 | 
					  selector: 'app-widget-frame',
 | 
				
			||||||
  templateUrl: './widget-frame.component.html',
 | 
					  templateUrl: './widget-frame.component.html',
 | 
				
			||||||
  styleUrls: ['./widget-frame.component.scss']
 | 
					  styleUrls: ['./widget-frame.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class WidgetFrameComponent implements OnInit {
 | 
					export class WidgetFrameComponent implements OnInit {
 | 
				
			||||||
 | 
					  constructor() {}
 | 
				
			||||||
  constructor() { }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  title: string
 | 
					  title: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentAsnComponent } from './document-asn.component';
 | 
					import { DocumentAsnComponent } from './document-asn.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DocumentASNComponentComponent', () => {
 | 
					describe('DocumentASNComponentComponent', () => {
 | 
				
			||||||
  let component: DocumentAsnComponent;
 | 
					  let component: DocumentAsnComponent
 | 
				
			||||||
  let fixture: ComponentFixture<DocumentAsnComponent>;
 | 
					  let fixture: ComponentFixture<DocumentAsnComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ DocumentAsnComponent ]
 | 
					      declarations: [DocumentAsnComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentAsnComponent);
 | 
					    fixture = TestBed.createComponent(DocumentAsnComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,27 +1,27 @@
 | 
				
			|||||||
import { Component, OnInit } from '@angular/core';
 | 
					import { Component, OnInit } from '@angular/core'
 | 
				
			||||||
import {DocumentService} from "../../services/rest/document.service";
 | 
					import { DocumentService } from '../../services/rest/document.service'
 | 
				
			||||||
import {ActivatedRoute, Router} from "@angular/router";
 | 
					import { ActivatedRoute, Router } from '@angular/router'
 | 
				
			||||||
import {FILTER_ASN} from "../../data/filter-rule-type";
 | 
					import { FILTER_ASN } from '../../data/filter-rule-type'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-document-asncomponent',
 | 
					  selector: 'app-document-asncomponent',
 | 
				
			||||||
  templateUrl: './document-asn.component.html',
 | 
					  templateUrl: './document-asn.component.html',
 | 
				
			||||||
  styleUrls: ['./document-asn.component.scss']
 | 
					  styleUrls: ['./document-asn.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DocumentAsnComponent implements OnInit {
 | 
					export class DocumentAsnComponent implements OnInit {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  asn: string
 | 
					  asn: string
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private documentsService: DocumentService,
 | 
					    private documentsService: DocumentService,
 | 
				
			||||||
    private route: ActivatedRoute,
 | 
					    private route: ActivatedRoute,
 | 
				
			||||||
    private router: Router) { }
 | 
					    private router: Router
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
 | 
					    this.route.paramMap.subscribe((paramMap) => {
 | 
				
			||||||
    this.route.paramMap.subscribe(paramMap => {
 | 
					      this.asn = paramMap.get('id')
 | 
				
			||||||
      this.asn = paramMap.get('id');
 | 
					      this.documentsService
 | 
				
			||||||
      this.documentsService.listAllFilteredIds([{rule_type: FILTER_ASN, value: this.asn}]).subscribe(documentId => {
 | 
					        .listAllFilteredIds([{ rule_type: FILTER_ASN, value: this.asn }])
 | 
				
			||||||
 | 
					        .subscribe((documentId) => {
 | 
				
			||||||
          if (documentId.length == 1) {
 | 
					          if (documentId.length == 1) {
 | 
				
			||||||
            this.router.navigate(['documents', documentId[0]])
 | 
					            this.router.navigate(['documents', documentId[0]])
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
@ -29,6 +29,5 @@ export class DocumentAsnComponent implements OnInit {
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentDetailComponent } from './document-detail.component';
 | 
					import { DocumentDetailComponent } from './document-detail.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DocumentDetailComponent', () => {
 | 
					describe('DocumentDetailComponent', () => {
 | 
				
			||||||
  let component: DocumentDetailComponent;
 | 
					  let component: DocumentDetailComponent
 | 
				
			||||||
  let fixture: ComponentFixture<DocumentDetailComponent>;
 | 
					  let fixture: ComponentFixture<DocumentDetailComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ DocumentDetailComponent ]
 | 
					      declarations: [DocumentDetailComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentDetailComponent);
 | 
					    fixture = TestBed.createComponent(DocumentDetailComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,38 +1,55 @@
 | 
				
			|||||||
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { FormControl, FormGroup } from '@angular/forms';
 | 
					  Component,
 | 
				
			||||||
import { ActivatedRoute, Router } from '@angular/router';
 | 
					  OnInit,
 | 
				
			||||||
import { NgbModal, NgbNav } from '@ng-bootstrap/ng-bootstrap';
 | 
					  OnDestroy,
 | 
				
			||||||
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
 | 
					  ViewChild,
 | 
				
			||||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
 | 
					  ElementRef,
 | 
				
			||||||
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata';
 | 
					} from '@angular/core'
 | 
				
			||||||
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
 | 
					import { FormControl, FormGroup } from '@angular/forms'
 | 
				
			||||||
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe';
 | 
					import { ActivatedRoute, Router } from '@angular/router'
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
 | 
					import { NgbModal, NgbNav } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
 | 
					import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
 | 
				
			||||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
 | 
					import { PaperlessDocument } from 'src/app/data/paperless-document'
 | 
				
			||||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
 | 
					import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata'
 | 
				
			||||||
import { DocumentService } from 'src/app/services/rest/document.service';
 | 
					import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
 | 
				
			||||||
import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component';
 | 
					import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
 | 
				
			||||||
import { CorrespondentEditDialogComponent } from '../manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component';
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component';
 | 
					import { OpenDocumentsService } from 'src/app/services/open-documents.service'
 | 
				
			||||||
import { PDFDocumentProxy } from 'ng2-pdf-viewer';
 | 
					import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
 | 
				
			||||||
import { TextComponent } from '../common/input/text/text.component';
 | 
					import { DocumentService } from 'src/app/services/rest/document.service'
 | 
				
			||||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
 | 
					import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component'
 | 
				
			||||||
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms';
 | 
					import { CorrespondentEditDialogComponent } from '../manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'
 | 
				
			||||||
import { Observable, Subject, BehaviorSubject } from 'rxjs';
 | 
					import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'
 | 
				
			||||||
import { first, takeUntil, switchMap, map, debounceTime, distinctUntilChanged } from 'rxjs/operators';
 | 
					import { PDFDocumentProxy } from 'ng2-pdf-viewer'
 | 
				
			||||||
import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions';
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type';
 | 
					import { TextComponent } from '../common/input/text/text.component'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  SettingsService,
 | 
				
			||||||
 | 
					  SETTINGS_KEYS,
 | 
				
			||||||
 | 
					} from 'src/app/services/settings.service'
 | 
				
			||||||
 | 
					import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'
 | 
				
			||||||
 | 
					import { Observable, Subject, BehaviorSubject } from 'rxjs'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  first,
 | 
				
			||||||
 | 
					  takeUntil,
 | 
				
			||||||
 | 
					  switchMap,
 | 
				
			||||||
 | 
					  map,
 | 
				
			||||||
 | 
					  debounceTime,
 | 
				
			||||||
 | 
					  distinctUntilChanged,
 | 
				
			||||||
 | 
					} from 'rxjs/operators'
 | 
				
			||||||
 | 
					import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions'
 | 
				
			||||||
 | 
					import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-document-detail',
 | 
					  selector: 'app-document-detail',
 | 
				
			||||||
  templateUrl: './document-detail.component.html',
 | 
					  templateUrl: './document-detail.component.html',
 | 
				
			||||||
  styleUrls: ['./document-detail.component.scss']
 | 
					  styleUrls: ['./document-detail.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponent {
 | 
					export class DocumentDetailComponent
 | 
				
			||||||
 | 
					  implements OnInit, OnDestroy, DirtyComponent
 | 
				
			||||||
  @ViewChild("inputTitle")
 | 
					{
 | 
				
			||||||
 | 
					  @ViewChild('inputTitle')
 | 
				
			||||||
  titleInput: TextComponent
 | 
					  titleInput: TextComponent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  expandOriginalMetadata = false
 | 
					  expandOriginalMetadata = false
 | 
				
			||||||
@ -63,7 +80,7 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
 | 
				
			|||||||
    correspondent: new FormControl(),
 | 
					    correspondent: new FormControl(),
 | 
				
			||||||
    document_type: new FormControl(),
 | 
					    document_type: new FormControl(),
 | 
				
			||||||
    archive_serial_number: new FormControl(),
 | 
					    archive_serial_number: new FormControl(),
 | 
				
			||||||
    tags: new FormControl([])
 | 
					    tags: new FormControl([]),
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  previewCurrentPage: number = 1
 | 
					  previewCurrentPage: number = 1
 | 
				
			||||||
@ -76,8 +93,13 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
 | 
				
			|||||||
  @ViewChild('nav') nav: NgbNav
 | 
					  @ViewChild('nav') nav: NgbNav
 | 
				
			||||||
  @ViewChild('pdfPreview') set pdfPreview(element) {
 | 
					  @ViewChild('pdfPreview') set pdfPreview(element) {
 | 
				
			||||||
    // this gets called when compontent added or removed from DOM
 | 
					    // this gets called when compontent added or removed from DOM
 | 
				
			||||||
    if (element && element.nativeElement.offsetParent !== null && this.nav?.activeId == 4) { // its visible
 | 
					    if (
 | 
				
			||||||
      setTimeout(()=> this.nav?.select(1));
 | 
					      element &&
 | 
				
			||||||
 | 
					      element.nativeElement.offsetParent !== null &&
 | 
				
			||||||
 | 
					      this.nav?.activeId == 4
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      // its visible
 | 
				
			||||||
 | 
					      setTimeout(() => this.nav?.select(1))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -92,14 +114,17 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
 | 
				
			|||||||
    private documentListViewService: DocumentListViewService,
 | 
					    private documentListViewService: DocumentListViewService,
 | 
				
			||||||
    private documentTitlePipe: DocumentTitlePipe,
 | 
					    private documentTitlePipe: DocumentTitlePipe,
 | 
				
			||||||
    private toastService: ToastService,
 | 
					    private toastService: ToastService,
 | 
				
			||||||
    private settings: SettingsService) {
 | 
					    private settings: SettingsService
 | 
				
			||||||
      this.titleSubject.pipe(
 | 
					  ) {
 | 
				
			||||||
 | 
					    this.titleSubject
 | 
				
			||||||
 | 
					      .pipe(
 | 
				
			||||||
        debounceTime(1000),
 | 
					        debounceTime(1000),
 | 
				
			||||||
        distinctUntilChanged(),
 | 
					        distinctUntilChanged(),
 | 
				
			||||||
        takeUntil(this.unsubscribeNotifier)
 | 
					        takeUntil(this.unsubscribeNotifier)
 | 
				
			||||||
      ).subscribe(titleValue => {
 | 
					      )
 | 
				
			||||||
 | 
					      .subscribe((titleValue) => {
 | 
				
			||||||
        this.title = titleValue
 | 
					        this.title = titleValue
 | 
				
			||||||
        this.documentForm.patchValue({'title': titleValue})
 | 
					        this.documentForm.patchValue({ title: titleValue })
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -112,28 +137,50 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getContentType() {
 | 
					  getContentType() {
 | 
				
			||||||
    return this.metadata?.has_archive_version ? 'application/pdf' : this.metadata?.original_mime_type
 | 
					    return this.metadata?.has_archive_version
 | 
				
			||||||
 | 
					      ? 'application/pdf'
 | 
				
			||||||
 | 
					      : this.metadata?.original_mime_type
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.documentForm.valueChanges.pipe(takeUntil(this.unsubscribeNotifier)).subscribe(wow => {
 | 
					    this.documentForm.valueChanges
 | 
				
			||||||
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
 | 
					      .subscribe((wow) => {
 | 
				
			||||||
        Object.assign(this.document, this.documentForm.value)
 | 
					        Object.assign(this.document, this.documentForm.value)
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.correspondentService.listAll().pipe(first()).subscribe(result => this.correspondents = result.results)
 | 
					    this.correspondentService
 | 
				
			||||||
    this.documentTypeService.listAll().pipe(first()).subscribe(result => this.documentTypes = result.results)
 | 
					      .listAll()
 | 
				
			||||||
 | 
					      .pipe(first())
 | 
				
			||||||
 | 
					      .subscribe((result) => (this.correspondents = result.results))
 | 
				
			||||||
 | 
					    this.documentTypeService
 | 
				
			||||||
 | 
					      .listAll()
 | 
				
			||||||
 | 
					      .pipe(first())
 | 
				
			||||||
 | 
					      .subscribe((result) => (this.documentTypes = result.results))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.route.paramMap.pipe(switchMap(paramMap => {
 | 
					    this.route.paramMap
 | 
				
			||||||
 | 
					      .pipe(
 | 
				
			||||||
 | 
					        switchMap((paramMap) => {
 | 
				
			||||||
          const documentId = +paramMap.get('id')
 | 
					          const documentId = +paramMap.get('id')
 | 
				
			||||||
          return this.documentsService.get(documentId)
 | 
					          return this.documentsService.get(documentId)
 | 
				
			||||||
    })).pipe(switchMap((doc) => {
 | 
					        })
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      .pipe(
 | 
				
			||||||
 | 
					        switchMap((doc) => {
 | 
				
			||||||
          this.documentId = doc.id
 | 
					          this.documentId = doc.id
 | 
				
			||||||
          this.previewUrl = this.documentsService.getPreviewUrl(this.documentId)
 | 
					          this.previewUrl = this.documentsService.getPreviewUrl(this.documentId)
 | 
				
			||||||
      this.downloadUrl = this.documentsService.getDownloadUrl(this.documentId)
 | 
					          this.downloadUrl = this.documentsService.getDownloadUrl(
 | 
				
			||||||
      this.downloadOriginalUrl = this.documentsService.getDownloadUrl(this.documentId, true)
 | 
					            this.documentId
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          this.downloadOriginalUrl = this.documentsService.getDownloadUrl(
 | 
				
			||||||
 | 
					            this.documentId,
 | 
				
			||||||
 | 
					            true
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
          this.suggestions = null
 | 
					          this.suggestions = null
 | 
				
			||||||
          if (this.openDocumentService.getOpenDocument(this.documentId)) {
 | 
					          if (this.openDocumentService.getOpenDocument(this.documentId)) {
 | 
				
			||||||
        this.updateComponent(this.openDocumentService.getOpenDocument(this.documentId))
 | 
					            this.updateComponent(
 | 
				
			||||||
 | 
					              this.openDocumentService.getOpenDocument(this.documentId)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            this.openDocumentService.openDocument(doc)
 | 
					            this.openDocumentService.openDocument(doc)
 | 
				
			||||||
            this.updateComponent(doc)
 | 
					            this.updateComponent(doc)
 | 
				
			||||||
@ -147,116 +194,190 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
 | 
				
			|||||||
            correspondent: doc.correspondent,
 | 
					            correspondent: doc.correspondent,
 | 
				
			||||||
            document_type: doc.document_type,
 | 
					            document_type: doc.document_type,
 | 
				
			||||||
            archive_serial_number: doc.archive_serial_number,
 | 
					            archive_serial_number: doc.archive_serial_number,
 | 
				
			||||||
        tags: [...doc.tags]
 | 
					            tags: [...doc.tags],
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.isDirty$ = dirtyCheck(this.documentForm, this.store.asObservable())
 | 
					          this.isDirty$ = dirtyCheck(
 | 
				
			||||||
 | 
					            this.documentForm,
 | 
				
			||||||
 | 
					            this.store.asObservable()
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return this.isDirty$.pipe(map(dirty => ({doc, dirty})))
 | 
					          return this.isDirty$.pipe(map((dirty) => ({ doc, dirty })))
 | 
				
			||||||
    }))
 | 
					        })
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
    .subscribe(({doc, dirty}) => {
 | 
					      .subscribe(
 | 
				
			||||||
 | 
					        ({ doc, dirty }) => {
 | 
				
			||||||
          this.openDocumentService.setDirty(doc.id, dirty)
 | 
					          this.openDocumentService.setDirty(doc.id, dirty)
 | 
				
			||||||
    }, error => {this.router.navigate(['404'])})
 | 
					        },
 | 
				
			||||||
 | 
					        (error) => {
 | 
				
			||||||
 | 
					          this.router.navigate(['404'])
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnDestroy() : void {
 | 
					  ngOnDestroy(): void {
 | 
				
			||||||
    this.unsubscribeNotifier.next();
 | 
					    this.unsubscribeNotifier.next()
 | 
				
			||||||
    this.unsubscribeNotifier.complete();
 | 
					    this.unsubscribeNotifier.complete()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  updateComponent(doc: PaperlessDocument) {
 | 
					  updateComponent(doc: PaperlessDocument) {
 | 
				
			||||||
    this.document = doc
 | 
					    this.document = doc
 | 
				
			||||||
    this.documentsService.getMetadata(doc.id).pipe(first()).subscribe(result => {
 | 
					    this.documentsService
 | 
				
			||||||
 | 
					      .getMetadata(doc.id)
 | 
				
			||||||
 | 
					      .pipe(first())
 | 
				
			||||||
 | 
					      .subscribe(
 | 
				
			||||||
 | 
					        (result) => {
 | 
				
			||||||
          this.metadata = result
 | 
					          this.metadata = result
 | 
				
			||||||
    }, error => {
 | 
					        },
 | 
				
			||||||
 | 
					        (error) => {
 | 
				
			||||||
          this.metadata = null
 | 
					          this.metadata = null
 | 
				
			||||||
    })
 | 
					        }
 | 
				
			||||||
    this.documentsService.getSuggestions(doc.id).pipe(first()).subscribe(result => {
 | 
					      )
 | 
				
			||||||
 | 
					    this.documentsService
 | 
				
			||||||
 | 
					      .getSuggestions(doc.id)
 | 
				
			||||||
 | 
					      .pipe(first())
 | 
				
			||||||
 | 
					      .subscribe(
 | 
				
			||||||
 | 
					        (result) => {
 | 
				
			||||||
          this.suggestions = result
 | 
					          this.suggestions = result
 | 
				
			||||||
    }, error => {
 | 
					        },
 | 
				
			||||||
 | 
					        (error) => {
 | 
				
			||||||
          this.suggestions = null
 | 
					          this.suggestions = null
 | 
				
			||||||
    })
 | 
					        }
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    this.title = this.documentTitlePipe.transform(doc.title)
 | 
					    this.title = this.documentTitlePipe.transform(doc.title)
 | 
				
			||||||
    this.documentForm.patchValue(doc)
 | 
					    this.documentForm.patchValue(doc)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  createDocumentType(newName: string) {
 | 
					  createDocumentType(newName: string) {
 | 
				
			||||||
    var modal = this.modalService.open(DocumentTypeEditDialogComponent, {backdrop: 'static'})
 | 
					    var modal = this.modalService.open(DocumentTypeEditDialogComponent, {
 | 
				
			||||||
 | 
					      backdrop: 'static',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    modal.componentInstance.dialogMode = 'create'
 | 
					    modal.componentInstance.dialogMode = 'create'
 | 
				
			||||||
    if (newName) modal.componentInstance.object = { name: newName }
 | 
					    if (newName) modal.componentInstance.object = { name: newName }
 | 
				
			||||||
    modal.componentInstance.success.pipe(switchMap(newDocumentType => {
 | 
					    modal.componentInstance.success
 | 
				
			||||||
      return this.documentTypeService.listAll().pipe(map(documentTypes => ({newDocumentType, documentTypes})))
 | 
					      .pipe(
 | 
				
			||||||
    }))
 | 
					        switchMap((newDocumentType) => {
 | 
				
			||||||
 | 
					          return this.documentTypeService
 | 
				
			||||||
 | 
					            .listAll()
 | 
				
			||||||
 | 
					            .pipe(map((documentTypes) => ({ newDocumentType, documentTypes })))
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
    .subscribe(({newDocumentType, documentTypes}) => {
 | 
					      .subscribe(({ newDocumentType, documentTypes }) => {
 | 
				
			||||||
        this.documentTypes = documentTypes.results
 | 
					        this.documentTypes = documentTypes.results
 | 
				
			||||||
        this.documentForm.get('document_type').setValue(newDocumentType.id)
 | 
					        this.documentForm.get('document_type').setValue(newDocumentType.id)
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  createCorrespondent(newName: string) {
 | 
					  createCorrespondent(newName: string) {
 | 
				
			||||||
    var modal = this.modalService.open(CorrespondentEditDialogComponent, {backdrop: 'static'})
 | 
					    var modal = this.modalService.open(CorrespondentEditDialogComponent, {
 | 
				
			||||||
 | 
					      backdrop: 'static',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    modal.componentInstance.dialogMode = 'create'
 | 
					    modal.componentInstance.dialogMode = 'create'
 | 
				
			||||||
    if (newName) modal.componentInstance.object = { name: newName }
 | 
					    if (newName) modal.componentInstance.object = { name: newName }
 | 
				
			||||||
    modal.componentInstance.success.pipe(switchMap(newCorrespondent => {
 | 
					    modal.componentInstance.success
 | 
				
			||||||
      return this.correspondentService.listAll().pipe(map(correspondents => ({newCorrespondent, correspondents})))
 | 
					      .pipe(
 | 
				
			||||||
    }))
 | 
					        switchMap((newCorrespondent) => {
 | 
				
			||||||
 | 
					          return this.correspondentService
 | 
				
			||||||
 | 
					            .listAll()
 | 
				
			||||||
 | 
					            .pipe(
 | 
				
			||||||
 | 
					              map((correspondents) => ({ newCorrespondent, correspondents }))
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
    .subscribe(({newCorrespondent, correspondents}) => {
 | 
					      .subscribe(({ newCorrespondent, correspondents }) => {
 | 
				
			||||||
        this.correspondents = correspondents.results
 | 
					        this.correspondents = correspondents.results
 | 
				
			||||||
        this.documentForm.get('correspondent').setValue(newCorrespondent.id)
 | 
					        this.documentForm.get('correspondent').setValue(newCorrespondent.id)
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  discard() {
 | 
					  discard() {
 | 
				
			||||||
    this.documentsService.get(this.documentId).pipe(first()).subscribe(doc => {
 | 
					    this.documentsService
 | 
				
			||||||
 | 
					      .get(this.documentId)
 | 
				
			||||||
 | 
					      .pipe(first())
 | 
				
			||||||
 | 
					      .subscribe(
 | 
				
			||||||
 | 
					        (doc) => {
 | 
				
			||||||
          Object.assign(this.document, doc)
 | 
					          Object.assign(this.document, doc)
 | 
				
			||||||
          this.title = doc.title
 | 
					          this.title = doc.title
 | 
				
			||||||
          this.documentForm.patchValue(doc)
 | 
					          this.documentForm.patchValue(doc)
 | 
				
			||||||
    }, error => {this.router.navigate(['404'])})
 | 
					        },
 | 
				
			||||||
 | 
					        (error) => {
 | 
				
			||||||
 | 
					          this.router.navigate(['404'])
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  save() {
 | 
					  save() {
 | 
				
			||||||
    this.networkActive = true
 | 
					    this.networkActive = true
 | 
				
			||||||
    this.store.next(this.documentForm.value)
 | 
					    this.store.next(this.documentForm.value)
 | 
				
			||||||
    this.documentsService.update(this.document).pipe(first()).subscribe(result => {
 | 
					    this.documentsService
 | 
				
			||||||
 | 
					      .update(this.document)
 | 
				
			||||||
 | 
					      .pipe(first())
 | 
				
			||||||
 | 
					      .subscribe(
 | 
				
			||||||
 | 
					        (result) => {
 | 
				
			||||||
          this.close()
 | 
					          this.close()
 | 
				
			||||||
          this.networkActive = false
 | 
					          this.networkActive = false
 | 
				
			||||||
          this.error = null
 | 
					          this.error = null
 | 
				
			||||||
    }, error => {
 | 
					        },
 | 
				
			||||||
 | 
					        (error) => {
 | 
				
			||||||
          this.networkActive = false
 | 
					          this.networkActive = false
 | 
				
			||||||
          this.error = error.error
 | 
					          this.error = error.error
 | 
				
			||||||
    })
 | 
					        }
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  saveEditNext() {
 | 
					  saveEditNext() {
 | 
				
			||||||
    this.networkActive = true
 | 
					    this.networkActive = true
 | 
				
			||||||
    this.store.next(this.documentForm.value)
 | 
					    this.store.next(this.documentForm.value)
 | 
				
			||||||
    this.documentsService.update(this.document).pipe(switchMap(updateResult => {
 | 
					    this.documentsService
 | 
				
			||||||
      return this.documentListViewService.getNext(this.documentId).pipe(map(nextDocId => ({nextDocId, updateResult})))
 | 
					      .update(this.document)
 | 
				
			||||||
    })).pipe(switchMap(({nextDocId, updateResult}) => {
 | 
					      .pipe(
 | 
				
			||||||
      if (nextDocId && updateResult) return this.openDocumentService.closeDocument(this.document).pipe(map(closeResult => ({updateResult, nextDocId, closeResult})))
 | 
					        switchMap((updateResult) => {
 | 
				
			||||||
    }))
 | 
					          return this.documentListViewService
 | 
				
			||||||
 | 
					            .getNext(this.documentId)
 | 
				
			||||||
 | 
					            .pipe(map((nextDocId) => ({ nextDocId, updateResult })))
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      .pipe(
 | 
				
			||||||
 | 
					        switchMap(({ nextDocId, updateResult }) => {
 | 
				
			||||||
 | 
					          if (nextDocId && updateResult)
 | 
				
			||||||
 | 
					            return this.openDocumentService
 | 
				
			||||||
 | 
					              .closeDocument(this.document)
 | 
				
			||||||
 | 
					              .pipe(
 | 
				
			||||||
 | 
					                map((closeResult) => ({ updateResult, nextDocId, closeResult }))
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
      .pipe(first())
 | 
					      .pipe(first())
 | 
				
			||||||
    .subscribe(({updateResult, nextDocId, closeResult}) => {
 | 
					      .subscribe(
 | 
				
			||||||
 | 
					        ({ updateResult, nextDocId, closeResult }) => {
 | 
				
			||||||
          this.error = null
 | 
					          this.error = null
 | 
				
			||||||
          this.networkActive = false
 | 
					          this.networkActive = false
 | 
				
			||||||
          if (closeResult && updateResult && nextDocId) {
 | 
					          if (closeResult && updateResult && nextDocId) {
 | 
				
			||||||
            this.router.navigate(['documents', nextDocId])
 | 
					            this.router.navigate(['documents', nextDocId])
 | 
				
			||||||
            this.titleInput?.focus()
 | 
					            this.titleInput?.focus()
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
    }, error => {
 | 
					        },
 | 
				
			||||||
 | 
					        (error) => {
 | 
				
			||||||
          this.networkActive = false
 | 
					          this.networkActive = false
 | 
				
			||||||
          this.error = error.error
 | 
					          this.error = error.error
 | 
				
			||||||
    })
 | 
					        }
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  close() {
 | 
					  close() {
 | 
				
			||||||
    this.openDocumentService.closeDocument(this.document).pipe(first()).subscribe(closed => {
 | 
					    this.openDocumentService
 | 
				
			||||||
      if (!closed) return;
 | 
					      .closeDocument(this.document)
 | 
				
			||||||
 | 
					      .pipe(first())
 | 
				
			||||||
 | 
					      .subscribe((closed) => {
 | 
				
			||||||
 | 
					        if (!closed) return
 | 
				
			||||||
        if (this.documentListViewService.activeSavedViewId) {
 | 
					        if (this.documentListViewService.activeSavedViewId) {
 | 
				
			||||||
        this.router.navigate(['view', this.documentListViewService.activeSavedViewId])
 | 
					          this.router.navigate([
 | 
				
			||||||
 | 
					            'view',
 | 
				
			||||||
 | 
					            this.documentListViewService.activeSavedViewId,
 | 
				
			||||||
 | 
					          ])
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          this.router.navigate(['documents'])
 | 
					          this.router.navigate(['documents'])
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -264,28 +385,43 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  delete() {
 | 
					  delete() {
 | 
				
			||||||
    let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
 | 
					    let modal = this.modalService.open(ConfirmDialogComponent, {
 | 
				
			||||||
 | 
					      backdrop: 'static',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    modal.componentInstance.title = $localize`Confirm delete`
 | 
					    modal.componentInstance.title = $localize`Confirm delete`
 | 
				
			||||||
    modal.componentInstance.messageBold = $localize`Do you really want to delete document "${this.document.title}"?`
 | 
					    modal.componentInstance.messageBold = $localize`Do you really want to delete document "${this.document.title}"?`
 | 
				
			||||||
    modal.componentInstance.message = $localize`The files for this document will be deleted permanently. This operation cannot be undone.`
 | 
					    modal.componentInstance.message = $localize`The files for this document will be deleted permanently. This operation cannot be undone.`
 | 
				
			||||||
    modal.componentInstance.btnClass = "btn-danger"
 | 
					    modal.componentInstance.btnClass = 'btn-danger'
 | 
				
			||||||
    modal.componentInstance.btnCaption = $localize`Delete document`
 | 
					    modal.componentInstance.btnCaption = $localize`Delete document`
 | 
				
			||||||
    modal.componentInstance.confirmClicked.pipe(switchMap(() => {
 | 
					    modal.componentInstance.confirmClicked
 | 
				
			||||||
 | 
					      .pipe(
 | 
				
			||||||
 | 
					        switchMap(() => {
 | 
				
			||||||
          modal.componentInstance.buttonsEnabled = false
 | 
					          modal.componentInstance.buttonsEnabled = false
 | 
				
			||||||
          return this.documentsService.delete(this.document)
 | 
					          return this.documentsService.delete(this.document)
 | 
				
			||||||
    }))
 | 
					        })
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
    .subscribe(() => {
 | 
					      .subscribe(
 | 
				
			||||||
 | 
					        () => {
 | 
				
			||||||
          modal.close()
 | 
					          modal.close()
 | 
				
			||||||
          this.close()
 | 
					          this.close()
 | 
				
			||||||
    }, error => {
 | 
					        },
 | 
				
			||||||
      this.toastService.showError($localize`Error deleting document: ${JSON.stringify(error)}`)
 | 
					        (error) => {
 | 
				
			||||||
 | 
					          this.toastService.showError(
 | 
				
			||||||
 | 
					            $localize`Error deleting document: ${JSON.stringify(error)}`
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
          modal.componentInstance.buttonsEnabled = true
 | 
					          modal.componentInstance.buttonsEnabled = true
 | 
				
			||||||
    })
 | 
					        }
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  moreLike() {
 | 
					  moreLike() {
 | 
				
			||||||
    this.documentListViewService.quickFilter([{rule_type: FILTER_FULLTEXT_MORELIKE, value: this.documentId.toString()}])
 | 
					    this.documentListViewService.quickFilter([
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        rule_type: FILTER_FULLTEXT_MORELIKE,
 | 
				
			||||||
 | 
					        value: this.documentId.toString(),
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  hasNext() {
 | 
					  hasNext() {
 | 
				
			||||||
@ -297,13 +433,17 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  nextDoc() {
 | 
					  nextDoc() {
 | 
				
			||||||
    this.documentListViewService.getNext(this.document.id).subscribe((nextDocId: number) => {
 | 
					    this.documentListViewService
 | 
				
			||||||
 | 
					      .getNext(this.document.id)
 | 
				
			||||||
 | 
					      .subscribe((nextDocId: number) => {
 | 
				
			||||||
        this.router.navigate(['documents', nextDocId])
 | 
					        this.router.navigate(['documents', nextDocId])
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  previousDoc () {
 | 
					  previousDoc() {
 | 
				
			||||||
    this.documentListViewService.getPrevious(this.document.id).subscribe((prevDocId: number) => {
 | 
					    this.documentListViewService
 | 
				
			||||||
 | 
					      .getPrevious(this.document.id)
 | 
				
			||||||
 | 
					      .subscribe((prevDocId: number) => {
 | 
				
			||||||
        this.router.navigate(['documents', prevDocId])
 | 
					        this.router.navigate(['documents', prevDocId])
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -311,5 +451,4 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen
 | 
				
			|||||||
  pdfPreviewLoaded(pdf: PDFDocumentProxy) {
 | 
					  pdfPreviewLoaded(pdf: PDFDocumentProxy) {
 | 
				
			||||||
    this.previewNumPages = pdf.numPages
 | 
					    this.previewNumPages = pdf.numPages
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { MetadataCollapseComponent } from './metadata-collapse.component';
 | 
					import { MetadataCollapseComponent } from './metadata-collapse.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('MetadataCollapseComponent', () => {
 | 
					describe('MetadataCollapseComponent', () => {
 | 
				
			||||||
  let component: MetadataCollapseComponent;
 | 
					  let component: MetadataCollapseComponent
 | 
				
			||||||
  let fixture: ComponentFixture<MetadataCollapseComponent>;
 | 
					  let fixture: ComponentFixture<MetadataCollapseComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ MetadataCollapseComponent ]
 | 
					      declarations: [MetadataCollapseComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(MetadataCollapseComponent);
 | 
					    fixture = TestBed.createComponent(MetadataCollapseComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,12 @@
 | 
				
			|||||||
import { Component, Input, OnInit } from '@angular/core';
 | 
					import { Component, Input, OnInit } from '@angular/core'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-metadata-collapse',
 | 
					  selector: 'app-metadata-collapse',
 | 
				
			||||||
  templateUrl: './metadata-collapse.component.html',
 | 
					  templateUrl: './metadata-collapse.component.html',
 | 
				
			||||||
  styleUrls: ['./metadata-collapse.component.scss']
 | 
					  styleUrls: ['./metadata-collapse.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class MetadataCollapseComponent implements OnInit {
 | 
					export class MetadataCollapseComponent implements OnInit {
 | 
				
			||||||
 | 
					  constructor() {}
 | 
				
			||||||
  constructor() { }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  expand = false
 | 
					  expand = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -17,7 +16,5 @@ export class MetadataCollapseComponent implements OnInit {
 | 
				
			|||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  title = $localize`Metadata`
 | 
					  title = $localize`Metadata`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { BulkEditorComponent } from './bulk-editor.component';
 | 
					import { BulkEditorComponent } from './bulk-editor.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('BulkEditorComponent', () => {
 | 
					describe('BulkEditorComponent', () => {
 | 
				
			||||||
  let component: BulkEditorComponent;
 | 
					  let component: BulkEditorComponent
 | 
				
			||||||
  let fixture: ComponentFixture<BulkEditorComponent>;
 | 
					  let fixture: ComponentFixture<BulkEditorComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ BulkEditorComponent ]
 | 
					      declarations: [BulkEditorComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(BulkEditorComponent);
 | 
					    fixture = TestBed.createComponent(BulkEditorComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,29 +1,37 @@
 | 
				
			|||||||
import { Component } from '@angular/core';
 | 
					import { Component } from '@angular/core'
 | 
				
			||||||
import { PaperlessTag } from 'src/app/data/paperless-tag';
 | 
					import { PaperlessTag } from 'src/app/data/paperless-tag'
 | 
				
			||||||
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
 | 
					import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
 | 
				
			||||||
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
 | 
					import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
 | 
				
			||||||
import { TagService } from 'src/app/services/rest/tag.service';
 | 
					import { TagService } from 'src/app/services/rest/tag.service'
 | 
				
			||||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
 | 
					import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
 | 
				
			||||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
 | 
					import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { DocumentService, SelectionDataItem } from 'src/app/services/rest/document.service';
 | 
					import {
 | 
				
			||||||
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
 | 
					  DocumentService,
 | 
				
			||||||
import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog/confirm-dialog.component';
 | 
					  SelectionDataItem,
 | 
				
			||||||
import { ChangedItems, FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component';
 | 
					} from 'src/app/services/rest/document.service'
 | 
				
			||||||
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component';
 | 
					import { OpenDocumentsService } from 'src/app/services/open-documents.service'
 | 
				
			||||||
import { MatchingModel } from 'src/app/data/matching-model';
 | 
					import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog/confirm-dialog.component'
 | 
				
			||||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
 | 
					import {
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					  ChangedItems,
 | 
				
			||||||
import { saveAs } from 'file-saver';
 | 
					  FilterableDropdownSelectionModel,
 | 
				
			||||||
 | 
					} from '../../common/filterable-dropdown/filterable-dropdown.component'
 | 
				
			||||||
 | 
					import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
 | 
				
			||||||
 | 
					import { MatchingModel } from 'src/app/data/matching-model'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  SettingsService,
 | 
				
			||||||
 | 
					  SETTINGS_KEYS,
 | 
				
			||||||
 | 
					} from 'src/app/services/settings.service'
 | 
				
			||||||
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
 | 
					import { saveAs } from 'file-saver'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-bulk-editor',
 | 
					  selector: 'app-bulk-editor',
 | 
				
			||||||
  templateUrl: './bulk-editor.component.html',
 | 
					  templateUrl: './bulk-editor.component.html',
 | 
				
			||||||
  styleUrls: ['./bulk-editor.component.scss']
 | 
					  styleUrls: ['./bulk-editor.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class BulkEditorComponent {
 | 
					export class BulkEditorComponent {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  tags: PaperlessTag[]
 | 
					  tags: PaperlessTag[]
 | 
				
			||||||
  correspondents: PaperlessCorrespondent[]
 | 
					  correspondents: PaperlessCorrespondent[]
 | 
				
			||||||
  documentTypes: PaperlessDocumentType[]
 | 
					  documentTypes: PaperlessDocumentType[]
 | 
				
			||||||
@ -42,43 +50,63 @@ export class BulkEditorComponent {
 | 
				
			|||||||
    private openDocumentService: OpenDocumentsService,
 | 
					    private openDocumentService: OpenDocumentsService,
 | 
				
			||||||
    private settings: SettingsService,
 | 
					    private settings: SettingsService,
 | 
				
			||||||
    private toastService: ToastService
 | 
					    private toastService: ToastService
 | 
				
			||||||
  ) { }
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  applyOnClose: boolean = this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE)
 | 
					  applyOnClose: boolean = this.settings.get(
 | 
				
			||||||
  showConfirmationDialogs: boolean = this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS)
 | 
					    SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  showConfirmationDialogs: boolean = this.settings.get(
 | 
				
			||||||
 | 
					    SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit() {
 | 
					  ngOnInit() {
 | 
				
			||||||
    this.tagService.listAll().subscribe(result => this.tags = result.results)
 | 
					    this.tagService
 | 
				
			||||||
    this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
 | 
					      .listAll()
 | 
				
			||||||
    this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
 | 
					      .subscribe((result) => (this.tags = result.results))
 | 
				
			||||||
 | 
					    this.correspondentService
 | 
				
			||||||
 | 
					      .listAll()
 | 
				
			||||||
 | 
					      .subscribe((result) => (this.correspondents = result.results))
 | 
				
			||||||
 | 
					    this.documentTypeService
 | 
				
			||||||
 | 
					      .listAll()
 | 
				
			||||||
 | 
					      .subscribe((result) => (this.documentTypes = result.results))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private executeBulkOperation(modal, method: string, args) {
 | 
					  private executeBulkOperation(modal, method: string, args) {
 | 
				
			||||||
    if (modal) {
 | 
					    if (modal) {
 | 
				
			||||||
      modal.componentInstance.buttonsEnabled = false
 | 
					      modal.componentInstance.buttonsEnabled = false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.documentService.bulkEdit(Array.from(this.list.selected), method, args).subscribe(
 | 
					    this.documentService
 | 
				
			||||||
      response => {
 | 
					      .bulkEdit(Array.from(this.list.selected), method, args)
 | 
				
			||||||
 | 
					      .subscribe(
 | 
				
			||||||
 | 
					        (response) => {
 | 
				
			||||||
          this.list.reload()
 | 
					          this.list.reload()
 | 
				
			||||||
          this.list.reduceSelectionToFilter()
 | 
					          this.list.reduceSelectionToFilter()
 | 
				
			||||||
        this.list.selected.forEach(id => {
 | 
					          this.list.selected.forEach((id) => {
 | 
				
			||||||
            this.openDocumentService.refreshDocument(id)
 | 
					            this.openDocumentService.refreshDocument(id)
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
          if (modal) {
 | 
					          if (modal) {
 | 
				
			||||||
            modal.close()
 | 
					            modal.close()
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
      }, error => {
 | 
					        },
 | 
				
			||||||
 | 
					        (error) => {
 | 
				
			||||||
          if (modal) {
 | 
					          if (modal) {
 | 
				
			||||||
            modal.componentInstance.buttonsEnabled = true
 | 
					            modal.componentInstance.buttonsEnabled = true
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        this.toastService.showError($localize`Error executing bulk operation: ${JSON.stringify(error.error)}`)
 | 
					          this.toastService.showError(
 | 
				
			||||||
 | 
					            $localize`Error executing bulk operation: ${JSON.stringify(
 | 
				
			||||||
 | 
					              error.error
 | 
				
			||||||
 | 
					            )}`
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private applySelectionData(items: SelectionDataItem[], selectionModel: FilterableDropdownSelectionModel) {
 | 
					  private applySelectionData(
 | 
				
			||||||
 | 
					    items: SelectionDataItem[],
 | 
				
			||||||
 | 
					    selectionModel: FilterableDropdownSelectionModel
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
    let selectionData = new Map<number, ToggleableItemState>()
 | 
					    let selectionData = new Map<number, ToggleableItemState>()
 | 
				
			||||||
    items.forEach(i => {
 | 
					    items.forEach((i) => {
 | 
				
			||||||
      if (i.document_count == this.list.selected.size) {
 | 
					      if (i.document_count == this.list.selected.size) {
 | 
				
			||||||
        selectionData.set(i.id, ToggleableItemState.Selected)
 | 
					        selectionData.set(i.id, ToggleableItemState.Selected)
 | 
				
			||||||
      } else if (i.document_count > 0) {
 | 
					      } else if (i.document_count > 0) {
 | 
				
			||||||
@ -89,129 +117,210 @@ export class BulkEditorComponent {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  openTagsDropdown() {
 | 
					  openTagsDropdown() {
 | 
				
			||||||
    this.documentService.getSelectionData(Array.from(this.list.selected)).subscribe(s => {
 | 
					    this.documentService
 | 
				
			||||||
 | 
					      .getSelectionData(Array.from(this.list.selected))
 | 
				
			||||||
 | 
					      .subscribe((s) => {
 | 
				
			||||||
        this.applySelectionData(s.selected_tags, this.tagSelectionModel)
 | 
					        this.applySelectionData(s.selected_tags, this.tagSelectionModel)
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  openDocumentTypeDropdown() {
 | 
					  openDocumentTypeDropdown() {
 | 
				
			||||||
    this.documentService.getSelectionData(Array.from(this.list.selected)).subscribe(s => {
 | 
					    this.documentService
 | 
				
			||||||
      this.applySelectionData(s.selected_document_types, this.documentTypeSelectionModel)
 | 
					      .getSelectionData(Array.from(this.list.selected))
 | 
				
			||||||
 | 
					      .subscribe((s) => {
 | 
				
			||||||
 | 
					        this.applySelectionData(
 | 
				
			||||||
 | 
					          s.selected_document_types,
 | 
				
			||||||
 | 
					          this.documentTypeSelectionModel
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  openCorrespondentDropdown() {
 | 
					  openCorrespondentDropdown() {
 | 
				
			||||||
    this.documentService.getSelectionData(Array.from(this.list.selected)).subscribe(s => {
 | 
					    this.documentService
 | 
				
			||||||
      this.applySelectionData(s.selected_correspondents, this.correspondentSelectionModel)
 | 
					      .getSelectionData(Array.from(this.list.selected))
 | 
				
			||||||
 | 
					      .subscribe((s) => {
 | 
				
			||||||
 | 
					        this.applySelectionData(
 | 
				
			||||||
 | 
					          s.selected_correspondents,
 | 
				
			||||||
 | 
					          this.correspondentSelectionModel
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private _localizeList(items: MatchingModel[]) {
 | 
					  private _localizeList(items: MatchingModel[]) {
 | 
				
			||||||
    if (items.length == 0) {
 | 
					    if (items.length == 0) {
 | 
				
			||||||
      return ""
 | 
					      return ''
 | 
				
			||||||
    } else if (items.length == 1) {
 | 
					    } else if (items.length == 1) {
 | 
				
			||||||
      return $localize`"${items[0].name}"`
 | 
					      return $localize`"${items[0].name}"`
 | 
				
			||||||
    } else if (items.length == 2) {
 | 
					    } else if (items.length == 2) {
 | 
				
			||||||
      return $localize`:This is for messages like 'modify "tag1" and "tag2"':"${items[0].name}" and "${items[1].name}"`
 | 
					      return $localize`:This is for messages like 'modify "tag1" and "tag2"':"${items[0].name}" and "${items[1].name}"`
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      let list = items.slice(0, items.length - 1).map(i => $localize`"${i.name}"`).join($localize`:this is used to separate enumerations and should probably be a comma and a whitespace in most languages:, `)
 | 
					      let list = items
 | 
				
			||||||
      return $localize`:this is for messages like 'modify "tag1", "tag2" and "tag3"':${list} and "${items[items.length - 1].name}"`
 | 
					        .slice(0, items.length - 1)
 | 
				
			||||||
 | 
					        .map((i) => $localize`"${i.name}"`)
 | 
				
			||||||
 | 
					        .join(
 | 
				
			||||||
 | 
					          $localize`:this is used to separate enumerations and should probably be a comma and a whitespace in most languages:, `
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      return $localize`:this is for messages like 'modify "tag1", "tag2" and "tag3"':${list} and "${
 | 
				
			||||||
 | 
					        items[items.length - 1].name
 | 
				
			||||||
 | 
					      }"`
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setTags(changedTags: ChangedItems) {
 | 
					  setTags(changedTags: ChangedItems) {
 | 
				
			||||||
    if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 0) return
 | 
					    if (
 | 
				
			||||||
 | 
					      changedTags.itemsToAdd.length == 0 &&
 | 
				
			||||||
 | 
					      changedTags.itemsToRemove.length == 0
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this.showConfirmationDialogs) {
 | 
					    if (this.showConfirmationDialogs) {
 | 
				
			||||||
      let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
 | 
					      let modal = this.modalService.open(ConfirmDialogComponent, {
 | 
				
			||||||
 | 
					        backdrop: 'static',
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
      modal.componentInstance.title = $localize`Confirm tags assignment`
 | 
					      modal.componentInstance.title = $localize`Confirm tags assignment`
 | 
				
			||||||
      if (changedTags.itemsToAdd.length == 1 && changedTags.itemsToRemove.length == 0) {
 | 
					      if (
 | 
				
			||||||
 | 
					        changedTags.itemsToAdd.length == 1 &&
 | 
				
			||||||
 | 
					        changedTags.itemsToRemove.length == 0
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
        let tag = changedTags.itemsToAdd[0]
 | 
					        let tag = changedTags.itemsToAdd[0]
 | 
				
			||||||
        modal.componentInstance.message = $localize`This operation will add the tag "${tag.name}" to ${this.list.selected.size} selected document(s).`
 | 
					        modal.componentInstance.message = $localize`This operation will add the tag "${tag.name}" to ${this.list.selected.size} selected document(s).`
 | 
				
			||||||
      } else if (changedTags.itemsToAdd.length > 1 && changedTags.itemsToRemove.length == 0) {
 | 
					      } else if (
 | 
				
			||||||
        modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} to ${this.list.selected.size} selected document(s).`
 | 
					        changedTags.itemsToAdd.length > 1 &&
 | 
				
			||||||
      } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 1) {
 | 
					        changedTags.itemsToRemove.length == 0
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(
 | 
				
			||||||
 | 
					          changedTags.itemsToAdd
 | 
				
			||||||
 | 
					        )} to ${this.list.selected.size} selected document(s).`
 | 
				
			||||||
 | 
					      } else if (
 | 
				
			||||||
 | 
					        changedTags.itemsToAdd.length == 0 &&
 | 
				
			||||||
 | 
					        changedTags.itemsToRemove.length == 1
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
        let tag = changedTags.itemsToRemove[0]
 | 
					        let tag = changedTags.itemsToRemove[0]
 | 
				
			||||||
        modal.componentInstance.message = $localize`This operation will remove the tag "${tag.name}" from ${this.list.selected.size} selected document(s).`
 | 
					        modal.componentInstance.message = $localize`This operation will remove the tag "${tag.name}" from ${this.list.selected.size} selected document(s).`
 | 
				
			||||||
      } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length > 1) {
 | 
					      } else if (
 | 
				
			||||||
        modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(changedTags.itemsToRemove)} from ${this.list.selected.size} selected document(s).`
 | 
					        changedTags.itemsToAdd.length == 0 &&
 | 
				
			||||||
 | 
					        changedTags.itemsToRemove.length > 1
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(
 | 
				
			||||||
 | 
					          changedTags.itemsToRemove
 | 
				
			||||||
 | 
					        )} from ${this.list.selected.size} selected document(s).`
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} and remove the tags ${this._localizeList(changedTags.itemsToRemove)} on ${this.list.selected.size} selected document(s).`
 | 
					        modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(
 | 
				
			||||||
 | 
					          changedTags.itemsToAdd
 | 
				
			||||||
 | 
					        )} and remove the tags ${this._localizeList(
 | 
				
			||||||
 | 
					          changedTags.itemsToRemove
 | 
				
			||||||
 | 
					        )} on ${this.list.selected.size} selected document(s).`
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      modal.componentInstance.btnClass = "btn-warning"
 | 
					      modal.componentInstance.btnClass = 'btn-warning'
 | 
				
			||||||
      modal.componentInstance.btnCaption = $localize`Confirm`
 | 
					      modal.componentInstance.btnCaption = $localize`Confirm`
 | 
				
			||||||
      modal.componentInstance.confirmClicked.subscribe(() => {
 | 
					      modal.componentInstance.confirmClicked.subscribe(() => {
 | 
				
			||||||
        this.executeBulkOperation(modal, 'modify_tags', {"add_tags": changedTags.itemsToAdd.map(t => t.id), "remove_tags": changedTags.itemsToRemove.map(t => t.id)})
 | 
					        this.executeBulkOperation(modal, 'modify_tags', {
 | 
				
			||||||
 | 
					          add_tags: changedTags.itemsToAdd.map((t) => t.id),
 | 
				
			||||||
 | 
					          remove_tags: changedTags.itemsToRemove.map((t) => t.id),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      this.executeBulkOperation(null, 'modify_tags', {"add_tags": changedTags.itemsToAdd.map(t => t.id), "remove_tags": changedTags.itemsToRemove.map(t => t.id)})
 | 
					      this.executeBulkOperation(null, 'modify_tags', {
 | 
				
			||||||
 | 
					        add_tags: changedTags.itemsToAdd.map((t) => t.id),
 | 
				
			||||||
 | 
					        remove_tags: changedTags.itemsToRemove.map((t) => t.id),
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setCorrespondents(changedCorrespondents: ChangedItems) {
 | 
					  setCorrespondents(changedCorrespondents: ChangedItems) {
 | 
				
			||||||
    if (changedCorrespondents.itemsToAdd.length == 0 && changedCorrespondents.itemsToRemove.length == 0) return
 | 
					    if (
 | 
				
			||||||
 | 
					      changedCorrespondents.itemsToAdd.length == 0 &&
 | 
				
			||||||
 | 
					      changedCorrespondents.itemsToRemove.length == 0
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let correspondent = changedCorrespondents.itemsToAdd.length > 0 ? changedCorrespondents.itemsToAdd[0] : null
 | 
					    let correspondent =
 | 
				
			||||||
 | 
					      changedCorrespondents.itemsToAdd.length > 0
 | 
				
			||||||
 | 
					        ? changedCorrespondents.itemsToAdd[0]
 | 
				
			||||||
 | 
					        : null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this.showConfirmationDialogs) {
 | 
					    if (this.showConfirmationDialogs) {
 | 
				
			||||||
      let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
 | 
					      let modal = this.modalService.open(ConfirmDialogComponent, {
 | 
				
			||||||
 | 
					        backdrop: 'static',
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
      modal.componentInstance.title = $localize`Confirm correspondent assignment`
 | 
					      modal.componentInstance.title = $localize`Confirm correspondent assignment`
 | 
				
			||||||
      if (correspondent) {
 | 
					      if (correspondent) {
 | 
				
			||||||
        modal.componentInstance.message = $localize`This operation will assign the correspondent "${correspondent.name}" to ${this.list.selected.size} selected document(s).`
 | 
					        modal.componentInstance.message = $localize`This operation will assign the correspondent "${correspondent.name}" to ${this.list.selected.size} selected document(s).`
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        modal.componentInstance.message = $localize`This operation will remove the correspondent from ${this.list.selected.size} selected document(s).`
 | 
					        modal.componentInstance.message = $localize`This operation will remove the correspondent from ${this.list.selected.size} selected document(s).`
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      modal.componentInstance.btnClass = "btn-warning"
 | 
					      modal.componentInstance.btnClass = 'btn-warning'
 | 
				
			||||||
      modal.componentInstance.btnCaption = $localize`Confirm`
 | 
					      modal.componentInstance.btnCaption = $localize`Confirm`
 | 
				
			||||||
      modal.componentInstance.confirmClicked.subscribe(() => {
 | 
					      modal.componentInstance.confirmClicked.subscribe(() => {
 | 
				
			||||||
        this.executeBulkOperation(modal, 'set_correspondent', {"correspondent": correspondent ? correspondent.id : null})
 | 
					        this.executeBulkOperation(modal, 'set_correspondent', {
 | 
				
			||||||
 | 
					          correspondent: correspondent ? correspondent.id : null,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      this.executeBulkOperation(null, 'set_correspondent', {"correspondent": correspondent ? correspondent.id : null})
 | 
					      this.executeBulkOperation(null, 'set_correspondent', {
 | 
				
			||||||
 | 
					        correspondent: correspondent ? correspondent.id : null,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setDocumentTypes(changedDocumentTypes: ChangedItems) {
 | 
					  setDocumentTypes(changedDocumentTypes: ChangedItems) {
 | 
				
			||||||
    if (changedDocumentTypes.itemsToAdd.length == 0 && changedDocumentTypes.itemsToRemove.length == 0) return
 | 
					    if (
 | 
				
			||||||
 | 
					      changedDocumentTypes.itemsToAdd.length == 0 &&
 | 
				
			||||||
 | 
					      changedDocumentTypes.itemsToRemove.length == 0
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let documentType = changedDocumentTypes.itemsToAdd.length > 0 ? changedDocumentTypes.itemsToAdd[0] : null
 | 
					    let documentType =
 | 
				
			||||||
 | 
					      changedDocumentTypes.itemsToAdd.length > 0
 | 
				
			||||||
 | 
					        ? changedDocumentTypes.itemsToAdd[0]
 | 
				
			||||||
 | 
					        : null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this.showConfirmationDialogs) {
 | 
					    if (this.showConfirmationDialogs) {
 | 
				
			||||||
      let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
 | 
					      let modal = this.modalService.open(ConfirmDialogComponent, {
 | 
				
			||||||
 | 
					        backdrop: 'static',
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
      modal.componentInstance.title = $localize`Confirm document type assignment`
 | 
					      modal.componentInstance.title = $localize`Confirm document type assignment`
 | 
				
			||||||
      if (documentType) {
 | 
					      if (documentType) {
 | 
				
			||||||
        modal.componentInstance.message = $localize`This operation will assign the document type "${documentType.name}" to ${this.list.selected.size} selected document(s).`
 | 
					        modal.componentInstance.message = $localize`This operation will assign the document type "${documentType.name}" to ${this.list.selected.size} selected document(s).`
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        modal.componentInstance.message = $localize`This operation will remove the document type from ${this.list.selected.size} selected document(s).`
 | 
					        modal.componentInstance.message = $localize`This operation will remove the document type from ${this.list.selected.size} selected document(s).`
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      modal.componentInstance.btnClass = "btn-warning"
 | 
					      modal.componentInstance.btnClass = 'btn-warning'
 | 
				
			||||||
      modal.componentInstance.btnCaption = $localize`Confirm`
 | 
					      modal.componentInstance.btnCaption = $localize`Confirm`
 | 
				
			||||||
      modal.componentInstance.confirmClicked.subscribe(() => {
 | 
					      modal.componentInstance.confirmClicked.subscribe(() => {
 | 
				
			||||||
        this.executeBulkOperation(modal, 'set_document_type', {"document_type": documentType ? documentType.id : null})
 | 
					        this.executeBulkOperation(modal, 'set_document_type', {
 | 
				
			||||||
 | 
					          document_type: documentType ? documentType.id : null,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      this.executeBulkOperation(null, 'set_document_type', {"document_type": documentType ? documentType.id : null})
 | 
					      this.executeBulkOperation(null, 'set_document_type', {
 | 
				
			||||||
 | 
					        document_type: documentType ? documentType.id : null,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  applyDelete() {
 | 
					  applyDelete() {
 | 
				
			||||||
    let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
 | 
					    let modal = this.modalService.open(ConfirmDialogComponent, {
 | 
				
			||||||
 | 
					      backdrop: 'static',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    modal.componentInstance.delayConfirm(5)
 | 
					    modal.componentInstance.delayConfirm(5)
 | 
				
			||||||
    modal.componentInstance.title = $localize`Delete confirm`
 | 
					    modal.componentInstance.title = $localize`Delete confirm`
 | 
				
			||||||
    modal.componentInstance.messageBold = $localize`This operation will permanently delete ${this.list.selected.size} selected document(s).`
 | 
					    modal.componentInstance.messageBold = $localize`This operation will permanently delete ${this.list.selected.size} selected document(s).`
 | 
				
			||||||
    modal.componentInstance.message = $localize`This operation cannot be undone.`
 | 
					    modal.componentInstance.message = $localize`This operation cannot be undone.`
 | 
				
			||||||
    modal.componentInstance.btnClass = "btn-danger"
 | 
					    modal.componentInstance.btnClass = 'btn-danger'
 | 
				
			||||||
    modal.componentInstance.btnCaption = $localize`Delete document(s)`
 | 
					    modal.componentInstance.btnCaption = $localize`Delete document(s)`
 | 
				
			||||||
    modal.componentInstance.confirmClicked.subscribe(() => {
 | 
					    modal.componentInstance.confirmClicked.subscribe(() => {
 | 
				
			||||||
      modal.componentInstance.buttonsEnabled = false
 | 
					      modal.componentInstance.buttonsEnabled = false
 | 
				
			||||||
      this.executeBulkOperation(modal, "delete", {})
 | 
					      this.executeBulkOperation(modal, 'delete', {})
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  downloadSelected(content = "archive") {
 | 
					  downloadSelected(content = 'archive') {
 | 
				
			||||||
    this.documentService.bulkDownload(Array.from(this.list.selected), content).subscribe((result: any) => {
 | 
					    this.documentService
 | 
				
			||||||
      saveAs(result, 'documents.zip');
 | 
					      .bulkDownload(Array.from(this.list.selected), content)
 | 
				
			||||||
 | 
					      .subscribe((result: any) => {
 | 
				
			||||||
 | 
					        saveAs(result, 'documents.zip')
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentCardLargeComponent } from './document-card-large.component';
 | 
					import { DocumentCardLargeComponent } from './document-card-large.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DocumentCardLargeComponent', () => {
 | 
					describe('DocumentCardLargeComponent', () => {
 | 
				
			||||||
  let component: DocumentCardLargeComponent;
 | 
					  let component: DocumentCardLargeComponent
 | 
				
			||||||
  let fixture: ComponentFixture<DocumentCardLargeComponent>;
 | 
					  let fixture: ComponentFixture<DocumentCardLargeComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ DocumentCardLargeComponent ]
 | 
					      declarations: [DocumentCardLargeComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentCardLargeComponent);
 | 
					    fixture = TestBed.createComponent(DocumentCardLargeComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,36 @@
 | 
				
			|||||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { DomSanitizer } from '@angular/platform-browser';
 | 
					  Component,
 | 
				
			||||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
 | 
					  EventEmitter,
 | 
				
			||||||
import { DocumentService } from 'src/app/services/rest/document.service';
 | 
					  Input,
 | 
				
			||||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
 | 
					  OnInit,
 | 
				
			||||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
 | 
					  Output,
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
 | 
					  ViewChild,
 | 
				
			||||||
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type';
 | 
					} from '@angular/core'
 | 
				
			||||||
 | 
					import { DomSanitizer } from '@angular/platform-browser'
 | 
				
			||||||
 | 
					import { PaperlessDocument } from 'src/app/data/paperless-document'
 | 
				
			||||||
 | 
					import { DocumentService } from 'src/app/services/rest/document.service'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  SettingsService,
 | 
				
			||||||
 | 
					  SETTINGS_KEYS,
 | 
				
			||||||
 | 
					} from 'src/app/services/settings.service'
 | 
				
			||||||
 | 
					import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
 | 
					import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-document-card-large',
 | 
					  selector: 'app-document-card-large',
 | 
				
			||||||
  templateUrl: './document-card-large.component.html',
 | 
					  templateUrl: './document-card-large.component.html',
 | 
				
			||||||
  styleUrls: ['./document-card-large.component.scss', '../popover-preview/popover-preview.scss']
 | 
					  styleUrls: [
 | 
				
			||||||
 | 
					    './document-card-large.component.scss',
 | 
				
			||||||
 | 
					    '../popover-preview/popover-preview.scss',
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DocumentCardLargeComponent implements OnInit {
 | 
					export class DocumentCardLargeComponent implements OnInit {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
  constructor(private documentService: DocumentService, private sanitizer: DomSanitizer, private settingsService: SettingsService) { }
 | 
					    private documentService: DocumentService,
 | 
				
			||||||
 | 
					    private sanitizer: DomSanitizer,
 | 
				
			||||||
 | 
					    private settingsService: SettingsService
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  selected = false
 | 
					  selected = false
 | 
				
			||||||
@ -39,7 +55,7 @@ export class DocumentCardLargeComponent implements OnInit {
 | 
				
			|||||||
  clickDocumentType = new EventEmitter<number>()
 | 
					  clickDocumentType = new EventEmitter<number>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Output()
 | 
					  @Output()
 | 
				
			||||||
  clickMoreLike= new EventEmitter()
 | 
					  clickMoreLike = new EventEmitter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @ViewChild('popover') popover: NgbPopover
 | 
					  @ViewChild('popover') popover: NgbPopover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -49,17 +65,16 @@ export class DocumentCardLargeComponent implements OnInit {
 | 
				
			|||||||
  get searchScoreClass() {
 | 
					  get searchScoreClass() {
 | 
				
			||||||
    if (this.document.__search_hit__) {
 | 
					    if (this.document.__search_hit__) {
 | 
				
			||||||
      if (this.document.__search_hit__.score > 0.7) {
 | 
					      if (this.document.__search_hit__.score > 0.7) {
 | 
				
			||||||
        return "success"
 | 
					        return 'success'
 | 
				
			||||||
      } else if (this.document.__search_hit__.score > 0.3) {
 | 
					      } else if (this.document.__search_hit__.score > 0.3) {
 | 
				
			||||||
        return "warning"
 | 
					        return 'warning'
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        return "danger"
 | 
					        return 'danger'
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getIsThumbInverted() {
 | 
					  getIsThumbInverted() {
 | 
				
			||||||
    return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
 | 
					    return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
 | 
				
			||||||
@ -90,7 +105,7 @@ export class DocumentCardLargeComponent implements OnInit {
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          this.popover.close()
 | 
					          this.popover.close()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }, 600);
 | 
					      }, 600)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentCardSmallComponent } from './document-card-small.component';
 | 
					import { DocumentCardSmallComponent } from './document-card-small.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DocumentCardSmallComponent', () => {
 | 
					describe('DocumentCardSmallComponent', () => {
 | 
				
			||||||
  let component: DocumentCardSmallComponent;
 | 
					  let component: DocumentCardSmallComponent
 | 
				
			||||||
  let fixture: ComponentFixture<DocumentCardSmallComponent>;
 | 
					  let fixture: ComponentFixture<DocumentCardSmallComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ DocumentCardSmallComponent ]
 | 
					      declarations: [DocumentCardSmallComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentCardSmallComponent);
 | 
					    fixture = TestBed.createComponent(DocumentCardSmallComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,18 +1,33 @@
 | 
				
			|||||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { map } from 'rxjs/operators';
 | 
					  Component,
 | 
				
			||||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
 | 
					  EventEmitter,
 | 
				
			||||||
import { DocumentService } from 'src/app/services/rest/document.service';
 | 
					  Input,
 | 
				
			||||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
 | 
					  OnInit,
 | 
				
			||||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
 | 
					  Output,
 | 
				
			||||||
 | 
					  ViewChild,
 | 
				
			||||||
 | 
					} from '@angular/core'
 | 
				
			||||||
 | 
					import { map } from 'rxjs/operators'
 | 
				
			||||||
 | 
					import { PaperlessDocument } from 'src/app/data/paperless-document'
 | 
				
			||||||
 | 
					import { DocumentService } from 'src/app/services/rest/document.service'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  SettingsService,
 | 
				
			||||||
 | 
					  SETTINGS_KEYS,
 | 
				
			||||||
 | 
					} from 'src/app/services/settings.service'
 | 
				
			||||||
 | 
					import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-document-card-small',
 | 
					  selector: 'app-document-card-small',
 | 
				
			||||||
  templateUrl: './document-card-small.component.html',
 | 
					  templateUrl: './document-card-small.component.html',
 | 
				
			||||||
  styleUrls: ['./document-card-small.component.scss', '../popover-preview/popover-preview.scss']
 | 
					  styleUrls: [
 | 
				
			||||||
 | 
					    './document-card-small.component.scss',
 | 
				
			||||||
 | 
					    '../popover-preview/popover-preview.scss',
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DocumentCardSmallComponent implements OnInit {
 | 
					export class DocumentCardSmallComponent implements OnInit {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
  constructor(private documentService: DocumentService, private settingsService: SettingsService) { }
 | 
					    private documentService: DocumentService,
 | 
				
			||||||
 | 
					    private settingsService: SettingsService
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  selected = false
 | 
					  selected = false
 | 
				
			||||||
@ -39,8 +54,7 @@ export class DocumentCardSmallComponent implements OnInit {
 | 
				
			|||||||
  mouseOnPreview = false
 | 
					  mouseOnPreview = false
 | 
				
			||||||
  popoverHidden = true
 | 
					  popoverHidden = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getIsThumbInverted() {
 | 
					  getIsThumbInverted() {
 | 
				
			||||||
    return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
 | 
					    return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
 | 
				
			||||||
@ -60,7 +74,7 @@ export class DocumentCardSmallComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  getTagsLimited$() {
 | 
					  getTagsLimited$() {
 | 
				
			||||||
    return this.document.tags$.pipe(
 | 
					    return this.document.tags$.pipe(
 | 
				
			||||||
      map(tags => {
 | 
					      map((tags) => {
 | 
				
			||||||
        if (tags.length > 7) {
 | 
					        if (tags.length > 7) {
 | 
				
			||||||
          this.moreTags = tags.length - 6
 | 
					          this.moreTags = tags.length - 6
 | 
				
			||||||
          return tags.slice(0, 6)
 | 
					          return tags.slice(0, 6)
 | 
				
			||||||
@ -84,7 +98,7 @@ export class DocumentCardSmallComponent implements OnInit {
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          this.popover.close()
 | 
					          this.popover.close()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }, 600);
 | 
					      }, 600)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentListComponent } from './document-list.component';
 | 
					import { DocumentListComponent } from './document-list.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DocumentListComponent', () => {
 | 
					describe('DocumentListComponent', () => {
 | 
				
			||||||
  let component: DocumentListComponent;
 | 
					  let component: DocumentListComponent
 | 
				
			||||||
  let fixture: ComponentFixture<DocumentListComponent>;
 | 
					  let fixture: ComponentFixture<DocumentListComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ DocumentListComponent ]
 | 
					      declarations: [DocumentListComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentListComponent);
 | 
					    fixture = TestBed.createComponent(DocumentListComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,27 +1,39 @@
 | 
				
			|||||||
import { Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { ActivatedRoute, Router } from '@angular/router';
 | 
					  Component,
 | 
				
			||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					  OnDestroy,
 | 
				
			||||||
import { Subscription } from 'rxjs';
 | 
					  OnInit,
 | 
				
			||||||
import { FilterRule, isFullTextFilterRule } from 'src/app/data/filter-rule';
 | 
					  QueryList,
 | 
				
			||||||
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type';
 | 
					  ViewChild,
 | 
				
			||||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
 | 
					  ViewChildren,
 | 
				
			||||||
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
 | 
					} from '@angular/core'
 | 
				
			||||||
import { SortableDirective, SortEvent } from 'src/app/directives/sortable.directive';
 | 
					import { ActivatedRoute, Router } from '@angular/router'
 | 
				
			||||||
import { ConsumerStatusService } from 'src/app/services/consumer-status.service';
 | 
					import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
 | 
					import { Subscription } from 'rxjs'
 | 
				
			||||||
import { DOCUMENT_SORT_FIELDS, DOCUMENT_SORT_FIELDS_FULLTEXT } from 'src/app/services/rest/document.service';
 | 
					import { FilterRule, isFullTextFilterRule } from 'src/app/data/filter-rule'
 | 
				
			||||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
 | 
					import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					import { PaperlessDocument } from 'src/app/data/paperless-document'
 | 
				
			||||||
import { FilterEditorComponent } from './filter-editor/filter-editor.component';
 | 
					import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
 | 
				
			||||||
import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component';
 | 
					import {
 | 
				
			||||||
 | 
					  SortableDirective,
 | 
				
			||||||
 | 
					  SortEvent,
 | 
				
			||||||
 | 
					} from 'src/app/directives/sortable.directive'
 | 
				
			||||||
 | 
					import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
 | 
				
			||||||
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  DOCUMENT_SORT_FIELDS,
 | 
				
			||||||
 | 
					  DOCUMENT_SORT_FIELDS_FULLTEXT,
 | 
				
			||||||
 | 
					} from 'src/app/services/rest/document.service'
 | 
				
			||||||
 | 
					import { SavedViewService } from 'src/app/services/rest/saved-view.service'
 | 
				
			||||||
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
 | 
					import { FilterEditorComponent } from './filter-editor/filter-editor.component'
 | 
				
			||||||
 | 
					import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-document-list',
 | 
					  selector: 'app-document-list',
 | 
				
			||||||
  templateUrl: './document-list.component.html',
 | 
					  templateUrl: './document-list.component.html',
 | 
				
			||||||
  styleUrls: ['./document-list.component.scss']
 | 
					  styleUrls: ['./document-list.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DocumentListComponent implements OnInit, OnDestroy {
 | 
					export class DocumentListComponent implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    public list: DocumentListViewService,
 | 
					    public list: DocumentListViewService,
 | 
				
			||||||
    public savedViewService: SavedViewService,
 | 
					    public savedViewService: SavedViewService,
 | 
				
			||||||
@ -30,12 +42,12 @@ export class DocumentListComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    private toastService: ToastService,
 | 
					    private toastService: ToastService,
 | 
				
			||||||
    private modalService: NgbModal,
 | 
					    private modalService: NgbModal,
 | 
				
			||||||
    private consumerStatusService: ConsumerStatusService
 | 
					    private consumerStatusService: ConsumerStatusService
 | 
				
			||||||
  ) { }
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @ViewChild("filterEditor")
 | 
					  @ViewChild('filterEditor')
 | 
				
			||||||
  private filterEditor: FilterEditorComponent
 | 
					  private filterEditor: FilterEditorComponent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @ViewChildren(SortableDirective) headers: QueryList<SortableDirective>;
 | 
					  @ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  displayMode = 'smallCards' // largeCards, smallCards, details
 | 
					  displayMode = 'smallCards' // largeCards, smallCards, details
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -52,7 +64,9 @@ export class DocumentListComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getSortFields() {
 | 
					  getSortFields() {
 | 
				
			||||||
    return isFullTextFilterRule(this.list.filterRules) ? DOCUMENT_SORT_FIELDS_FULLTEXT : DOCUMENT_SORT_FIELDS
 | 
					    return isFullTextFilterRule(this.list.filterRules)
 | 
				
			||||||
 | 
					      ? DOCUMENT_SORT_FIELDS_FULLTEXT
 | 
				
			||||||
 | 
					      : DOCUMENT_SORT_FIELDS
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onSort(event: SortEvent) {
 | 
					  onSort(event: SortEvent) {
 | 
				
			||||||
@ -71,14 +85,16 @@ export class DocumentListComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    if (localStorage.getItem('document-list:displayMode') != null) {
 | 
					    if (localStorage.getItem('document-list:displayMode') != null) {
 | 
				
			||||||
      this.displayMode = localStorage.getItem('document-list:displayMode')
 | 
					      this.displayMode = localStorage.getItem('document-list:displayMode')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.consumptionFinishedSubscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(() => {
 | 
					    this.consumptionFinishedSubscription = this.consumerStatusService
 | 
				
			||||||
 | 
					      .onDocumentConsumptionFinished()
 | 
				
			||||||
 | 
					      .subscribe(() => {
 | 
				
			||||||
        this.list.reload()
 | 
					        this.list.reload()
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    this.route.paramMap.subscribe(params => {
 | 
					    this.route.paramMap.subscribe((params) => {
 | 
				
			||||||
      if (params.has('id')) {
 | 
					      if (params.has('id')) {
 | 
				
			||||||
        this.savedViewService.getCached(+params.get('id')).subscribe(view => {
 | 
					        this.savedViewService.getCached(+params.get('id')).subscribe((view) => {
 | 
				
			||||||
          if (!view) {
 | 
					          if (!view) {
 | 
				
			||||||
            this.router.navigate(["404"])
 | 
					            this.router.navigate(['404'])
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          this.list.activateSavedView(view)
 | 
					          this.list.activateSavedView(view)
 | 
				
			||||||
@ -110,19 +126,23 @@ export class DocumentListComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
        id: this.list.activeSavedViewId,
 | 
					        id: this.list.activeSavedViewId,
 | 
				
			||||||
        filter_rules: this.list.filterRules,
 | 
					        filter_rules: this.list.filterRules,
 | 
				
			||||||
        sort_field: this.list.sortField,
 | 
					        sort_field: this.list.sortField,
 | 
				
			||||||
        sort_reverse: this.list.sortReverse
 | 
					        sort_reverse: this.list.sortReverse,
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.savedViewService.patch(savedView).subscribe(result => {
 | 
					      this.savedViewService.patch(savedView).subscribe((result) => {
 | 
				
			||||||
        this.toastService.showInfo($localize`View "${this.list.activeSavedViewTitle}" saved successfully.`)
 | 
					        this.toastService.showInfo(
 | 
				
			||||||
 | 
					          $localize`View "${this.list.activeSavedViewTitle}" saved successfully.`
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        this.unmodifiedFilterRules = this.list.filterRules
 | 
					        this.unmodifiedFilterRules = this.list.filterRules
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  saveViewConfigAs() {
 | 
					  saveViewConfigAs() {
 | 
				
			||||||
    let modal = this.modalService.open(SaveViewConfigDialogComponent, {backdrop: 'static'})
 | 
					    let modal = this.modalService.open(SaveViewConfigDialogComponent, {
 | 
				
			||||||
 | 
					      backdrop: 'static',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    modal.componentInstance.defaultName = this.filterEditor.generateFilterName()
 | 
					    modal.componentInstance.defaultName = this.filterEditor.generateFilterName()
 | 
				
			||||||
    modal.componentInstance.saveClicked.subscribe(formValue => {
 | 
					    modal.componentInstance.saveClicked.subscribe((formValue) => {
 | 
				
			||||||
      modal.componentInstance.buttonsEnabled = false
 | 
					      modal.componentInstance.buttonsEnabled = false
 | 
				
			||||||
      let savedView: PaperlessSavedView = {
 | 
					      let savedView: PaperlessSavedView = {
 | 
				
			||||||
        name: formValue.name,
 | 
					        name: formValue.name,
 | 
				
			||||||
@ -130,16 +150,21 @@ export class DocumentListComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
        show_in_sidebar: formValue.showInSideBar,
 | 
					        show_in_sidebar: formValue.showInSideBar,
 | 
				
			||||||
        filter_rules: this.list.filterRules,
 | 
					        filter_rules: this.list.filterRules,
 | 
				
			||||||
        sort_reverse: this.list.sortReverse,
 | 
					        sort_reverse: this.list.sortReverse,
 | 
				
			||||||
        sort_field: this.list.sortField
 | 
					        sort_field: this.list.sortField,
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.savedViewService.create(savedView).subscribe(() => {
 | 
					      this.savedViewService.create(savedView).subscribe(
 | 
				
			||||||
 | 
					        () => {
 | 
				
			||||||
          modal.close()
 | 
					          modal.close()
 | 
				
			||||||
        this.toastService.showInfo($localize`View "${savedView.name}" created successfully.`)
 | 
					          this.toastService.showInfo(
 | 
				
			||||||
      }, error => {
 | 
					            $localize`View "${savedView.name}" created successfully.`
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        (error) => {
 | 
				
			||||||
          modal.componentInstance.error = error.error
 | 
					          modal.componentInstance.error = error.error
 | 
				
			||||||
          modal.componentInstance.buttonsEnabled = true
 | 
					          modal.componentInstance.buttonsEnabled = true
 | 
				
			||||||
      })
 | 
					        }
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -170,7 +195,9 @@ export class DocumentListComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clickMoreLike(documentID: number) {
 | 
					  clickMoreLike(documentID: number) {
 | 
				
			||||||
    this.list.quickFilter([{rule_type: FILTER_FULLTEXT_MORELIKE, value: documentID.toString()}])
 | 
					    this.list.quickFilter([
 | 
				
			||||||
 | 
					      { rule_type: FILTER_FULLTEXT_MORELIKE, value: documentID.toString() },
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  trackByDocumentId(index, item: PaperlessDocument) {
 | 
					  trackByDocumentId(index, item: PaperlessDocument) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FilterEditorComponent } from './filter-editor.component';
 | 
					import { FilterEditorComponent } from './filter-editor.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('FilterEditorComponent', () => {
 | 
					describe('FilterEditorComponent', () => {
 | 
				
			||||||
  let component: FilterEditorComponent;
 | 
					  let component: FilterEditorComponent
 | 
				
			||||||
  let fixture: ComponentFixture<FilterEditorComponent>;
 | 
					  let fixture: ComponentFixture<FilterEditorComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ FilterEditorComponent ]
 | 
					      declarations: [FilterEditorComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(FilterEditorComponent);
 | 
					    fixture = TestBed.createComponent(FilterEditorComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,56 +1,85 @@
 | 
				
			|||||||
import { Component, EventEmitter, Input, Output, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { PaperlessTag } from 'src/app/data/paperless-tag';
 | 
					  Component,
 | 
				
			||||||
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
 | 
					  EventEmitter,
 | 
				
			||||||
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
 | 
					  Input,
 | 
				
			||||||
import { Subject, Subscription } from 'rxjs';
 | 
					  Output,
 | 
				
			||||||
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
 | 
					  OnInit,
 | 
				
			||||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
 | 
					  OnDestroy,
 | 
				
			||||||
import { TagService } from 'src/app/services/rest/tag.service';
 | 
					  ViewChild,
 | 
				
			||||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
 | 
					  ElementRef,
 | 
				
			||||||
import { FilterRule } from 'src/app/data/filter-rule';
 | 
					} from '@angular/core'
 | 
				
			||||||
import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_ASN, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_FULLTEXT_MORELIKE, FILTER_FULLTEXT_QUERY, FILTER_HAS_ANY_TAG, FILTER_HAS_TAGS_ALL, FILTER_HAS_TAGS_ANY, FILTER_DOES_NOT_HAVE_TAG, FILTER_TITLE, FILTER_TITLE_CONTENT } from 'src/app/data/filter-rule-type';
 | 
					import { PaperlessTag } from 'src/app/data/paperless-tag'
 | 
				
			||||||
import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component';
 | 
					import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
 | 
				
			||||||
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component';
 | 
					import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
 | 
				
			||||||
import { DocumentService } from 'src/app/services/rest/document.service';
 | 
					import { Subject, Subscription } from 'rxjs'
 | 
				
			||||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
 | 
					import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
 | 
				
			||||||
 | 
					import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
 | 
				
			||||||
 | 
					import { TagService } from 'src/app/services/rest/tag.service'
 | 
				
			||||||
 | 
					import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
 | 
				
			||||||
 | 
					import { FilterRule } from 'src/app/data/filter-rule'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  FILTER_ADDED_AFTER,
 | 
				
			||||||
 | 
					  FILTER_ADDED_BEFORE,
 | 
				
			||||||
 | 
					  FILTER_ASN,
 | 
				
			||||||
 | 
					  FILTER_CORRESPONDENT,
 | 
				
			||||||
 | 
					  FILTER_CREATED_AFTER,
 | 
				
			||||||
 | 
					  FILTER_CREATED_BEFORE,
 | 
				
			||||||
 | 
					  FILTER_DOCUMENT_TYPE,
 | 
				
			||||||
 | 
					  FILTER_FULLTEXT_MORELIKE,
 | 
				
			||||||
 | 
					  FILTER_FULLTEXT_QUERY,
 | 
				
			||||||
 | 
					  FILTER_HAS_ANY_TAG,
 | 
				
			||||||
 | 
					  FILTER_HAS_TAGS_ALL,
 | 
				
			||||||
 | 
					  FILTER_HAS_TAGS_ANY,
 | 
				
			||||||
 | 
					  FILTER_DOES_NOT_HAVE_TAG,
 | 
				
			||||||
 | 
					  FILTER_TITLE,
 | 
				
			||||||
 | 
					  FILTER_TITLE_CONTENT,
 | 
				
			||||||
 | 
					} from 'src/app/data/filter-rule-type'
 | 
				
			||||||
 | 
					import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component'
 | 
				
			||||||
 | 
					import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
 | 
				
			||||||
 | 
					import { DocumentService } from 'src/app/services/rest/document.service'
 | 
				
			||||||
 | 
					import { PaperlessDocument } from 'src/app/data/paperless-document'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TEXT_FILTER_TARGET_TITLE = "title"
 | 
					const TEXT_FILTER_TARGET_TITLE = 'title'
 | 
				
			||||||
const TEXT_FILTER_TARGET_TITLE_CONTENT = "title-content"
 | 
					const TEXT_FILTER_TARGET_TITLE_CONTENT = 'title-content'
 | 
				
			||||||
const TEXT_FILTER_TARGET_ASN = "asn"
 | 
					const TEXT_FILTER_TARGET_ASN = 'asn'
 | 
				
			||||||
const TEXT_FILTER_TARGET_FULLTEXT_QUERY = "fulltext-query"
 | 
					const TEXT_FILTER_TARGET_FULLTEXT_QUERY = 'fulltext-query'
 | 
				
			||||||
const TEXT_FILTER_TARGET_FULLTEXT_MORELIKE = "fulltext-morelike"
 | 
					const TEXT_FILTER_TARGET_FULLTEXT_MORELIKE = 'fulltext-morelike'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-filter-editor',
 | 
					  selector: 'app-filter-editor',
 | 
				
			||||||
  templateUrl: './filter-editor.component.html',
 | 
					  templateUrl: './filter-editor.component.html',
 | 
				
			||||||
  styleUrls: ['./filter-editor.component.scss']
 | 
					  styleUrls: ['./filter-editor.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
					export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  generateFilterName() {
 | 
					  generateFilterName() {
 | 
				
			||||||
    if (this.filterRules.length == 1) {
 | 
					    if (this.filterRules.length == 1) {
 | 
				
			||||||
      let rule = this.filterRules[0]
 | 
					      let rule = this.filterRules[0]
 | 
				
			||||||
      switch(this.filterRules[0].rule_type) {
 | 
					      switch (this.filterRules[0].rule_type) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        case FILTER_CORRESPONDENT:
 | 
					        case FILTER_CORRESPONDENT:
 | 
				
			||||||
          if (rule.value) {
 | 
					          if (rule.value) {
 | 
				
			||||||
            return $localize`Correspondent: ${this.correspondents.find(c => c.id == +rule.value)?.name}`
 | 
					            return $localize`Correspondent: ${
 | 
				
			||||||
 | 
					              this.correspondents.find((c) => c.id == +rule.value)?.name
 | 
				
			||||||
 | 
					            }`
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            return $localize`Without correspondent`
 | 
					            return $localize`Without correspondent`
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case FILTER_DOCUMENT_TYPE:
 | 
					        case FILTER_DOCUMENT_TYPE:
 | 
				
			||||||
          if (rule.value) {
 | 
					          if (rule.value) {
 | 
				
			||||||
            return $localize`Type: ${this.documentTypes.find(dt => dt.id == +rule.value)?.name}`
 | 
					            return $localize`Type: ${
 | 
				
			||||||
 | 
					              this.documentTypes.find((dt) => dt.id == +rule.value)?.name
 | 
				
			||||||
 | 
					            }`
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            return $localize`Without document type`
 | 
					            return $localize`Without document type`
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case FILTER_HAS_TAGS_ALL:
 | 
					        case FILTER_HAS_TAGS_ALL:
 | 
				
			||||||
          return $localize`Tag: ${this.tags.find(t => t.id == +rule.value)?.name}`
 | 
					          return $localize`Tag: ${
 | 
				
			||||||
 | 
					            this.tags.find((t) => t.id == +rule.value)?.name
 | 
				
			||||||
 | 
					          }`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case FILTER_HAS_ANY_TAG:
 | 
					        case FILTER_HAS_ANY_TAG:
 | 
				
			||||||
          if (rule.value == "false") {
 | 
					          if (rule.value == 'false') {
 | 
				
			||||||
            return $localize`Without any tag`
 | 
					            return $localize`Without any tag`
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -62,7 +91,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return ""
 | 
					    return ''
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
@ -70,28 +99,37 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    private tagService: TagService,
 | 
					    private tagService: TagService,
 | 
				
			||||||
    private correspondentService: CorrespondentService,
 | 
					    private correspondentService: CorrespondentService,
 | 
				
			||||||
    private documentService: DocumentService
 | 
					    private documentService: DocumentService
 | 
				
			||||||
  ) { }
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @ViewChild("textFilterInput")
 | 
					  @ViewChild('textFilterInput')
 | 
				
			||||||
  textFilterInput: ElementRef
 | 
					  textFilterInput: ElementRef
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  tags: PaperlessTag[] = []
 | 
					  tags: PaperlessTag[] = []
 | 
				
			||||||
  correspondents: PaperlessCorrespondent[] = []
 | 
					  correspondents: PaperlessCorrespondent[] = []
 | 
				
			||||||
  documentTypes: PaperlessDocumentType[] = []
 | 
					  documentTypes: PaperlessDocumentType[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _textFilter = ""
 | 
					  _textFilter = ''
 | 
				
			||||||
  _moreLikeId: number
 | 
					  _moreLikeId: number
 | 
				
			||||||
  _moreLikeDoc: PaperlessDocument
 | 
					  _moreLikeDoc: PaperlessDocument
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get textFilterTargets() {
 | 
					  get textFilterTargets() {
 | 
				
			||||||
    let targets = [
 | 
					    let targets = [
 | 
				
			||||||
      {id: TEXT_FILTER_TARGET_TITLE, name: $localize`Title`},
 | 
					      { id: TEXT_FILTER_TARGET_TITLE, name: $localize`Title` },
 | 
				
			||||||
      {id: TEXT_FILTER_TARGET_TITLE_CONTENT, name: $localize`Title & content`},
 | 
					      {
 | 
				
			||||||
      {id: TEXT_FILTER_TARGET_ASN, name: $localize`ASN`},
 | 
					        id: TEXT_FILTER_TARGET_TITLE_CONTENT,
 | 
				
			||||||
      {id: TEXT_FILTER_TARGET_FULLTEXT_QUERY, name: $localize`Advanced search`}
 | 
					        name: $localize`Title & content`,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      { id: TEXT_FILTER_TARGET_ASN, name: $localize`ASN` },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        id: TEXT_FILTER_TARGET_FULLTEXT_QUERY,
 | 
				
			||||||
 | 
					        name: $localize`Advanced search`,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    if (this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE) {
 | 
					    if (this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE) {
 | 
				
			||||||
      targets.push({id: TEXT_FILTER_TARGET_FULLTEXT_MORELIKE, name: $localize`More like`})
 | 
					      targets.push({
 | 
				
			||||||
 | 
					        id: TEXT_FILTER_TARGET_FULLTEXT_MORELIKE,
 | 
				
			||||||
 | 
					        name: $localize`More like`,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return targets
 | 
					    return targets
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -99,10 +137,10 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
 | 
					  textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get textFilterTargetName() {
 | 
					  get textFilterTargetName() {
 | 
				
			||||||
    return this.textFilterTargets.find(t => t.id == this.textFilterTarget)?.name
 | 
					    return this.textFilterTargets.find((t) => t.id == this.textFilterTarget)
 | 
				
			||||||
 | 
					      ?.name
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
  tagSelectionModel = new FilterableDropdownSelectionModel()
 | 
					  tagSelectionModel = new FilterableDropdownSelectionModel()
 | 
				
			||||||
  correspondentSelectionModel = new FilterableDropdownSelectionModel()
 | 
					  correspondentSelectionModel = new FilterableDropdownSelectionModel()
 | 
				
			||||||
  documentTypeSelectionModel = new FilterableDropdownSelectionModel()
 | 
					  documentTypeSelectionModel = new FilterableDropdownSelectionModel()
 | 
				
			||||||
@ -126,7 +164,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  set filterRules (value: FilterRule[]) {
 | 
					  set filterRules(value: FilterRule[]) {
 | 
				
			||||||
    this._filterRules = value
 | 
					    this._filterRules = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.documentTypeSelectionModel.clear(false)
 | 
					    this.documentTypeSelectionModel.clear(false)
 | 
				
			||||||
@ -139,7 +177,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    this.dateCreatedBefore = null
 | 
					    this.dateCreatedBefore = null
 | 
				
			||||||
    this.dateCreatedAfter = null
 | 
					    this.dateCreatedAfter = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    value.forEach(rule => {
 | 
					    value.forEach((rule) => {
 | 
				
			||||||
      switch (rule.rule_type) {
 | 
					      switch (rule.rule_type) {
 | 
				
			||||||
        case FILTER_TITLE:
 | 
					        case FILTER_TITLE:
 | 
				
			||||||
          this._textFilter = rule.value
 | 
					          this._textFilter = rule.value
 | 
				
			||||||
@ -160,7 +198,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
        case FILTER_FULLTEXT_MORELIKE:
 | 
					        case FILTER_FULLTEXT_MORELIKE:
 | 
				
			||||||
          this._moreLikeId = +rule.value
 | 
					          this._moreLikeId = +rule.value
 | 
				
			||||||
          this.textFilterTarget = TEXT_FILTER_TARGET_FULLTEXT_MORELIKE
 | 
					          this.textFilterTarget = TEXT_FILTER_TARGET_FULLTEXT_MORELIKE
 | 
				
			||||||
          this.documentService.get(this._moreLikeId).subscribe(result => {
 | 
					          this.documentService.get(this._moreLikeId).subscribe((result) => {
 | 
				
			||||||
            this._moreLikeDoc = result
 | 
					            this._moreLikeDoc = result
 | 
				
			||||||
            this._textFilter = result.title
 | 
					            this._textFilter = result.title
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
@ -178,23 +216,43 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
          this.dateAddedBefore = rule.value
 | 
					          this.dateAddedBefore = rule.value
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
        case FILTER_HAS_TAGS_ALL:
 | 
					        case FILTER_HAS_TAGS_ALL:
 | 
				
			||||||
          this.tagSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false)
 | 
					          this.tagSelectionModel.set(
 | 
				
			||||||
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
 | 
					            ToggleableItemState.Selected,
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
        case FILTER_HAS_TAGS_ANY:
 | 
					        case FILTER_HAS_TAGS_ANY:
 | 
				
			||||||
          this.tagSelectionModel.logicalOperator = 'or'
 | 
					          this.tagSelectionModel.logicalOperator = 'or'
 | 
				
			||||||
          this.tagSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false)
 | 
					          this.tagSelectionModel.set(
 | 
				
			||||||
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
 | 
					            ToggleableItemState.Selected,
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
        case FILTER_HAS_ANY_TAG:
 | 
					        case FILTER_HAS_ANY_TAG:
 | 
				
			||||||
          this.tagSelectionModel.set(null, ToggleableItemState.Selected, false)
 | 
					          this.tagSelectionModel.set(null, ToggleableItemState.Selected, false)
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
        case FILTER_DOES_NOT_HAVE_TAG:
 | 
					        case FILTER_DOES_NOT_HAVE_TAG:
 | 
				
			||||||
          this.tagSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Excluded, false)
 | 
					          this.tagSelectionModel.set(
 | 
				
			||||||
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
 | 
					            ToggleableItemState.Excluded,
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
        case FILTER_CORRESPONDENT:
 | 
					        case FILTER_CORRESPONDENT:
 | 
				
			||||||
          this.correspondentSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false)
 | 
					          this.correspondentSelectionModel.set(
 | 
				
			||||||
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
 | 
					            ToggleableItemState.Selected,
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
        case FILTER_DOCUMENT_TYPE:
 | 
					        case FILTER_DOCUMENT_TYPE:
 | 
				
			||||||
          this.documentTypeSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false)
 | 
					          this.documentTypeSelectionModel.set(
 | 
				
			||||||
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
 | 
					            ToggleableItemState.Selected,
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
@ -203,49 +261,104 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  get filterRules(): FilterRule[] {
 | 
					  get filterRules(): FilterRule[] {
 | 
				
			||||||
    let filterRules: FilterRule[] = []
 | 
					    let filterRules: FilterRule[] = []
 | 
				
			||||||
    if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT) {
 | 
					    if (
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_TITLE_CONTENT, value: this._textFilter})
 | 
					      this._textFilter &&
 | 
				
			||||||
 | 
					      this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      filterRules.push({
 | 
				
			||||||
 | 
					        rule_type: FILTER_TITLE_CONTENT,
 | 
				
			||||||
 | 
					        value: this._textFilter,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE) {
 | 
					    if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE) {
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_TITLE, value: this._textFilter})
 | 
					      filterRules.push({ rule_type: FILTER_TITLE, value: this._textFilter })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_ASN) {
 | 
					    if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_ASN) {
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_ASN, value: this._textFilter})
 | 
					      filterRules.push({ rule_type: FILTER_ASN, value: this._textFilter })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_QUERY) {
 | 
					    if (
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_FULLTEXT_QUERY, value: this._textFilter})
 | 
					      this._textFilter &&
 | 
				
			||||||
 | 
					      this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_QUERY
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      filterRules.push({
 | 
				
			||||||
 | 
					        rule_type: FILTER_FULLTEXT_QUERY,
 | 
				
			||||||
 | 
					        value: this._textFilter,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this._moreLikeId && this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE) {
 | 
					    if (
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_FULLTEXT_MORELIKE, value: this._moreLikeId?.toString()})
 | 
					      this._moreLikeId &&
 | 
				
			||||||
 | 
					      this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      filterRules.push({
 | 
				
			||||||
 | 
					        rule_type: FILTER_FULLTEXT_MORELIKE,
 | 
				
			||||||
 | 
					        value: this._moreLikeId?.toString(),
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this.tagSelectionModel.isNoneSelected()) {
 | 
					    if (this.tagSelectionModel.isNoneSelected()) {
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_HAS_ANY_TAG, value: "false"})
 | 
					      filterRules.push({ rule_type: FILTER_HAS_ANY_TAG, value: 'false' })
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      const tagFilterType = this.tagSelectionModel.logicalOperator == 'and' ? FILTER_HAS_TAGS_ALL : FILTER_HAS_TAGS_ANY
 | 
					      const tagFilterType =
 | 
				
			||||||
      this.tagSelectionModel.getSelectedItems().filter(tag => tag.id).forEach(tag => {
 | 
					        this.tagSelectionModel.logicalOperator == 'and'
 | 
				
			||||||
        filterRules.push({rule_type: tagFilterType, value: tag.id?.toString()})
 | 
					          ? FILTER_HAS_TAGS_ALL
 | 
				
			||||||
 | 
					          : FILTER_HAS_TAGS_ANY
 | 
				
			||||||
 | 
					      this.tagSelectionModel
 | 
				
			||||||
 | 
					        .getSelectedItems()
 | 
				
			||||||
 | 
					        .filter((tag) => tag.id)
 | 
				
			||||||
 | 
					        .forEach((tag) => {
 | 
				
			||||||
 | 
					          filterRules.push({
 | 
				
			||||||
 | 
					            rule_type: tagFilterType,
 | 
				
			||||||
 | 
					            value: tag.id?.toString(),
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      this.tagSelectionModel
 | 
				
			||||||
 | 
					        .getExcludedItems()
 | 
				
			||||||
 | 
					        .filter((tag) => tag.id)
 | 
				
			||||||
 | 
					        .forEach((tag) => {
 | 
				
			||||||
 | 
					          filterRules.push({
 | 
				
			||||||
 | 
					            rule_type: FILTER_DOES_NOT_HAVE_TAG,
 | 
				
			||||||
 | 
					            value: tag.id?.toString(),
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
      this.tagSelectionModel.getExcludedItems().filter(tag => tag.id).forEach(tag => {
 | 
					 | 
				
			||||||
        filterRules.push({rule_type: FILTER_DOES_NOT_HAVE_TAG, value: tag.id?.toString()})
 | 
					 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.correspondentSelectionModel.getSelectedItems().forEach(correspondent => {
 | 
					    this.correspondentSelectionModel
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_CORRESPONDENT, value: correspondent.id?.toString()})
 | 
					      .getSelectedItems()
 | 
				
			||||||
 | 
					      .forEach((correspondent) => {
 | 
				
			||||||
 | 
					        filterRules.push({
 | 
				
			||||||
 | 
					          rule_type: FILTER_CORRESPONDENT,
 | 
				
			||||||
 | 
					          value: correspondent.id?.toString(),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    this.documentTypeSelectionModel
 | 
				
			||||||
 | 
					      .getSelectedItems()
 | 
				
			||||||
 | 
					      .forEach((documentType) => {
 | 
				
			||||||
 | 
					        filterRules.push({
 | 
				
			||||||
 | 
					          rule_type: FILTER_DOCUMENT_TYPE,
 | 
				
			||||||
 | 
					          value: documentType.id?.toString(),
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    this.documentTypeSelectionModel.getSelectedItems().forEach(documentType => {
 | 
					 | 
				
			||||||
      filterRules.push({rule_type: FILTER_DOCUMENT_TYPE, value: documentType.id?.toString()})
 | 
					 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    if (this.dateCreatedBefore) {
 | 
					    if (this.dateCreatedBefore) {
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_CREATED_BEFORE, value: this.dateCreatedBefore})
 | 
					      filterRules.push({
 | 
				
			||||||
 | 
					        rule_type: FILTER_CREATED_BEFORE,
 | 
				
			||||||
 | 
					        value: this.dateCreatedBefore,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this.dateCreatedAfter) {
 | 
					    if (this.dateCreatedAfter) {
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_CREATED_AFTER, value: this.dateCreatedAfter})
 | 
					      filterRules.push({
 | 
				
			||||||
 | 
					        rule_type: FILTER_CREATED_AFTER,
 | 
				
			||||||
 | 
					        value: this.dateCreatedAfter,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this.dateAddedBefore) {
 | 
					    if (this.dateAddedBefore) {
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_ADDED_BEFORE, value: this.dateAddedBefore})
 | 
					      filterRules.push({
 | 
				
			||||||
 | 
					        rule_type: FILTER_ADDED_BEFORE,
 | 
				
			||||||
 | 
					        value: this.dateAddedBefore,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this.dateAddedAfter) {
 | 
					    if (this.dateAddedAfter) {
 | 
				
			||||||
      filterRules.push({rule_type: FILTER_ADDED_AFTER, value: this.dateAddedAfter})
 | 
					      filterRules.push({
 | 
				
			||||||
 | 
					        rule_type: FILTER_ADDED_AFTER,
 | 
				
			||||||
 | 
					        value: this.dateAddedAfter,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return filterRules
 | 
					    return filterRules
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -260,14 +373,20 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    if (this._unmodifiedFilterRules.length != this._filterRules.length) {
 | 
					    if (this._unmodifiedFilterRules.length != this._filterRules.length) {
 | 
				
			||||||
      modified = true
 | 
					      modified = true
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      modified = this._unmodifiedFilterRules.some(rule => {
 | 
					      modified = this._unmodifiedFilterRules.some((rule) => {
 | 
				
			||||||
        return (this._filterRules.find(fri => fri.rule_type == rule.rule_type && fri.value == rule.value) == undefined)
 | 
					        return (
 | 
				
			||||||
 | 
					          this._filterRules.find(
 | 
				
			||||||
 | 
					            (fri) => fri.rule_type == rule.rule_type && fri.value == rule.value
 | 
				
			||||||
 | 
					          ) == undefined
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!modified) {
 | 
					      if (!modified) {
 | 
				
			||||||
        // only check other direction if we havent already determined is modified
 | 
					        // only check other direction if we havent already determined is modified
 | 
				
			||||||
        modified = this._filterRules.some(rule => {
 | 
					        modified = this._filterRules.some((rule) => {
 | 
				
			||||||
          this._unmodifiedFilterRules.find(fr => fr.rule_type == rule.rule_type && fr.value == rule.value) == undefined
 | 
					          this._unmodifiedFilterRules.find(
 | 
				
			||||||
 | 
					            (fr) => fr.rule_type == rule.rule_type && fr.value == rule.value
 | 
				
			||||||
 | 
					          ) == undefined
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -290,23 +409,27 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  subscription: Subscription
 | 
					  subscription: Subscription
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit() {
 | 
					  ngOnInit() {
 | 
				
			||||||
    this.tagService.listAll().subscribe(result => this.tags = result.results)
 | 
					    this.tagService
 | 
				
			||||||
    this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
 | 
					      .listAll()
 | 
				
			||||||
    this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
 | 
					      .subscribe((result) => (this.tags = result.results))
 | 
				
			||||||
 | 
					    this.correspondentService
 | 
				
			||||||
 | 
					      .listAll()
 | 
				
			||||||
 | 
					      .subscribe((result) => (this.correspondents = result.results))
 | 
				
			||||||
 | 
					    this.documentTypeService
 | 
				
			||||||
 | 
					      .listAll()
 | 
				
			||||||
 | 
					      .subscribe((result) => (this.documentTypes = result.results))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.textFilterDebounce = new Subject<string>()
 | 
					    this.textFilterDebounce = new Subject<string>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.subscription = this.textFilterDebounce.pipe(
 | 
					    this.subscription = this.textFilterDebounce
 | 
				
			||||||
      debounceTime(400),
 | 
					      .pipe(debounceTime(400), distinctUntilChanged())
 | 
				
			||||||
      distinctUntilChanged()
 | 
					      .subscribe((text) => {
 | 
				
			||||||
    ).subscribe(text => {
 | 
					 | 
				
			||||||
        this._textFilter = text
 | 
					        this._textFilter = text
 | 
				
			||||||
        this.documentService.searchQuery = text
 | 
					        this.documentService.searchQuery = text
 | 
				
			||||||
        this.updateRules()
 | 
					        this.updateRules()
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this._textFilter) this.documentService.searchQuery = this._textFilter
 | 
					    if (this._textFilter) this.documentService.searchQuery = this._textFilter
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnDestroy() {
 | 
					  ngOnDestroy() {
 | 
				
			||||||
@ -324,11 +447,17 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  addCorrespondent(correspondentId: number) {
 | 
					  addCorrespondent(correspondentId: number) {
 | 
				
			||||||
    this.correspondentSelectionModel.set(correspondentId, ToggleableItemState.Selected)
 | 
					    this.correspondentSelectionModel.set(
 | 
				
			||||||
 | 
					      correspondentId,
 | 
				
			||||||
 | 
					      ToggleableItemState.Selected
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  addDocumentType(documentTypeId: number) {
 | 
					  addDocumentType(documentTypeId: number) {
 | 
				
			||||||
    this.documentTypeSelectionModel.set(documentTypeId, ToggleableItemState.Selected)
 | 
					    this.documentTypeSelectionModel.set(
 | 
				
			||||||
 | 
					      documentTypeId,
 | 
				
			||||||
 | 
					      ToggleableItemState.Selected
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onTagsDropdownOpen() {
 | 
					  onTagsDropdownOpen() {
 | 
				
			||||||
@ -344,8 +473,11 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  changeTextFilterTarget(target) {
 | 
					  changeTextFilterTarget(target) {
 | 
				
			||||||
    if (this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE && target != TEXT_FILTER_TARGET_FULLTEXT_MORELIKE) {
 | 
					    if (
 | 
				
			||||||
      this._textFilter = ""
 | 
					      this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE &&
 | 
				
			||||||
 | 
					      target != TEXT_FILTER_TARGET_FULLTEXT_MORELIKE
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      this._textFilter = ''
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.textFilterTarget = target
 | 
					    this.textFilterTarget = target
 | 
				
			||||||
    this.textFilterInput.nativeElement.focus()
 | 
					    this.textFilterInput.nativeElement.focus()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { SaveViewConfigDialogComponent } from './save-view-config-dialog.component';
 | 
					import { SaveViewConfigDialogComponent } from './save-view-config-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('SaveViewConfigDialogComponent', () => {
 | 
					describe('SaveViewConfigDialogComponent', () => {
 | 
				
			||||||
  let component: SaveViewConfigDialogComponent;
 | 
					  let component: SaveViewConfigDialogComponent
 | 
				
			||||||
  let fixture: ComponentFixture<SaveViewConfigDialogComponent>;
 | 
					  let fixture: ComponentFixture<SaveViewConfigDialogComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ SaveViewConfigDialogComponent ]
 | 
					      declarations: [SaveViewConfigDialogComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(SaveViewConfigDialogComponent);
 | 
					    fixture = TestBed.createComponent(SaveViewConfigDialogComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,14 @@
 | 
				
			|||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 | 
					import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
 | 
				
			||||||
import { FormControl, FormGroup } from '@angular/forms';
 | 
					import { FormControl, FormGroup } from '@angular/forms'
 | 
				
			||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-save-view-config-dialog',
 | 
					  selector: 'app-save-view-config-dialog',
 | 
				
			||||||
  templateUrl: './save-view-config-dialog.component.html',
 | 
					  templateUrl: './save-view-config-dialog.component.html',
 | 
				
			||||||
  styleUrls: ['./save-view-config-dialog.component.scss']
 | 
					  styleUrls: ['./save-view-config-dialog.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class SaveViewConfigDialogComponent implements OnInit {
 | 
					export class SaveViewConfigDialogComponent implements OnInit {
 | 
				
			||||||
 | 
					  constructor(private modal: NgbActiveModal) {}
 | 
				
			||||||
  constructor(private modal: NgbActiveModal) { }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Output()
 | 
					  @Output()
 | 
				
			||||||
  public saveClicked = new EventEmitter()
 | 
					  public saveClicked = new EventEmitter()
 | 
				
			||||||
@ -22,7 +21,7 @@ export class SaveViewConfigDialogComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  closeEnabled = false
 | 
					  closeEnabled = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _defaultName = ""
 | 
					  _defaultName = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get defaultName() {
 | 
					  get defaultName() {
 | 
				
			||||||
    return this._defaultName
 | 
					    return this._defaultName
 | 
				
			||||||
@ -31,7 +30,7 @@ export class SaveViewConfigDialogComponent implements OnInit {
 | 
				
			|||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  set defaultName(value: string) {
 | 
					  set defaultName(value: string) {
 | 
				
			||||||
    this._defaultName = value
 | 
					    this._defaultName = value
 | 
				
			||||||
    this.saveViewConfigForm.patchValue({name: value})
 | 
					    this.saveViewConfigForm.patchValue({ name: value })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  saveViewConfigForm = new FormGroup({
 | 
					  saveViewConfigForm = new FormGroup({
 | 
				
			||||||
@ -44,7 +43,7 @@ export class SaveViewConfigDialogComponent implements OnInit {
 | 
				
			|||||||
    // wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM
 | 
					    // wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM
 | 
				
			||||||
    setTimeout(() => {
 | 
					    setTimeout(() => {
 | 
				
			||||||
      this.closeEnabled = true
 | 
					      this.closeEnabled = true
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  save() {
 | 
					  save() {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog.component';
 | 
					import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('CorrespondentEditDialogComponent', () => {
 | 
					describe('CorrespondentEditDialogComponent', () => {
 | 
				
			||||||
  let component: CorrespondentEditDialogComponent;
 | 
					  let component: CorrespondentEditDialogComponent
 | 
				
			||||||
  let fixture: ComponentFixture<CorrespondentEditDialogComponent>;
 | 
					  let fixture: ComponentFixture<CorrespondentEditDialogComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ CorrespondentEditDialogComponent ]
 | 
					      declarations: [CorrespondentEditDialogComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(CorrespondentEditDialogComponent);
 | 
					    fixture = TestBed.createComponent(CorrespondentEditDialogComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +1,22 @@
 | 
				
			|||||||
import { Component } from '@angular/core';
 | 
					import { Component } from '@angular/core'
 | 
				
			||||||
import { FormControl, FormGroup } from '@angular/forms';
 | 
					import { FormControl, FormGroup } from '@angular/forms'
 | 
				
			||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
 | 
					import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
 | 
				
			||||||
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
 | 
					import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
 | 
				
			||||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
 | 
					import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-correspondent-edit-dialog',
 | 
					  selector: 'app-correspondent-edit-dialog',
 | 
				
			||||||
  templateUrl: './correspondent-edit-dialog.component.html',
 | 
					  templateUrl: './correspondent-edit-dialog.component.html',
 | 
				
			||||||
  styleUrls: ['./correspondent-edit-dialog.component.scss']
 | 
					  styleUrls: ['./correspondent-edit-dialog.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class CorrespondentEditDialogComponent extends EditDialogComponent<PaperlessCorrespondent> {
 | 
					export class CorrespondentEditDialogComponent extends EditDialogComponent<PaperlessCorrespondent> {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
  constructor(service: CorrespondentService, activeModal: NgbActiveModal, toastService: ToastService) {
 | 
					    service: CorrespondentService,
 | 
				
			||||||
 | 
					    activeModal: NgbActiveModal,
 | 
				
			||||||
 | 
					    toastService: ToastService
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
    super(service, activeModal, toastService)
 | 
					    super(service, activeModal, toastService)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -29,9 +32,8 @@ export class CorrespondentEditDialogComponent extends EditDialogComponent<Paperl
 | 
				
			|||||||
    return new FormGroup({
 | 
					    return new FormGroup({
 | 
				
			||||||
      name: new FormControl(''),
 | 
					      name: new FormControl(''),
 | 
				
			||||||
      matching_algorithm: new FormControl(1),
 | 
					      matching_algorithm: new FormControl(1),
 | 
				
			||||||
      match: new FormControl(""),
 | 
					      match: new FormControl(''),
 | 
				
			||||||
      is_insensitive: new FormControl(true)
 | 
					      is_insensitive: new FormControl(true),
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { CorrespondentListComponent } from './correspondent-list.component';
 | 
					import { CorrespondentListComponent } from './correspondent-list.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('CorrespondentListComponent', () => {
 | 
					describe('CorrespondentListComponent', () => {
 | 
				
			||||||
  let component: CorrespondentListComponent;
 | 
					  let component: CorrespondentListComponent
 | 
				
			||||||
  let fixture: ComponentFixture<CorrespondentListComponent>;
 | 
					  let fixture: ComponentFixture<CorrespondentListComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ CorrespondentListComponent ]
 | 
					      declarations: [CorrespondentListComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(CorrespondentListComponent);
 | 
					    fixture = TestBed.createComponent(CorrespondentListComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,31 @@
 | 
				
			|||||||
import { Component } from '@angular/core';
 | 
					import { Component } from '@angular/core'
 | 
				
			||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { FILTER_CORRESPONDENT } from 'src/app/data/filter-rule-type';
 | 
					import { FILTER_CORRESPONDENT } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
 | 
					import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
 | 
					import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
import { GenericListComponent } from '../generic-list/generic-list.component';
 | 
					import { GenericListComponent } from '../generic-list/generic-list.component'
 | 
				
			||||||
import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog/correspondent-edit-dialog.component';
 | 
					import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog/correspondent-edit-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-correspondent-list',
 | 
					  selector: 'app-correspondent-list',
 | 
				
			||||||
  templateUrl: './correspondent-list.component.html',
 | 
					  templateUrl: './correspondent-list.component.html',
 | 
				
			||||||
  styleUrls: ['./correspondent-list.component.scss']
 | 
					  styleUrls: ['./correspondent-list.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class CorrespondentListComponent extends GenericListComponent<PaperlessCorrespondent> {
 | 
					export class CorrespondentListComponent extends GenericListComponent<PaperlessCorrespondent> {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
  constructor(correspondentsService: CorrespondentService, modalService: NgbModal,
 | 
					    correspondentsService: CorrespondentService,
 | 
				
			||||||
 | 
					    modalService: NgbModal,
 | 
				
			||||||
    private list: DocumentListViewService,
 | 
					    private list: DocumentListViewService,
 | 
				
			||||||
    toastService: ToastService
 | 
					    toastService: ToastService
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    super(correspondentsService,modalService,CorrespondentEditDialogComponent, toastService)
 | 
					    super(
 | 
				
			||||||
 | 
					      correspondentsService,
 | 
				
			||||||
 | 
					      modalService,
 | 
				
			||||||
 | 
					      CorrespondentEditDialogComponent,
 | 
				
			||||||
 | 
					      toastService
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getDeleteMessage(object: PaperlessCorrespondent) {
 | 
					  getDeleteMessage(object: PaperlessCorrespondent) {
 | 
				
			||||||
@ -27,6 +33,8 @@ export class CorrespondentListComponent extends GenericListComponent<PaperlessCo
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  filterDocuments(object: PaperlessCorrespondent) {
 | 
					  filterDocuments(object: PaperlessCorrespondent) {
 | 
				
			||||||
    this.list.quickFilter([{rule_type: FILTER_CORRESPONDENT, value: object.id.toString()}])
 | 
					    this.list.quickFilter([
 | 
				
			||||||
 | 
					      { rule_type: FILTER_CORRESPONDENT, value: object.id.toString() },
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog.component';
 | 
					import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DocumentTypeEditDialogComponent', () => {
 | 
					describe('DocumentTypeEditDialogComponent', () => {
 | 
				
			||||||
  let component: DocumentTypeEditDialogComponent;
 | 
					  let component: DocumentTypeEditDialogComponent
 | 
				
			||||||
  let fixture: ComponentFixture<DocumentTypeEditDialogComponent>;
 | 
					  let fixture: ComponentFixture<DocumentTypeEditDialogComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ DocumentTypeEditDialogComponent ]
 | 
					      declarations: [DocumentTypeEditDialogComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentTypeEditDialogComponent);
 | 
					    fixture = TestBed.createComponent(DocumentTypeEditDialogComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +1,22 @@
 | 
				
			|||||||
import { Component } from '@angular/core';
 | 
					import { Component } from '@angular/core'
 | 
				
			||||||
import { FormControl, FormGroup } from '@angular/forms';
 | 
					import { FormControl, FormGroup } from '@angular/forms'
 | 
				
			||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
 | 
					import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
 | 
				
			||||||
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
 | 
					import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
 | 
				
			||||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
 | 
					import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-document-type-edit-dialog',
 | 
					  selector: 'app-document-type-edit-dialog',
 | 
				
			||||||
  templateUrl: './document-type-edit-dialog.component.html',
 | 
					  templateUrl: './document-type-edit-dialog.component.html',
 | 
				
			||||||
  styleUrls: ['./document-type-edit-dialog.component.scss']
 | 
					  styleUrls: ['./document-type-edit-dialog.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DocumentTypeEditDialogComponent extends EditDialogComponent<PaperlessDocumentType> {
 | 
					export class DocumentTypeEditDialogComponent extends EditDialogComponent<PaperlessDocumentType> {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
  constructor(service: DocumentTypeService, activeModal: NgbActiveModal, toastService: ToastService) {
 | 
					    service: DocumentTypeService,
 | 
				
			||||||
 | 
					    activeModal: NgbActiveModal,
 | 
				
			||||||
 | 
					    toastService: ToastService
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
    super(service, activeModal, toastService)
 | 
					    super(service, activeModal, toastService)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -29,9 +32,8 @@ export class DocumentTypeEditDialogComponent extends EditDialogComponent<Paperle
 | 
				
			|||||||
    return new FormGroup({
 | 
					    return new FormGroup({
 | 
				
			||||||
      name: new FormControl(''),
 | 
					      name: new FormControl(''),
 | 
				
			||||||
      matching_algorithm: new FormControl(1),
 | 
					      matching_algorithm: new FormControl(1),
 | 
				
			||||||
      match: new FormControl(""),
 | 
					      match: new FormControl(''),
 | 
				
			||||||
      is_insensitive: new FormControl(true)
 | 
					      is_insensitive: new FormControl(true),
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentTypeListComponent } from './document-type-list.component';
 | 
					import { DocumentTypeListComponent } from './document-type-list.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DocumentTypeListComponent', () => {
 | 
					describe('DocumentTypeListComponent', () => {
 | 
				
			||||||
  let component: DocumentTypeListComponent;
 | 
					  let component: DocumentTypeListComponent
 | 
				
			||||||
  let fixture: ComponentFixture<DocumentTypeListComponent>;
 | 
					  let fixture: ComponentFixture<DocumentTypeListComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ DocumentTypeListComponent ]
 | 
					      declarations: [DocumentTypeListComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentTypeListComponent);
 | 
					    fixture = TestBed.createComponent(DocumentTypeListComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,22 @@
 | 
				
			|||||||
import { Component } from '@angular/core';
 | 
					import { Component } from '@angular/core'
 | 
				
			||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { FILTER_DOCUMENT_TYPE } from 'src/app/data/filter-rule-type';
 | 
					import { FILTER_DOCUMENT_TYPE } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
 | 
					import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
 | 
					import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
import { GenericListComponent } from '../generic-list/generic-list.component';
 | 
					import { GenericListComponent } from '../generic-list/generic-list.component'
 | 
				
			||||||
import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog/document-type-edit-dialog.component';
 | 
					import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog/document-type-edit-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-document-type-list',
 | 
					  selector: 'app-document-type-list',
 | 
				
			||||||
  templateUrl: './document-type-list.component.html',
 | 
					  templateUrl: './document-type-list.component.html',
 | 
				
			||||||
  styleUrls: ['./document-type-list.component.scss']
 | 
					  styleUrls: ['./document-type-list.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DocumentTypeListComponent extends GenericListComponent<PaperlessDocumentType> {
 | 
					export class DocumentTypeListComponent extends GenericListComponent<PaperlessDocumentType> {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
  constructor(service: DocumentTypeService, modalService: NgbModal,
 | 
					    service: DocumentTypeService,
 | 
				
			||||||
 | 
					    modalService: NgbModal,
 | 
				
			||||||
    private list: DocumentListViewService,
 | 
					    private list: DocumentListViewService,
 | 
				
			||||||
    toastService: ToastService
 | 
					    toastService: ToastService
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
@ -26,8 +27,9 @@ export class DocumentTypeListComponent extends GenericListComponent<PaperlessDoc
 | 
				
			|||||||
    return $localize`Do you really want to delete the document type "${object.name}"?`
 | 
					    return $localize`Do you really want to delete the document type "${object.name}"?`
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
  filterDocuments(object: PaperlessDocumentType) {
 | 
					  filterDocuments(object: PaperlessDocumentType) {
 | 
				
			||||||
    this.list.quickFilter([{rule_type: FILTER_DOCUMENT_TYPE, value: object.id.toString()}])
 | 
					    this.list.quickFilter([
 | 
				
			||||||
 | 
					      { rule_type: FILTER_DOCUMENT_TYPE, value: object.id.toString() },
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { GenericListComponent } from './generic-list.component';
 | 
					import { GenericListComponent } from './generic-list.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('GenericListComponent', () => {
 | 
					describe('GenericListComponent', () => {
 | 
				
			||||||
  let component: GenericListComponent;
 | 
					  let component: GenericListComponent
 | 
				
			||||||
  let fixture: ComponentFixture<GenericListComponent>;
 | 
					  let fixture: ComponentFixture<GenericListComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ GenericListComponent ]
 | 
					      declarations: [GenericListComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(GenericListComponent);
 | 
					    fixture = TestBed.createComponent(GenericListComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,39 @@
 | 
				
			|||||||
import { Directive, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					  Directive,
 | 
				
			||||||
import { Subject, Subscription } from 'rxjs';
 | 
					  OnDestroy,
 | 
				
			||||||
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
 | 
					  OnInit,
 | 
				
			||||||
import { MatchingModel, MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model';
 | 
					  QueryList,
 | 
				
			||||||
import { ObjectWithId } from 'src/app/data/object-with-id';
 | 
					  ViewChildren,
 | 
				
			||||||
import { SortableDirective, SortEvent } from 'src/app/directives/sortable.directive';
 | 
					} from '@angular/core'
 | 
				
			||||||
import { AbstractNameFilterService } from 'src/app/services/rest/abstract-name-filter-service';
 | 
					import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					import { Subject, Subscription } from 'rxjs'
 | 
				
			||||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component';
 | 
					import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  MatchingModel,
 | 
				
			||||||
 | 
					  MATCHING_ALGORITHMS,
 | 
				
			||||||
 | 
					  MATCH_AUTO,
 | 
				
			||||||
 | 
					} from 'src/app/data/matching-model'
 | 
				
			||||||
 | 
					import { ObjectWithId } from 'src/app/data/object-with-id'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  SortableDirective,
 | 
				
			||||||
 | 
					  SortEvent,
 | 
				
			||||||
 | 
					} from 'src/app/directives/sortable.directive'
 | 
				
			||||||
 | 
					import { AbstractNameFilterService } from 'src/app/services/rest/abstract-name-filter-service'
 | 
				
			||||||
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
 | 
					import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Directive()
 | 
					@Directive()
 | 
				
			||||||
export abstract class GenericListComponent<T extends ObjectWithId> implements OnInit, OnDestroy {
 | 
					export abstract class GenericListComponent<T extends ObjectWithId>
 | 
				
			||||||
 | 
					  implements OnInit, OnDestroy
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private service: AbstractNameFilterService<T>,
 | 
					    private service: AbstractNameFilterService<T>,
 | 
				
			||||||
    private modalService: NgbModal,
 | 
					    private modalService: NgbModal,
 | 
				
			||||||
    private editDialogComponent: any,
 | 
					    private editDialogComponent: any,
 | 
				
			||||||
    private toastService: ToastService) {
 | 
					    private toastService: ToastService
 | 
				
			||||||
    }
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @ViewChildren(SortableDirective) headers: QueryList<SortableDirective>;
 | 
					  @ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public data: T[] = []
 | 
					  public data: T[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -38,9 +52,11 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
 | 
				
			|||||||
    if (o.matching_algorithm == MATCH_AUTO) {
 | 
					    if (o.matching_algorithm == MATCH_AUTO) {
 | 
				
			||||||
      return $localize`Automatic`
 | 
					      return $localize`Automatic`
 | 
				
			||||||
    } else if (o.match && o.match.length > 0) {
 | 
					    } else if (o.match && o.match.length > 0) {
 | 
				
			||||||
      return `${MATCHING_ALGORITHMS.find(a => a.id == o.matching_algorithm).shortName}: ${o.match}`
 | 
					      return `${
 | 
				
			||||||
 | 
					        MATCHING_ALGORITHMS.find((a) => a.id == o.matching_algorithm).shortName
 | 
				
			||||||
 | 
					      }: ${o.match}`
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return "-"
 | 
					      return '-'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -50,16 +66,14 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
 | 
				
			|||||||
    this.reloadData()
 | 
					    this.reloadData()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.reloadData()
 | 
					    this.reloadData()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.nameFilterDebounce = new Subject<string>()
 | 
					    this.nameFilterDebounce = new Subject<string>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.subscription = this.nameFilterDebounce.pipe(
 | 
					    this.subscription = this.nameFilterDebounce
 | 
				
			||||||
      debounceTime(400),
 | 
					      .pipe(debounceTime(400), distinctUntilChanged())
 | 
				
			||||||
      distinctUntilChanged()
 | 
					      .subscribe((title) => {
 | 
				
			||||||
    ).subscribe(title => {
 | 
					 | 
				
			||||||
        this._nameFilter = title
 | 
					        this._nameFilter = title
 | 
				
			||||||
        this.page = 1
 | 
					        this.page = 1
 | 
				
			||||||
        this.reloadData()
 | 
					        this.reloadData()
 | 
				
			||||||
@ -71,25 +85,37 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reloadData() {
 | 
					  reloadData() {
 | 
				
			||||||
    this.service.listFiltered(this.page, null, this.sortField, this.sortReverse, this._nameFilter).subscribe(c => {
 | 
					    this.service
 | 
				
			||||||
 | 
					      .listFiltered(
 | 
				
			||||||
 | 
					        this.page,
 | 
				
			||||||
 | 
					        null,
 | 
				
			||||||
 | 
					        this.sortField,
 | 
				
			||||||
 | 
					        this.sortReverse,
 | 
				
			||||||
 | 
					        this._nameFilter
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      .subscribe((c) => {
 | 
				
			||||||
        this.data = c.results
 | 
					        this.data = c.results
 | 
				
			||||||
        this.collectionSize = c.count
 | 
					        this.collectionSize = c.count
 | 
				
			||||||
    });
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  openCreateDialog() {
 | 
					  openCreateDialog() {
 | 
				
			||||||
    var activeModal = this.modalService.open(this.editDialogComponent, {backdrop: 'static'})
 | 
					    var activeModal = this.modalService.open(this.editDialogComponent, {
 | 
				
			||||||
 | 
					      backdrop: 'static',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    activeModal.componentInstance.dialogMode = 'create'
 | 
					    activeModal.componentInstance.dialogMode = 'create'
 | 
				
			||||||
    activeModal.componentInstance.success.subscribe(o => {
 | 
					    activeModal.componentInstance.success.subscribe((o) => {
 | 
				
			||||||
      this.reloadData()
 | 
					      this.reloadData()
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  openEditDialog(object: T) {
 | 
					  openEditDialog(object: T) {
 | 
				
			||||||
    var activeModal = this.modalService.open(this.editDialogComponent, {backdrop: 'static'})
 | 
					    var activeModal = this.modalService.open(this.editDialogComponent, {
 | 
				
			||||||
 | 
					      backdrop: 'static',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    activeModal.componentInstance.object = object
 | 
					    activeModal.componentInstance.object = object
 | 
				
			||||||
    activeModal.componentInstance.dialogMode = 'edit'
 | 
					    activeModal.componentInstance.dialogMode = 'edit'
 | 
				
			||||||
    activeModal.componentInstance.success.subscribe(o => {
 | 
					    activeModal.componentInstance.success.subscribe((o) => {
 | 
				
			||||||
      this.reloadData()
 | 
					      this.reloadData()
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -99,23 +125,31 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  openDeleteDialog(object: T) {
 | 
					  openDeleteDialog(object: T) {
 | 
				
			||||||
    var activeModal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
 | 
					    var activeModal = this.modalService.open(ConfirmDialogComponent, {
 | 
				
			||||||
 | 
					      backdrop: 'static',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    activeModal.componentInstance.title = $localize`Confirm delete`
 | 
					    activeModal.componentInstance.title = $localize`Confirm delete`
 | 
				
			||||||
    activeModal.componentInstance.messageBold = this.getDeleteMessage(object)
 | 
					    activeModal.componentInstance.messageBold = this.getDeleteMessage(object)
 | 
				
			||||||
    activeModal.componentInstance.message = $localize`Associated documents will not be deleted.`
 | 
					    activeModal.componentInstance.message = $localize`Associated documents will not be deleted.`
 | 
				
			||||||
    activeModal.componentInstance.btnClass = "btn-danger"
 | 
					    activeModal.componentInstance.btnClass = 'btn-danger'
 | 
				
			||||||
    activeModal.componentInstance.btnCaption = $localize`Delete`
 | 
					    activeModal.componentInstance.btnCaption = $localize`Delete`
 | 
				
			||||||
    activeModal.componentInstance.confirmClicked.subscribe(() => {
 | 
					    activeModal.componentInstance.confirmClicked.subscribe(() => {
 | 
				
			||||||
      activeModal.componentInstance.buttonsEnabled = false
 | 
					      activeModal.componentInstance.buttonsEnabled = false
 | 
				
			||||||
      this.service.delete(object).subscribe(_ => {
 | 
					      this.service.delete(object).subscribe(
 | 
				
			||||||
 | 
					        (_) => {
 | 
				
			||||||
          activeModal.close()
 | 
					          activeModal.close()
 | 
				
			||||||
          this.reloadData()
 | 
					          this.reloadData()
 | 
				
			||||||
      }, error => {
 | 
					        },
 | 
				
			||||||
 | 
					        (error) => {
 | 
				
			||||||
          activeModal.componentInstance.buttonsEnabled = true
 | 
					          activeModal.componentInstance.buttonsEnabled = true
 | 
				
			||||||
        this.toastService.showError($localize`Error while deleting element: ${JSON.stringify(error.error)}`)
 | 
					          this.toastService.showError(
 | 
				
			||||||
      })
 | 
					            $localize`Error while deleting element: ${JSON.stringify(
 | 
				
			||||||
 | 
					              error.error
 | 
				
			||||||
 | 
					            )}`
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get nameFilter() {
 | 
					  get nameFilter() {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { LogsComponent } from './logs.component';
 | 
					import { LogsComponent } from './logs.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('LogsComponent', () => {
 | 
					describe('LogsComponent', () => {
 | 
				
			||||||
  let component: LogsComponent;
 | 
					  let component: LogsComponent
 | 
				
			||||||
  let fixture: ComponentFixture<LogsComponent>;
 | 
					  let fixture: ComponentFixture<LogsComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ LogsComponent ]
 | 
					      declarations: [LogsComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(LogsComponent);
 | 
					    fixture = TestBed.createComponent(LogsComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,19 @@
 | 
				
			|||||||
import { Component, ElementRef, OnInit, AfterViewChecked, ViewChild } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { LogService } from 'src/app/services/rest/log.service';
 | 
					  Component,
 | 
				
			||||||
 | 
					  ElementRef,
 | 
				
			||||||
 | 
					  OnInit,
 | 
				
			||||||
 | 
					  AfterViewChecked,
 | 
				
			||||||
 | 
					  ViewChild,
 | 
				
			||||||
 | 
					} from '@angular/core'
 | 
				
			||||||
 | 
					import { LogService } from 'src/app/services/rest/log.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-logs',
 | 
					  selector: 'app-logs',
 | 
				
			||||||
  templateUrl: './logs.component.html',
 | 
					  templateUrl: './logs.component.html',
 | 
				
			||||||
  styleUrls: ['./logs.component.scss']
 | 
					  styleUrls: ['./logs.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class LogsComponent implements OnInit, AfterViewChecked {
 | 
					export class LogsComponent implements OnInit, AfterViewChecked {
 | 
				
			||||||
 | 
					  constructor(private logService: LogService) {}
 | 
				
			||||||
  constructor(private logService: LogService) { }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  logs: string[] = []
 | 
					  logs: string[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -19,7 +24,7 @@ export class LogsComponent implements OnInit, AfterViewChecked {
 | 
				
			|||||||
  @ViewChild('logContainer') logContainer: ElementRef
 | 
					  @ViewChild('logContainer') logContainer: ElementRef
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.logService.list().subscribe(result => {
 | 
					    this.logService.list().subscribe((result) => {
 | 
				
			||||||
      this.logFiles = result
 | 
					      this.logFiles = result
 | 
				
			||||||
      if (this.logFiles.length > 0) {
 | 
					      if (this.logFiles.length > 0) {
 | 
				
			||||||
        this.activeLog = this.logFiles[0]
 | 
					        this.activeLog = this.logFiles[0]
 | 
				
			||||||
@ -29,25 +34,28 @@ export class LogsComponent implements OnInit, AfterViewChecked {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngAfterViewChecked() {
 | 
					  ngAfterViewChecked() {
 | 
				
			||||||
    this.scrollToBottom();
 | 
					    this.scrollToBottom()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reloadLogs() {
 | 
					  reloadLogs() {
 | 
				
			||||||
    this.logService.get(this.activeLog).subscribe(result => {
 | 
					    this.logService.get(this.activeLog).subscribe(
 | 
				
			||||||
 | 
					      (result) => {
 | 
				
			||||||
        this.logs = result
 | 
					        this.logs = result
 | 
				
			||||||
    }, error => {
 | 
					      },
 | 
				
			||||||
 | 
					      (error) => {
 | 
				
			||||||
        this.logs = []
 | 
					        this.logs = []
 | 
				
			||||||
    })
 | 
					      }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getLogLevel(log: string) {
 | 
					  getLogLevel(log: string) {
 | 
				
			||||||
    if (log.indexOf("[DEBUG]") != -1) {
 | 
					    if (log.indexOf('[DEBUG]') != -1) {
 | 
				
			||||||
      return 10
 | 
					      return 10
 | 
				
			||||||
    } else if (log.indexOf("[WARNING]") != -1) {
 | 
					    } else if (log.indexOf('[WARNING]') != -1) {
 | 
				
			||||||
      return 30
 | 
					      return 30
 | 
				
			||||||
    } else if (log.indexOf("[ERROR]") != -1) {
 | 
					    } else if (log.indexOf('[ERROR]') != -1) {
 | 
				
			||||||
      return 40
 | 
					      return 40
 | 
				
			||||||
    } else if (log.indexOf("[CRITICAL]") != -1) {
 | 
					    } else if (log.indexOf('[CRITICAL]') != -1) {
 | 
				
			||||||
      return 50
 | 
					      return 50
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return 20
 | 
					      return 20
 | 
				
			||||||
@ -58,8 +66,7 @@ export class LogsComponent implements OnInit, AfterViewChecked {
 | 
				
			|||||||
    this.logContainer?.nativeElement.scroll({
 | 
					    this.logContainer?.nativeElement.scroll({
 | 
				
			||||||
      top: this.logContainer.nativeElement.scrollHeight,
 | 
					      top: this.logContainer.nativeElement.scrollHeight,
 | 
				
			||||||
      left: 0,
 | 
					      left: 0,
 | 
				
			||||||
      behavior: 'auto'
 | 
					      behavior: 'auto',
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { SettingsComponent } from './settings.component';
 | 
					import { SettingsComponent } from './settings.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('SettingsComponent', () => {
 | 
					describe('SettingsComponent', () => {
 | 
				
			||||||
  let component: SettingsComponent;
 | 
					  let component: SettingsComponent
 | 
				
			||||||
  let fixture: ComponentFixture<SettingsComponent>;
 | 
					  let fixture: ComponentFixture<SettingsComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ SettingsComponent ]
 | 
					      declarations: [SettingsComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(SettingsComponent);
 | 
					    fixture = TestBed.createComponent(SettingsComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,39 +1,49 @@
 | 
				
			|||||||
import { Component, Inject, LOCALE_ID, OnInit, OnDestroy, Renderer2 } from '@angular/core';
 | 
					import {
 | 
				
			||||||
import { FormControl, FormGroup } from '@angular/forms';
 | 
					  Component,
 | 
				
			||||||
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
 | 
					  Inject,
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
 | 
					  LOCALE_ID,
 | 
				
			||||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
 | 
					  OnInit,
 | 
				
			||||||
import { LanguageOption, SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
 | 
					  OnDestroy,
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					  Renderer2,
 | 
				
			||||||
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms';
 | 
					} from '@angular/core'
 | 
				
			||||||
import { Observable, Subscription, BehaviorSubject } from 'rxjs';
 | 
					import { FormControl, FormGroup } from '@angular/forms'
 | 
				
			||||||
 | 
					import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
 | 
				
			||||||
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
 | 
					import { SavedViewService } from 'src/app/services/rest/saved-view.service'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  LanguageOption,
 | 
				
			||||||
 | 
					  SettingsService,
 | 
				
			||||||
 | 
					  SETTINGS_KEYS,
 | 
				
			||||||
 | 
					} from 'src/app/services/settings.service'
 | 
				
			||||||
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
 | 
					import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'
 | 
				
			||||||
 | 
					import { Observable, Subscription, BehaviorSubject } from 'rxjs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-settings',
 | 
					  selector: 'app-settings',
 | 
				
			||||||
  templateUrl: './settings.component.html',
 | 
					  templateUrl: './settings.component.html',
 | 
				
			||||||
  styleUrls: ['./settings.component.scss']
 | 
					  styleUrls: ['./settings.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
 | 
					export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  savedViewGroup = new FormGroup({})
 | 
					  savedViewGroup = new FormGroup({})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  settingsForm = new FormGroup({
 | 
					  settingsForm = new FormGroup({
 | 
				
			||||||
    'bulkEditConfirmationDialogs': new FormControl(null),
 | 
					    bulkEditConfirmationDialogs: new FormControl(null),
 | 
				
			||||||
    'bulkEditApplyOnClose': new FormControl(null),
 | 
					    bulkEditApplyOnClose: new FormControl(null),
 | 
				
			||||||
    'documentListItemPerPage': new FormControl(null),
 | 
					    documentListItemPerPage: new FormControl(null),
 | 
				
			||||||
    'darkModeUseSystem': new FormControl(null),
 | 
					    darkModeUseSystem: new FormControl(null),
 | 
				
			||||||
    'darkModeEnabled': new FormControl(null),
 | 
					    darkModeEnabled: new FormControl(null),
 | 
				
			||||||
    'darkModeInvertThumbs': new FormControl(null),
 | 
					    darkModeInvertThumbs: new FormControl(null),
 | 
				
			||||||
    'themeColor': new FormControl(null),
 | 
					    themeColor: new FormControl(null),
 | 
				
			||||||
    'useNativePdfViewer': new FormControl(null),
 | 
					    useNativePdfViewer: new FormControl(null),
 | 
				
			||||||
    'savedViews': this.savedViewGroup,
 | 
					    savedViews: this.savedViewGroup,
 | 
				
			||||||
    'displayLanguage': new FormControl(null),
 | 
					    displayLanguage: new FormControl(null),
 | 
				
			||||||
    'dateLocale': new FormControl(null),
 | 
					    dateLocale: new FormControl(null),
 | 
				
			||||||
    'dateFormat': new FormControl(null),
 | 
					    dateFormat: new FormControl(null),
 | 
				
			||||||
    'notificationsConsumerNewDocument': new FormControl(null),
 | 
					    notificationsConsumerNewDocument: new FormControl(null),
 | 
				
			||||||
    'notificationsConsumerSuccess': new FormControl(null),
 | 
					    notificationsConsumerSuccess: new FormControl(null),
 | 
				
			||||||
    'notificationsConsumerFailed': new FormControl(null),
 | 
					    notificationsConsumerFailed: new FormControl(null),
 | 
				
			||||||
    'notificationsConsumerSuppressOnDashboard': new FormControl(null),
 | 
					    notificationsConsumerSuppressOnDashboard: new FormControl(null),
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  savedViews: PaperlessSavedView[]
 | 
					  savedViews: PaperlessSavedView[]
 | 
				
			||||||
@ -44,7 +54,11 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
 | 
				
			|||||||
  isDirty: Boolean = false
 | 
					  isDirty: Boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get computedDateLocale(): string {
 | 
					  get computedDateLocale(): string {
 | 
				
			||||||
    return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage || this.currentLocale
 | 
					    return (
 | 
				
			||||||
 | 
					      this.settingsForm.value.dateLocale ||
 | 
				
			||||||
 | 
					      this.settingsForm.value.displayLanguage ||
 | 
				
			||||||
 | 
					      this.currentLocale
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
@ -53,48 +67,71 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
 | 
				
			|||||||
    private toastService: ToastService,
 | 
					    private toastService: ToastService,
 | 
				
			||||||
    private settings: SettingsService,
 | 
					    private settings: SettingsService,
 | 
				
			||||||
    @Inject(LOCALE_ID) public currentLocale: string
 | 
					    @Inject(LOCALE_ID) public currentLocale: string
 | 
				
			||||||
  ) { }
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit() {
 | 
					  ngOnInit() {
 | 
				
			||||||
    this.savedViewService.listAll().subscribe(r => {
 | 
					    this.savedViewService.listAll().subscribe((r) => {
 | 
				
			||||||
      this.savedViews = r.results
 | 
					      this.savedViews = r.results
 | 
				
			||||||
      let storeData = {
 | 
					      let storeData = {
 | 
				
			||||||
        'bulkEditConfirmationDialogs': this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS),
 | 
					        bulkEditConfirmationDialogs: this.settings.get(
 | 
				
			||||||
        'bulkEditApplyOnClose': this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE),
 | 
					          SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS
 | 
				
			||||||
        'documentListItemPerPage': this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE),
 | 
					        ),
 | 
				
			||||||
        'darkModeUseSystem': this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM),
 | 
					        bulkEditApplyOnClose: this.settings.get(
 | 
				
			||||||
        'darkModeEnabled': this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED),
 | 
					          SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE
 | 
				
			||||||
        'darkModeInvertThumbs': this.settings.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED),
 | 
					        ),
 | 
				
			||||||
        'themeColor': this.settings.get(SETTINGS_KEYS.THEME_COLOR),
 | 
					        documentListItemPerPage: this.settings.get(
 | 
				
			||||||
        'useNativePdfViewer': this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER),
 | 
					          SETTINGS_KEYS.DOCUMENT_LIST_SIZE
 | 
				
			||||||
        'savedViews': {},
 | 
					        ),
 | 
				
			||||||
        'displayLanguage': this.settings.getLanguage(),
 | 
					        darkModeUseSystem: this.settings.get(
 | 
				
			||||||
        'dateLocale': this.settings.get(SETTINGS_KEYS.DATE_LOCALE),
 | 
					          SETTINGS_KEYS.DARK_MODE_USE_SYSTEM
 | 
				
			||||||
        'dateFormat': this.settings.get(SETTINGS_KEYS.DATE_FORMAT),
 | 
					        ),
 | 
				
			||||||
        'notificationsConsumerNewDocument': this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT),
 | 
					        darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED),
 | 
				
			||||||
        'notificationsConsumerSuccess': this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS),
 | 
					        darkModeInvertThumbs: this.settings.get(
 | 
				
			||||||
        'notificationsConsumerFailed': this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED),
 | 
					          SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED
 | 
				
			||||||
        'notificationsConsumerSuppressOnDashboard': this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD),
 | 
					        ),
 | 
				
			||||||
 | 
					        themeColor: this.settings.get(SETTINGS_KEYS.THEME_COLOR),
 | 
				
			||||||
 | 
					        useNativePdfViewer: this.settings.get(
 | 
				
			||||||
 | 
					          SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        savedViews: {},
 | 
				
			||||||
 | 
					        displayLanguage: this.settings.getLanguage(),
 | 
				
			||||||
 | 
					        dateLocale: this.settings.get(SETTINGS_KEYS.DATE_LOCALE),
 | 
				
			||||||
 | 
					        dateFormat: this.settings.get(SETTINGS_KEYS.DATE_FORMAT),
 | 
				
			||||||
 | 
					        notificationsConsumerNewDocument: this.settings.get(
 | 
				
			||||||
 | 
					          SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        notificationsConsumerSuccess: this.settings.get(
 | 
				
			||||||
 | 
					          SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        notificationsConsumerFailed: this.settings.get(
 | 
				
			||||||
 | 
					          SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        notificationsConsumerSuppressOnDashboard: this.settings.get(
 | 
				
			||||||
 | 
					          SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      for (let view of this.savedViews) {
 | 
					      for (let view of this.savedViews) {
 | 
				
			||||||
        storeData.savedViews[view.id.toString()] = {
 | 
					        storeData.savedViews[view.id.toString()] = {
 | 
				
			||||||
          "id": view.id,
 | 
					          id: view.id,
 | 
				
			||||||
          "name": view.name,
 | 
					          name: view.name,
 | 
				
			||||||
          "show_on_dashboard": view.show_on_dashboard,
 | 
					          show_on_dashboard: view.show_on_dashboard,
 | 
				
			||||||
          "show_in_sidebar": view.show_in_sidebar
 | 
					          show_in_sidebar: view.show_in_sidebar,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.savedViewGroup.addControl(view.id.toString(), new FormGroup({
 | 
					        this.savedViewGroup.addControl(
 | 
				
			||||||
          "id": new FormControl(null),
 | 
					          view.id.toString(),
 | 
				
			||||||
          "name": new FormControl(null),
 | 
					          new FormGroup({
 | 
				
			||||||
          "show_on_dashboard": new FormControl(null),
 | 
					            id: new FormControl(null),
 | 
				
			||||||
          "show_in_sidebar": new FormControl(null)
 | 
					            name: new FormControl(null),
 | 
				
			||||||
        }))
 | 
					            show_on_dashboard: new FormControl(null),
 | 
				
			||||||
 | 
					            show_in_sidebar: new FormControl(null),
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.store = new BehaviorSubject(storeData)
 | 
					      this.store = new BehaviorSubject(storeData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.storeSub = this.store.asObservable().subscribe(state => {
 | 
					      this.storeSub = this.store.asObservable().subscribe((state) => {
 | 
				
			||||||
        this.settingsForm.patchValue(state, { emitEvent: false })
 | 
					        this.settingsForm.patchValue(state, { emitEvent: false })
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -102,45 +139,93 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
 | 
				
			|||||||
      this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable())
 | 
					      this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Record dirty in case we need to 'undo' appearance settings if not saved on close
 | 
					      // Record dirty in case we need to 'undo' appearance settings if not saved on close
 | 
				
			||||||
      this.isDirty$.subscribe(dirty => {
 | 
					      this.isDirty$.subscribe((dirty) => {
 | 
				
			||||||
        this.isDirty = dirty
 | 
					        this.isDirty = dirty
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // "Live" visual changes prior to save
 | 
					      // "Live" visual changes prior to save
 | 
				
			||||||
      this.settingsForm.valueChanges.subscribe(() => {
 | 
					      this.settingsForm.valueChanges.subscribe(() => {
 | 
				
			||||||
        this.settings.updateAppearanceSettings(this.settingsForm.get('darkModeUseSystem').value, this.settingsForm.get('darkModeEnabled').value, this.settingsForm.get('themeColor').value)
 | 
					        this.settings.updateAppearanceSettings(
 | 
				
			||||||
 | 
					          this.settingsForm.get('darkModeUseSystem').value,
 | 
				
			||||||
 | 
					          this.settingsForm.get('darkModeEnabled').value,
 | 
				
			||||||
 | 
					          this.settingsForm.get('themeColor').value
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnDestroy() {
 | 
					  ngOnDestroy() {
 | 
				
			||||||
    if (this.isDirty) this.settings.updateAppearanceSettings() // in case user changed appearance but didnt save
 | 
					    if (this.isDirty) this.settings.updateAppearanceSettings() // in case user changed appearance but didnt save
 | 
				
			||||||
    this.storeSub && this.storeSub.unsubscribe();
 | 
					    this.storeSub && this.storeSub.unsubscribe()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  deleteSavedView(savedView: PaperlessSavedView) {
 | 
					  deleteSavedView(savedView: PaperlessSavedView) {
 | 
				
			||||||
    this.savedViewService.delete(savedView).subscribe(() => {
 | 
					    this.savedViewService.delete(savedView).subscribe(() => {
 | 
				
			||||||
      this.savedViewGroup.removeControl(savedView.id.toString())
 | 
					      this.savedViewGroup.removeControl(savedView.id.toString())
 | 
				
			||||||
      this.savedViews.splice(this.savedViews.indexOf(savedView), 1)
 | 
					      this.savedViews.splice(this.savedViews.indexOf(savedView), 1)
 | 
				
			||||||
      this.toastService.showInfo($localize`Saved view "${savedView.name}" deleted.`)
 | 
					      this.toastService.showInfo(
 | 
				
			||||||
 | 
					        $localize`Saved view "${savedView.name}" deleted.`
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private saveLocalSettings() {
 | 
					  private saveLocalSettings() {
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE, this.settingsForm.value.bulkEditApplyOnClose)
 | 
					    this.settings.set(
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS, this.settingsForm.value.bulkEditConfirmationDialogs)
 | 
					      SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE,
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage)
 | 
					      this.settingsForm.value.bulkEditApplyOnClose
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem)
 | 
					    )
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString())
 | 
					    this.settings.set(
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED, (this.settingsForm.value.darkModeInvertThumbs == true).toString())
 | 
					      SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS,
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.THEME_COLOR, (this.settingsForm.value.themeColor).toString())
 | 
					      this.settingsForm.value.bulkEditConfirmationDialogs
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, this.settingsForm.value.useNativePdfViewer)
 | 
					    )
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.DATE_LOCALE, this.settingsForm.value.dateLocale)
 | 
					    this.settings.set(
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.DATE_FORMAT, this.settingsForm.value.dateFormat)
 | 
					      SETTINGS_KEYS.DOCUMENT_LIST_SIZE,
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT, this.settingsForm.value.notificationsConsumerNewDocument)
 | 
					      this.settingsForm.value.documentListItemPerPage
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS, this.settingsForm.value.notificationsConsumerSuccess)
 | 
					    )
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED, this.settingsForm.value.notificationsConsumerFailed)
 | 
					    this.settings.set(
 | 
				
			||||||
    this.settings.set(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD, this.settingsForm.value.notificationsConsumerSuppressOnDashboard)
 | 
					      SETTINGS_KEYS.DARK_MODE_USE_SYSTEM,
 | 
				
			||||||
 | 
					      this.settingsForm.value.darkModeUseSystem
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.DARK_MODE_ENABLED,
 | 
				
			||||||
 | 
					      (this.settingsForm.value.darkModeEnabled == true).toString()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED,
 | 
				
			||||||
 | 
					      (this.settingsForm.value.darkModeInvertThumbs == true).toString()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.THEME_COLOR,
 | 
				
			||||||
 | 
					      this.settingsForm.value.themeColor.toString()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER,
 | 
				
			||||||
 | 
					      this.settingsForm.value.useNativePdfViewer
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.DATE_LOCALE,
 | 
				
			||||||
 | 
					      this.settingsForm.value.dateLocale
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.DATE_FORMAT,
 | 
				
			||||||
 | 
					      this.settingsForm.value.dateFormat
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT,
 | 
				
			||||||
 | 
					      this.settingsForm.value.notificationsConsumerNewDocument
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS,
 | 
				
			||||||
 | 
					      this.settingsForm.value.notificationsConsumerSuccess
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED,
 | 
				
			||||||
 | 
					      this.settingsForm.value.notificationsConsumerFailed
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.settings.set(
 | 
				
			||||||
 | 
					      SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD,
 | 
				
			||||||
 | 
					      this.settingsForm.value.notificationsConsumerSuppressOnDashboard
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    this.settings.setLanguage(this.settingsForm.value.displayLanguage)
 | 
					    this.settings.setLanguage(this.settingsForm.value.displayLanguage)
 | 
				
			||||||
    this.store.next(this.settingsForm.value)
 | 
					    this.store.next(this.settingsForm.value)
 | 
				
			||||||
    this.documentListViewService.updatePageSize()
 | 
					    this.documentListViewService.updatePageSize()
 | 
				
			||||||
@ -149,14 +234,14 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get displayLanguageOptions(): LanguageOption[] {
 | 
					  get displayLanguageOptions(): LanguageOption[] {
 | 
				
			||||||
    return [
 | 
					    return [{ code: '', name: $localize`Use system language` }].concat(
 | 
				
			||||||
      {code: "", name: $localize`Use system language`}
 | 
					      this.settings.getLanguageOptions()
 | 
				
			||||||
    ].concat(this.settings.getLanguageOptions())
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get dateLocaleOptions(): LanguageOption[] {
 | 
					  get dateLocaleOptions(): LanguageOption[] {
 | 
				
			||||||
    return [
 | 
					    return [
 | 
				
			||||||
      {code: "", name: $localize`Use date format of display language`}
 | 
					      { code: '', name: $localize`Use date format of display language` },
 | 
				
			||||||
    ].concat(this.settings.getDateLocaleOptions())
 | 
					    ].concat(this.settings.getDateLocaleOptions())
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -170,18 +255,24 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
 | 
				
			|||||||
      x.push(this.savedViewGroup.value[id])
 | 
					      x.push(this.savedViewGroup.value[id])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (x.length > 0) {
 | 
					    if (x.length > 0) {
 | 
				
			||||||
      this.savedViewService.patchMany(x).subscribe(s => {
 | 
					      this.savedViewService.patchMany(x).subscribe(
 | 
				
			||||||
 | 
					        (s) => {
 | 
				
			||||||
          this.saveLocalSettings()
 | 
					          this.saveLocalSettings()
 | 
				
			||||||
      }, error => {
 | 
					        },
 | 
				
			||||||
        this.toastService.showError($localize`Error while storing settings on server: ${JSON.stringify(error.error)}`)
 | 
					        (error) => {
 | 
				
			||||||
      })
 | 
					          this.toastService.showError(
 | 
				
			||||||
 | 
					            $localize`Error while storing settings on server: ${JSON.stringify(
 | 
				
			||||||
 | 
					              error.error
 | 
				
			||||||
 | 
					            )}`
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      this.saveLocalSettings()
 | 
					      this.saveLocalSettings()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clearThemeColor() {
 | 
					  clearThemeColor() {
 | 
				
			||||||
    this.settingsForm.get('themeColor').patchValue('');
 | 
					    this.settingsForm.get('themeColor').patchValue('')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { TagEditDialogComponent } from './tag-edit-dialog.component';
 | 
					import { TagEditDialogComponent } from './tag-edit-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('TagEditDialogComponent', () => {
 | 
					describe('TagEditDialogComponent', () => {
 | 
				
			||||||
  let component: TagEditDialogComponent;
 | 
					  let component: TagEditDialogComponent
 | 
				
			||||||
  let fixture: ComponentFixture<TagEditDialogComponent>;
 | 
					  let fixture: ComponentFixture<TagEditDialogComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ TagEditDialogComponent ]
 | 
					      declarations: [TagEditDialogComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(TagEditDialogComponent);
 | 
					    fixture = TestBed.createComponent(TagEditDialogComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,23 @@
 | 
				
			|||||||
import { Component } from '@angular/core';
 | 
					import { Component } from '@angular/core'
 | 
				
			||||||
import { FormControl, FormGroup } from '@angular/forms';
 | 
					import { FormControl, FormGroup } from '@angular/forms'
 | 
				
			||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
 | 
					import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
 | 
				
			||||||
import { PaperlessTag } from 'src/app/data/paperless-tag';
 | 
					import { PaperlessTag } from 'src/app/data/paperless-tag'
 | 
				
			||||||
import { TagService } from 'src/app/services/rest/tag.service';
 | 
					import { TagService } from 'src/app/services/rest/tag.service'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
import { randomColor } from 'src/app/utils/color';
 | 
					import { randomColor } from 'src/app/utils/color'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-tag-edit-dialog',
 | 
					  selector: 'app-tag-edit-dialog',
 | 
				
			||||||
  templateUrl: './tag-edit-dialog.component.html',
 | 
					  templateUrl: './tag-edit-dialog.component.html',
 | 
				
			||||||
  styleUrls: ['./tag-edit-dialog.component.scss']
 | 
					  styleUrls: ['./tag-edit-dialog.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
 | 
					export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
  constructor(service: TagService, activeModal: NgbActiveModal, toastService: ToastService) {
 | 
					    service: TagService,
 | 
				
			||||||
 | 
					    activeModal: NgbActiveModal,
 | 
				
			||||||
 | 
					    toastService: ToastService
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
    super(service, activeModal, toastService)
 | 
					    super(service, activeModal, toastService)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -32,9 +35,8 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
 | 
				
			|||||||
      color: new FormControl(randomColor()),
 | 
					      color: new FormControl(randomColor()),
 | 
				
			||||||
      is_inbox_tag: new FormControl(false),
 | 
					      is_inbox_tag: new FormControl(false),
 | 
				
			||||||
      matching_algorithm: new FormControl(1),
 | 
					      matching_algorithm: new FormControl(1),
 | 
				
			||||||
      match: new FormControl(""),
 | 
					      match: new FormControl(''),
 | 
				
			||||||
      is_insensitive: new FormControl(true)
 | 
					      is_insensitive: new FormControl(true),
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { TagListComponent } from './tag-list.component';
 | 
					import { TagListComponent } from './tag-list.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('TagListComponent', () => {
 | 
					describe('TagListComponent', () => {
 | 
				
			||||||
  let component: TagListComponent;
 | 
					  let component: TagListComponent
 | 
				
			||||||
  let fixture: ComponentFixture<TagListComponent>;
 | 
					  let fixture: ComponentFixture<TagListComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ TagListComponent ]
 | 
					      declarations: [TagListComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(TagListComponent);
 | 
					    fixture = TestBed.createComponent(TagListComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,22 @@
 | 
				
			|||||||
import { Component } from '@angular/core';
 | 
					import { Component } from '@angular/core'
 | 
				
			||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type';
 | 
					import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
import { PaperlessTag } from 'src/app/data/paperless-tag';
 | 
					import { PaperlessTag } from 'src/app/data/paperless-tag'
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
import { TagService } from 'src/app/services/rest/tag.service';
 | 
					import { TagService } from 'src/app/services/rest/tag.service'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service';
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
import { GenericListComponent } from '../generic-list/generic-list.component';
 | 
					import { GenericListComponent } from '../generic-list/generic-list.component'
 | 
				
			||||||
import { TagEditDialogComponent } from './tag-edit-dialog/tag-edit-dialog.component';
 | 
					import { TagEditDialogComponent } from './tag-edit-dialog/tag-edit-dialog.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-tag-list',
 | 
					  selector: 'app-tag-list',
 | 
				
			||||||
  templateUrl: './tag-list.component.html',
 | 
					  templateUrl: './tag-list.component.html',
 | 
				
			||||||
  styleUrls: ['./tag-list.component.scss']
 | 
					  styleUrls: ['./tag-list.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class TagListComponent extends GenericListComponent<PaperlessTag> {
 | 
					export class TagListComponent extends GenericListComponent<PaperlessTag> {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
  constructor(tagService: TagService, modalService: NgbModal,
 | 
					    tagService: TagService,
 | 
				
			||||||
 | 
					    modalService: NgbModal,
 | 
				
			||||||
    private list: DocumentListViewService,
 | 
					    private list: DocumentListViewService,
 | 
				
			||||||
    toastService: ToastService
 | 
					    toastService: ToastService
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
@ -27,7 +28,8 @@ export class TagListComponent extends GenericListComponent<PaperlessTag> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  filterDocuments(object: PaperlessTag) {
 | 
					  filterDocuments(object: PaperlessTag) {
 | 
				
			||||||
    this.list.quickFilter([{rule_type: FILTER_HAS_TAGS_ALL, value: object.id.toString()}])
 | 
					    this.list.quickFilter([
 | 
				
			||||||
 | 
					      { rule_type: FILTER_HAS_TAGS_ALL, value: object.id.toString() },
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,24 @@
 | 
				
			|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { NotFoundComponent } from './not-found.component';
 | 
					import { NotFoundComponent } from './not-found.component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('NotFoundComponent', () => {
 | 
					describe('NotFoundComponent', () => {
 | 
				
			||||||
  let component: NotFoundComponent;
 | 
					  let component: NotFoundComponent
 | 
				
			||||||
  let fixture: ComponentFixture<NotFoundComponent>;
 | 
					  let fixture: ComponentFixture<NotFoundComponent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      declarations: [ NotFoundComponent ]
 | 
					      declarations: [NotFoundComponent],
 | 
				
			||||||
 | 
					    }).compileComponents()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    fixture = TestBed.createComponent(NotFoundComponent);
 | 
					    fixture = TestBed.createComponent(NotFoundComponent)
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should create', () => {
 | 
					  it('should create', () => {
 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					    expect(component).toBeTruthy()
 | 
				
			||||||
  });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,12 @@
 | 
				
			|||||||
import { Component, OnInit } from '@angular/core';
 | 
					import { Component, OnInit } from '@angular/core'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-not-found',
 | 
					  selector: 'app-not-found',
 | 
				
			||||||
  templateUrl: './not-found.component.html',
 | 
					  templateUrl: './not-found.component.html',
 | 
				
			||||||
  styleUrls: ['./not-found.component.scss']
 | 
					  styleUrls: ['./not-found.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class NotFoundComponent implements OnInit {
 | 
					export class NotFoundComponent implements OnInit {
 | 
				
			||||||
 | 
					  constructor() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() { }
 | 
					  ngOnInit(): void {}
 | 
				
			||||||
 | 
					 | 
				
			||||||
  ngOnInit(): void {
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -27,40 +27,160 @@ export const FILTER_FULLTEXT_QUERY = 20
 | 
				
			|||||||
export const FILTER_FULLTEXT_MORELIKE = 21
 | 
					export const FILTER_FULLTEXT_MORELIKE = 21
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const FILTER_RULE_TYPES: FilterRuleType[] = [
 | 
					export const FILTER_RULE_TYPES: FilterRuleType[] = [
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_TITLE,
 | 
				
			||||||
 | 
					    filtervar: 'title__icontains',
 | 
				
			||||||
 | 
					    datatype: 'string',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					    default: '',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_CONTENT,
 | 
				
			||||||
 | 
					    filtervar: 'content__icontains',
 | 
				
			||||||
 | 
					    datatype: 'string',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					    default: '',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {id: FILTER_TITLE, filtervar: "title__icontains", datatype: "string", multi: false, default: ""},
 | 
					  {
 | 
				
			||||||
  {id: FILTER_CONTENT, filtervar: "content__icontains", datatype: "string", multi: false, default: ""},
 | 
					    id: FILTER_ASN,
 | 
				
			||||||
 | 
					    filtervar: 'archive_serial_number',
 | 
				
			||||||
 | 
					    datatype: 'number',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {id: FILTER_ASN, filtervar: "archive_serial_number", datatype: "number", multi: false},
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_CORRESPONDENT,
 | 
				
			||||||
 | 
					    filtervar: 'correspondent__id',
 | 
				
			||||||
 | 
					    isnull_filtervar: 'correspondent__isnull',
 | 
				
			||||||
 | 
					    datatype: 'correspondent',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_DOCUMENT_TYPE,
 | 
				
			||||||
 | 
					    filtervar: 'document_type__id',
 | 
				
			||||||
 | 
					    isnull_filtervar: 'document_type__isnull',
 | 
				
			||||||
 | 
					    datatype: 'document_type',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {id: FILTER_CORRESPONDENT, filtervar: "correspondent__id", isnull_filtervar: "correspondent__isnull", datatype: "correspondent", multi: false},
 | 
					  {
 | 
				
			||||||
  {id: FILTER_DOCUMENT_TYPE, filtervar: "document_type__id", isnull_filtervar: "document_type__isnull", datatype: "document_type", multi: false},
 | 
					    id: FILTER_IS_IN_INBOX,
 | 
				
			||||||
 | 
					    filtervar: 'is_in_inbox',
 | 
				
			||||||
 | 
					    datatype: 'boolean',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					    default: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_HAS_TAGS_ALL,
 | 
				
			||||||
 | 
					    filtervar: 'tags__id__all',
 | 
				
			||||||
 | 
					    datatype: 'tag',
 | 
				
			||||||
 | 
					    multi: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_HAS_TAGS_ANY,
 | 
				
			||||||
 | 
					    filtervar: 'tags__id__in',
 | 
				
			||||||
 | 
					    datatype: 'tag',
 | 
				
			||||||
 | 
					    multi: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_DOES_NOT_HAVE_TAG,
 | 
				
			||||||
 | 
					    filtervar: 'tags__id__none',
 | 
				
			||||||
 | 
					    datatype: 'tag',
 | 
				
			||||||
 | 
					    multi: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_HAS_ANY_TAG,
 | 
				
			||||||
 | 
					    filtervar: 'is_tagged',
 | 
				
			||||||
 | 
					    datatype: 'boolean',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					    default: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {id: FILTER_IS_IN_INBOX, filtervar: "is_in_inbox", datatype: "boolean", multi: false, default: true},
 | 
					  {
 | 
				
			||||||
  {id: FILTER_HAS_TAGS_ALL, filtervar: "tags__id__all", datatype: "tag", multi: true},
 | 
					    id: FILTER_CREATED_BEFORE,
 | 
				
			||||||
  {id: FILTER_HAS_TAGS_ANY, filtervar: "tags__id__in", datatype: "tag", multi: true},
 | 
					    filtervar: 'created__date__lt',
 | 
				
			||||||
  {id: FILTER_DOES_NOT_HAVE_TAG, filtervar: "tags__id__none", datatype: "tag", multi: true},
 | 
					    datatype: 'date',
 | 
				
			||||||
  {id: FILTER_HAS_ANY_TAG, filtervar: "is_tagged", datatype: "boolean", multi: false, default: true},
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_CREATED_AFTER,
 | 
				
			||||||
 | 
					    filtervar: 'created__date__gt',
 | 
				
			||||||
 | 
					    datatype: 'date',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {id: FILTER_CREATED_BEFORE, filtervar: "created__date__lt", datatype: "date", multi: false},
 | 
					  {
 | 
				
			||||||
  {id: FILTER_CREATED_AFTER, filtervar: "created__date__gt", datatype: "date", multi: false},
 | 
					    id: FILTER_CREATED_YEAR,
 | 
				
			||||||
 | 
					    filtervar: 'created__year',
 | 
				
			||||||
 | 
					    datatype: 'number',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_CREATED_MONTH,
 | 
				
			||||||
 | 
					    filtervar: 'created__month',
 | 
				
			||||||
 | 
					    datatype: 'number',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_CREATED_DAY,
 | 
				
			||||||
 | 
					    filtervar: 'created__day',
 | 
				
			||||||
 | 
					    datatype: 'number',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {id: FILTER_CREATED_YEAR, filtervar: "created__year", datatype: "number", multi: false},
 | 
					  {
 | 
				
			||||||
  {id: FILTER_CREATED_MONTH, filtervar: "created__month", datatype: "number", multi: false},
 | 
					    id: FILTER_ADDED_BEFORE,
 | 
				
			||||||
  {id: FILTER_CREATED_DAY, filtervar: "created__day", datatype: "number", multi: false},
 | 
					    filtervar: 'added__date__lt',
 | 
				
			||||||
 | 
					    datatype: 'date',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_ADDED_AFTER,
 | 
				
			||||||
 | 
					    filtervar: 'added__date__gt',
 | 
				
			||||||
 | 
					    datatype: 'date',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {id: FILTER_ADDED_BEFORE, filtervar: "added__date__lt", datatype: "date", multi: false},
 | 
					  {
 | 
				
			||||||
  {id: FILTER_ADDED_AFTER, filtervar: "added__date__gt", datatype: "date", multi: false},
 | 
					    id: FILTER_MODIFIED_BEFORE,
 | 
				
			||||||
 | 
					    filtervar: 'modified__date__lt',
 | 
				
			||||||
 | 
					    datatype: 'date',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_MODIFIED_AFTER,
 | 
				
			||||||
 | 
					    filtervar: 'modified__date__gt',
 | 
				
			||||||
 | 
					    datatype: 'date',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_ASN_ISNULL,
 | 
				
			||||||
 | 
					    filtervar: 'archive_serial_number__isnull',
 | 
				
			||||||
 | 
					    datatype: 'boolean',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {id: FILTER_MODIFIED_BEFORE, filtervar: "modified__date__lt", datatype: "date", multi: false},
 | 
					  {
 | 
				
			||||||
  {id: FILTER_MODIFIED_AFTER, filtervar: "modified__date__gt", datatype: "date", multi: false},
 | 
					    id: FILTER_TITLE_CONTENT,
 | 
				
			||||||
  {id: FILTER_ASN_ISNULL, filtervar: "archive_serial_number__isnull", datatype: "boolean", multi: false},
 | 
					    filtervar: 'title_content',
 | 
				
			||||||
 | 
					    datatype: 'string',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {id: FILTER_TITLE_CONTENT, filtervar: "title_content", datatype: "string", multi: false},
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_FULLTEXT_QUERY,
 | 
				
			||||||
 | 
					    filtervar: 'query',
 | 
				
			||||||
 | 
					    datatype: 'string',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {id: FILTER_FULLTEXT_QUERY, filtervar: "query", datatype: "string", multi: false},
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_FULLTEXT_MORELIKE,
 | 
				
			||||||
  {id: FILTER_FULLTEXT_MORELIKE, filtervar: "more_like_id", datatype: "number", multi: false},
 | 
					    filtervar: 'more_like_id',
 | 
				
			||||||
 | 
					    datatype: 'number',
 | 
				
			||||||
 | 
					    multi: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface FilterRuleType {
 | 
					export interface FilterRuleType {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,13 @@
 | 
				
			|||||||
import { FILTER_FULLTEXT_MORELIKE, FILTER_FULLTEXT_QUERY } from "./filter-rule-type"
 | 
					import {
 | 
				
			||||||
 | 
					  FILTER_FULLTEXT_MORELIKE,
 | 
				
			||||||
 | 
					  FILTER_FULLTEXT_QUERY,
 | 
				
			||||||
 | 
					} from './filter-rule-type'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function cloneFilterRules(filterRules: FilterRule[]): FilterRule[] {
 | 
					export function cloneFilterRules(filterRules: FilterRule[]): FilterRule[] {
 | 
				
			||||||
  if (filterRules) {
 | 
					  if (filterRules) {
 | 
				
			||||||
    let newRules: FilterRule[] = []
 | 
					    let newRules: FilterRule[] = []
 | 
				
			||||||
    for (let rule of filterRules) {
 | 
					    for (let rule of filterRules) {
 | 
				
			||||||
      newRules.push({rule_type: rule.rule_type, value: rule.value})
 | 
					      newRules.push({ rule_type: rule.rule_type, value: rule.value })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return newRules
 | 
					    return newRules
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
@ -13,7 +16,13 @@ export function cloneFilterRules(filterRules: FilterRule[]): FilterRule[] {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function isFullTextFilterRule(filterRules: FilterRule[]): boolean {
 | 
					export function isFullTextFilterRule(filterRules: FilterRule[]): boolean {
 | 
				
			||||||
  return filterRules.find(r => r.rule_type == FILTER_FULLTEXT_QUERY || r.rule_type == FILTER_FULLTEXT_MORELIKE) != null
 | 
					  return (
 | 
				
			||||||
 | 
					    filterRules.find(
 | 
				
			||||||
 | 
					      (r) =>
 | 
				
			||||||
 | 
					        r.rule_type == FILTER_FULLTEXT_QUERY ||
 | 
				
			||||||
 | 
					        r.rule_type == FILTER_FULLTEXT_MORELIKE
 | 
				
			||||||
 | 
					    ) != null
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface FilterRule {
 | 
					export interface FilterRule {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
import { ObjectWithId } from './object-with-id';
 | 
					import { ObjectWithId } from './object-with-id'
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const MATCH_ANY = 1
 | 
					export const MATCH_ANY = 1
 | 
				
			||||||
export const MATCH_ALL = 2
 | 
					export const MATCH_ALL = 2
 | 
				
			||||||
@ -9,16 +8,39 @@ export const MATCH_FUZZY = 5
 | 
				
			|||||||
export const MATCH_AUTO = 6
 | 
					export const MATCH_AUTO = 6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const MATCHING_ALGORITHMS = [
 | 
					export const MATCHING_ALGORITHMS = [
 | 
				
			||||||
    {id: MATCH_ANY, shortName: $localize`Any word`, name: $localize`Any: Document contains any of these words (space separated)`},
 | 
					  {
 | 
				
			||||||
    {id: MATCH_ALL, shortName: $localize`All words`, name: $localize`All: Document contains all of these words (space separated)`},
 | 
					    id: MATCH_ANY,
 | 
				
			||||||
    {id: MATCH_LITERAL, shortName: $localize`Exact match`, name: $localize`Exact: Document contains this string`},
 | 
					    shortName: $localize`Any word`,
 | 
				
			||||||
    {id: MATCH_REGEX, shortName: $localize`Regular expression`, name: $localize`Regular expression: Document matches this regular expression`},
 | 
					    name: $localize`Any: Document contains any of these words (space separated)`,
 | 
				
			||||||
    {id: MATCH_FUZZY, shortName: $localize`Fuzzy word`, name: $localize`Fuzzy: Document contains a word similar to this word`},
 | 
					  },
 | 
				
			||||||
    {id: MATCH_AUTO, shortName: $localize`Automatic`, name: $localize`Auto: Learn matching automatically`},
 | 
					  {
 | 
				
			||||||
 | 
					    id: MATCH_ALL,
 | 
				
			||||||
 | 
					    shortName: $localize`All words`,
 | 
				
			||||||
 | 
					    name: $localize`All: Document contains all of these words (space separated)`,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: MATCH_LITERAL,
 | 
				
			||||||
 | 
					    shortName: $localize`Exact match`,
 | 
				
			||||||
 | 
					    name: $localize`Exact: Document contains this string`,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: MATCH_REGEX,
 | 
				
			||||||
 | 
					    shortName: $localize`Regular expression`,
 | 
				
			||||||
 | 
					    name: $localize`Regular expression: Document matches this regular expression`,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: MATCH_FUZZY,
 | 
				
			||||||
 | 
					    shortName: $localize`Fuzzy word`,
 | 
				
			||||||
 | 
					    name: $localize`Fuzzy: Document contains a word similar to this word`,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: MATCH_AUTO,
 | 
				
			||||||
 | 
					    shortName: $localize`Automatic`,
 | 
				
			||||||
 | 
					    name: $localize`Auto: Learn matching automatically`,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface MatchingModel extends ObjectWithId {
 | 
					export interface MatchingModel extends ObjectWithId {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  name?: string
 | 
					  name?: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  slug?: string
 | 
					  slug?: string
 | 
				
			||||||
@ -30,5 +52,4 @@ export interface MatchingModel extends ObjectWithId {
 | 
				
			|||||||
  is_insensitive?: boolean
 | 
					  is_insensitive?: boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  document_count?: number
 | 
					  document_count?: number
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user