Compare commits

..

82 Commits

Author SHA1 Message Date
shamoon
3fa4fad811
Merge migrations again 2025-06-28 05:53:41 -07:00
shamoon
8b0e97d3ec
Fix merge conflict 2025-06-28 05:53:41 -07:00
shamoon
2ba5c0c391
Merge migrations 2025-06-28 05:53:40 -07:00
shamoon
1367453d2c
Add fallback parsing for invalid ai responses 2025-06-28 05:53:40 -07:00
shamoon
f92982217f
Truncate similar docs content 2025-06-28 05:53:40 -07:00
shamoon
701b254b86
Fix paperless_ai logging 2025-06-28 05:53:40 -07:00
shamoon
a092dd3ab0
token limiting 2025-06-28 05:53:40 -07:00
shamoon
9fdfdf9b48
Update AI docs 2025-06-28 05:53:40 -07:00
shamoon
81bacec484
Cover app config changes 2025-06-28 05:53:40 -07:00
shamoon
5b13e3594e
Mock auto-trigger llm index 2025-06-28 05:53:24 -07:00
shamoon
3f051fb27b
Fix / cleanup ai indexing test 2025-06-28 05:53:07 -07:00
shamoon
d1294c4183
Doh, add tests in new module 2025-06-28 05:53:07 -07:00
shamoon
ca87f43262
Coverage for llmindex tasks 2025-06-28 05:53:06 -07:00
shamoon
c20273f46f
Cover llmindex in system status 2025-06-28 05:53:06 -07:00
shamoon
48d0315cc4
Add llmindex to systemstatus 2025-06-28 05:53:06 -07:00
shamoon
553bfeb9fc
Auto-trigger llmindex rebuild when enabled 2025-06-28 05:53:06 -07:00
shamoon
1b9d775508
Use PaperlessTask for llmindex 2025-06-28 05:51:43 -07:00
shamoon
c75ec8dfc3
Create llmindex if doesnt exist on update run 2025-06-28 05:51:43 -07:00
shamoon
f8890bd14a
Move ai to its own module 2025-06-28 05:51:42 -07:00
shamoon
8fc77d92a9
Better respect perms for ai suggestions 2025-06-28 05:51:42 -07:00
shamoon
92837f86c0
Refactor load_or_build_index 2025-06-28 05:51:42 -07:00
shamoon
4b43e39fbb
Update chat view decorators 2025-06-28 05:51:42 -07:00
shamoon
51a89b0cde
Cover matching 2025-06-28 05:51:42 -07:00
shamoon
6536a9c874
Cover partial indexing 2025-06-28 05:51:42 -07:00
shamoon
1e04ce1e57
Refactor and consolidate rag / embedding and tests 2025-06-28 05:51:42 -07:00
shamoon
199f328999
indexing cleanup and tests 2025-06-28 05:51:41 -07:00
shamoon
495a6fe2fe
Use partial reindex for bulk updates 2025-06-28 05:51:41 -07:00
shamoon
03f183712b
Unify prompts, cover 2025-06-28 05:51:41 -07:00
shamoon
f8c6989eaf
Incremental llm index update, add scheduled llm index task 2025-06-28 05:51:41 -07:00
shamoon
5c0903b6da
Some cleanup, typing 2025-06-28 05:51:41 -07:00
shamoon
d49982a5ba
Handle doc updates, refactor 2025-06-28 05:51:41 -07:00
shamoon
db0dc337bd
Chat coverage 2025-06-28 05:51:41 -07:00
shamoon
fd1554fb96
Tests for rest of RAG 2025-06-28 05:51:41 -07:00
shamoon
404dbae431
Chat component and service coverage 2025-06-28 05:51:40 -07:00
shamoon
0f1aee3a3c
Real doc ID updating 2025-06-28 05:51:40 -07:00
shamoon
3f8dbc630a
Sweet chat animation, cursor 2025-06-28 05:51:40 -07:00
shamoon
5180651400
Only show chat if enabled 2025-06-28 05:51:40 -07:00
shamoon
f0ac80a08a
Fix partial length in chat 2025-06-28 05:51:40 -07:00
shamoon
d439b58aaf
Fix gzip breaks streaming and flush stream 2025-06-28 05:51:40 -07:00
shamoon
37745e846d
Fix openai api key, config settings saving 2025-06-28 05:51:40 -07:00
shamoon
9c00b48dc7
Try rewriting with httpclient 2025-06-28 05:51:40 -07:00
shamoon
bdaae882a6
Extremely basic chat component 2025-06-28 05:51:39 -07:00
shamoon
37e1290e00
Just use the built-in ollama LLM class of course 2025-06-28 05:51:39 -07:00
shamoon
183d369350
Fix naming 2025-06-28 05:51:39 -07:00
shamoon
d431f1af15
Trim nodes 2025-06-28 05:51:39 -07:00
shamoon
b4ea2b7521
Backend streaming chat 2025-06-28 05:51:39 -07:00
shamoon
46df529c3a
Fixup some tests 2025-06-28 05:51:39 -07:00
shamoon
3ed877b301
Just some docs
[ci skip]
2025-06-28 05:51:39 -07:00
shamoon
1e79795fbf
Unify, respect perms
[ci skip]
2025-06-28 05:51:38 -07:00
shamoon
cee5a3b62d
Individual doc chat
[ci skip]
2025-06-28 05:51:38 -07:00
shamoon
0807e32278
Super basic doc chat
[ci skip]
2025-06-28 05:51:38 -07:00
shamoon
6bdf396083
Better encapsulate backends, use llama_index OpenAI 2025-06-28 05:51:38 -07:00
shamoon
5c88a7207d
Add backend settings to frontend config
[ci skip]
2025-06-28 05:51:38 -07:00
shamoon
3e8a9958a5
Tweak ollama timeout, prompt
[ci skip]
2025-06-28 05:51:38 -07:00
shamoon
b1b2d03644
Fix ollama, fix RAG
[ci skip]
2025-06-28 05:51:38 -07:00
shamoon
2c4b8c9afe
RAG into suggestions 2025-06-28 05:51:38 -07:00
shamoon
0a19a5500c
llamaindex vector index, llmindex mangement command 2025-06-28 05:51:37 -07:00
shamoon
edeb9a7534
Docs 2025-06-28 05:51:37 -07:00
shamoon
06b0817cc2
Use password and select config fields 2025-06-28 05:51:37 -07:00
shamoon
3051ea5fbb
Use a frontend config 2025-06-28 05:51:37 -07:00
shamoon
c3175c2cd6
Pass AI enabled to frontend 2025-06-28 05:51:37 -07:00
shamoon
77796ac3f4
Basic handling of non-AI response 2025-06-28 05:51:37 -07:00
shamoon
45451ac110
Cleaner auto-remove 2025-06-28 05:51:37 -07:00
shamoon
2e34223ead
Automatically remove suggestions after add 2025-06-28 05:51:36 -07:00
shamoon
c416bca3df
Test views, caching 2025-06-28 05:51:36 -07:00
shamoon
b8dc6665dc
Invalidate llm suggestion cache on doc save 2025-06-28 05:51:36 -07:00
shamoon
2fe901cd8d
Fix 2025-06-28 05:51:36 -07:00
shamoon
199834ee8f
Backend tests 2025-06-28 05:51:36 -07:00
shamoon
5849c6fff6
Correct object retrieval 2025-06-28 05:51:36 -07:00
shamoon
de6e43738c
Refactor 2025-06-28 05:51:36 -07:00
shamoon
4e23a072d4
Move module 2025-06-28 05:51:35 -07:00
shamoon
b600e27f90
Hook up the add buttons 2025-06-28 05:51:35 -07:00
shamoon
2c83e0d07f
Refine the suggestions dropdown ui a bit 2025-06-28 05:51:35 -07:00
shamoon
be327c1b72
Suggestions dropdown 2025-06-28 05:51:35 -07:00
shamoon
4013f15e51
Messing with a suggest button 2025-06-28 05:51:35 -07:00
shamoon
a53014ab89
Rename config 2025-06-28 05:51:35 -07:00
shamoon
214420ba1a
Title suggestion ui 2025-06-28 05:51:35 -07:00
shamoon
453dc5062b
Just start the frontend
[ci skip]
2025-06-28 05:51:35 -07:00
shamoon
1b6a4a3be4
wow llama3 is bad 2025-06-28 05:51:34 -07:00
shamoon
3d33149f03
Changeup logging 2025-06-28 05:51:34 -07:00
shamoon
455ff068cb
Some logging, error handling 2025-06-28 05:51:34 -07:00
shamoon
0b06ded65a
Basic start 2025-06-28 05:51:30 -07:00
171 changed files with 3560 additions and 4342 deletions

View File

@ -1,55 +0,0 @@
title: "[Support] "
body:
- type: textarea
id: description
attributes:
label: What's your question or issue?
description: Provide a clear and concise description of what you're trying to do, and what's going wrong.
placeholder: |
I'm trying to...
[Include screenshots if helpful]
validations:
required: true
- type: textarea
id: steps
attributes:
label: What have you tried?
description: Describe any steps you've already taken to troubleshoot or solve the issue.
placeholder: |
- I checked the logs and saw...
- I followed the install guide and tried...
- type: input
id: version
attributes:
label: Paperless-ngx version
placeholder: e.g. 1.14.0
validations:
required: true
- type: input
id: host-os
attributes:
label: Host OS
description: Include architecture if relevant.
placeholder: e.g. Ubuntu 22.04 / Raspberry Pi arm64
- type: dropdown
id: install-method
attributes:
label: Installation method
options:
- Docker - official image
- Docker - linuxserver.io image
- Bare metal
- Other (please describe above)
- type: textarea
id: system-status
attributes:
label: System status
description: If available, copy & paste the system status output from Settings > System Status > Copy
render: json
- type: textarea
id: logs
attributes:
label: Relevant logs or output
description: If you have logs, errors that might help, paste it here.
render: bash

View File

@ -61,7 +61,7 @@ jobs:
cd src-ui
pnpm run ng extract-i18n
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v6
uses: stefanzweifel/git-auto-commit-action@v5
with:
file_pattern: 'src-ui/messages.xlf src/locale/en_US/LC_MESSAGES/django.po'
commit_message: "Auto translate strings"

View File

@ -457,22 +457,6 @@ of the index and usually makes queries faster and also ensures that the
autocompletion works properly. This command is regularly invoked by the
task scheduler.
### Clearing the database read cache
If the database read cache is enabled, **you must run this command** after making any changes to the database outside the application context.
This includes operations such as restoring a database backup or executing SQL statements like UPDATE, INSERT, DELETE, ALTER, CREATE, or DROP.
Failing to invalidate the cache after such modifications can lead to stale data being served from the cache, and **may cause data corruption** or inconsistent behavior in the application.
Use the following management command to clear the cache:
```
invalidate_cachalot
```
!!! info
The database read cache is based on Django-Cachalot. You can refer to their [documentation](https://django-cachalot.readthedocs.io/en/latest/quickstart.html#manage-py-command).
### Managing filenames {#renamer}
If you use paperless' feature to

View File

@ -6007,6 +6007,7 @@ primarily.
a very good job at ocr'ing a document with the default
language. Certain language specifics such as umlauts may not get
picked up properly.
- `PAPERLESS_DEBUG` defaults to `false`.
- The presence of `PAPERLESS_DBHOST` now determines whether to use
PostgreSQL or SQLite.
- `PAPERLESS_OCR_THREADS` is gone and replaced with

View File

@ -159,41 +159,6 @@ Available options are `postgresql` and `mariadb`.
Defaults to unset, which uses Djangos built-in defaults.
#### [`PAPERLESS_DB_READ_CACHE_ENABLED=<bool>`](#PAPERLESS_DB_READ_CACHE_ENABLED) {#PAPERLESS_DB_READ_CACHE_ENABLED}
: Caches the database read query results into Redis. This can significantly improve application response times by caching database queries, at the cost of slightly increased memory usage.
Defaults to `false`.
!!! danger
**Do not modify the database outside the application while it is running.**
This includes actions such as restoring a backup, upgrading the database, or performing manual inserts. All external modifications must be done **only when the application is stopped**.
After making any such changes, you **must invalidate the DB read cache** using the `invalidate_cachalot` management command.
#### [`PAPERLESS_READ_CACHE_TTL=<int>`](#PAPERLESS_READ_CACHE_TTL) {#PAPERLESS_READ_CACHE_TTL}
: Specifies how long (in seconds) read data should be cached.
Allowed values are between `1` (one second) and `31536000` (one year). Defaults to `3600` (one hour).
!!! warning
A high TTL increases memory usage over time. Memory may be used until end of TTL, even if the cache is invalidated with the `invalidate_cachalot` command.
In case of an out-of-memory (OOM) situation, Redis may stop accepting new data — including cache entries, scheduled tasks, and documents to consume.
If your system has limited RAM, consider configuring a dedicated Redis instance for the read cache, with a memory limit and the eviction policy set to `allkeys-lru`.
For more details, refer to the [Redis eviction policy documentation](https://redis.io/docs/latest/develop/reference/eviction/), and see the `PAPERLESS_READ_CACHE_REDIS_URL` setting to specify a separate Redis broker.
#### [`PAPERLESS_READ_CACHE_REDIS_URL=<url>`](#PAPERLESS_READ_CACHE_REDIS_URL) {#PAPERLESS_READ_CACHE_REDIS_URL}
: Defines the Redis instance used for the read cache.
Defaults to `None`.
!!! Note
If this value is not set, the same Redis instance used for scheduled tasks will be used for caching as well.
## Optional Services
### Tika {#tika}
@ -1003,22 +968,6 @@ still perform some basic text pre-processing before matching.
Defaults to 1.
#### [`PAPERLESS_DATE_PARSER_LANGUAGES=<lang>`](#PAPERLESS_DATE_PARSER_LANGUAGES) {#PAPERLESS_DATE_PARSER_LANGUAGES}
Specifies which language Paperless should use when parsing dates from documents.
This should be a language code supported by the dateparser library,
for example: "en", or a combination such as "en+de".
Locales are also supported (e.g., "en-AU").
Multiple languages can be combined using "+", for example: "en+de" or "en-AU+de".
For valid values, refer to the list of supported languages and locales in the [dateparser documentation](https://dateparser.readthedocs.io/en/latest/supported_locales.html).
Set this to match the languages in which most of your documents are written.
If not set, Paperless will attempt to infer the language(s) from the OCR configuration (`PAPERLESS_OCR_LANGUAGE`).
!!! note
This format differs from the `PAPERLESS_OCR_LANGUAGE` setting, which uses ISO 639-2 codes (3 letters, e.g., "eng+deu" for Tesseract OCR).
#### [`PAPERLESS_EMAIL_TASK_CRON=<cron expression>`](#PAPERLESS_EMAIL_TASK_CRON) {#PAPERLESS_EMAIL_TASK_CRON}
: Configures the scheduled email fetching frequency. The value

View File

@ -1,6 +1,10 @@
# Have a look at the docs for documentation.
# https://docs.paperless-ngx.com/configuration/
# Debug. Only enable this for development.
#PAPERLESS_DEBUG=false
# Required services
#PAPERLESS_REDIS=redis://localhost:6379

View File

@ -26,7 +26,6 @@ dependencies = [
"django~=5.1.7",
"django-allauth[socialaccount,mfa]~=65.4.0",
"django-auditlog~=3.1.2",
"django-cachalot~=2.8.0",
"django-celery-results~=2.6.0",
"django-compression-middleware~=0.5.0",
"django-cors-headers~=4.7.0",
@ -231,6 +230,9 @@ lint.per-file-ignores."src/documents/parsers.py" = [
lint.per-file-ignores."src/documents/signals/handlers.py" = [
"PTH",
] # TODO Enable & remove
lint.per-file-ignores."src/paperless/settings.py" = [
"PTH",
] # TODO Enable & remove
lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [
"RUF001",
]

View File

@ -60,12 +60,10 @@
"path": "./extra-webpack.config.ts"
},
"outputPath": "dist/paperless-ui",
"main": "src/main.ts",
"outputHashing": "none",
"index": "src/index.html",
"polyfills": [
"src/polyfills.ts"
],
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"localize": true,
"assets": [
@ -88,15 +86,12 @@
"file-saver",
"utif"
],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true,
"stylePreprocessorOptions": {
"includePaths": [
"."
]
}
"namedChunks": true
},
"configurations": {
"production": {
@ -112,6 +107,8 @@
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
@ -191,30 +188,6 @@
},
"@angular-eslint/schematics:library": {
"setParserOptionsProject": true
},
"@schematics/angular:component": {
"type": "component"
},
"@schematics/angular:directive": {
"type": "directive"
},
"@schematics/angular:service": {
"type": "service"
},
"@schematics/angular:guard": {
"typeSeparator": "."
},
"@schematics/angular:interceptor": {
"typeSeparator": "."
},
"@schematics/angular:module": {
"typeSeparator": "."
},
"@schematics/angular:pipe": {
"typeSeparator": "."
},
"@schematics/angular:resolver": {
"typeSeparator": "."
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -11,28 +11,28 @@
},
"private": true,
"dependencies": {
"@angular/cdk": "^20.0.4",
"@angular/common": "~20.0.6",
"@angular/compiler": "~20.0.6",
"@angular/core": "~20.0.6",
"@angular/forms": "~20.0.6",
"@angular/localize": "~20.0.6",
"@angular/platform-browser": "~20.0.6",
"@angular/platform-browser-dynamic": "~20.0.6",
"@angular/router": "~20.0.6",
"@ng-bootstrap/ng-bootstrap": "^19.0.1",
"@ng-select/ng-select": "^15.1.3",
"@angular/cdk": "^19.2.14",
"@angular/common": "~19.2.14",
"@angular/compiler": "~19.2.14",
"@angular/core": "~19.2.14",
"@angular/forms": "~19.2.14",
"@angular/localize": "~19.2.14",
"@angular/platform-browser": "~19.2.14",
"@angular/platform-browser-dynamic": "~19.2.14",
"@angular/router": "~19.2.14",
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
"@ng-select/ng-select": "^14.9.0",
"@ngneat/dirty-check-forms": "^3.0.3",
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.7",
"bootstrap": "^5.3.6",
"file-saver": "^2.0.5",
"mime-names": "^1.0.0",
"ng2-pdf-viewer": "^10.4.0",
"ngx-bootstrap-icons": "^1.9.3",
"ngx-color": "^10.0.0",
"ngx-cookie-service": "^20.0.1",
"ngx-device-detector": "^10.0.2",
"ngx-ui-tour-ng-bootstrap": "^17.0.0",
"ngx-cookie-service": "^19.1.2",
"ngx-device-detector": "^9.0.0",
"ngx-ui-tour-ng-bootstrap": "^16.0.0",
"rxjs": "^7.8.2",
"tslib": "^2.8.1",
"utif": "^3.1.0",
@ -40,26 +40,26 @@
"zone.js": "^0.15.1"
},
"devDependencies": {
"@angular-builders/custom-webpack": "^20.0.0",
"@angular-builders/jest": "^20.0.0",
"@angular-devkit/core": "^20.0.4",
"@angular-devkit/schematics": "^20.0.4",
"@angular-eslint/builder": "20.1.1",
"@angular-eslint/eslint-plugin": "20.1.1",
"@angular-eslint/eslint-plugin-template": "20.1.1",
"@angular-eslint/schematics": "20.1.1",
"@angular-eslint/template-parser": "20.1.1",
"@angular/build": "^20.0.4",
"@angular/cli": "~20.0.4",
"@angular/compiler-cli": "~20.0.6",
"@angular-builders/custom-webpack": "^19.0.1",
"@angular-builders/jest": "^19.0.1",
"@angular-devkit/build-angular": "^19.2.14",
"@angular-devkit/core": "^19.2.14",
"@angular-devkit/schematics": "^19.2.14",
"@angular-eslint/builder": "19.7.0",
"@angular-eslint/eslint-plugin": "19.7.0",
"@angular-eslint/eslint-plugin-template": "19.7.0",
"@angular-eslint/schematics": "19.7.0",
"@angular-eslint/template-parser": "19.7.0",
"@angular/cli": "~19.2.14",
"@angular/compiler-cli": "~19.2.14",
"@codecov/webpack-plugin": "^1.9.1",
"@playwright/test": "^1.53.2",
"@playwright/test": "^1.51.1",
"@types/jest": "^29.5.14",
"@types/node": "^24.0.10",
"@typescript-eslint/eslint-plugin": "^8.35.1",
"@typescript-eslint/parser": "^8.35.1",
"@typescript-eslint/utils": "^8.35.1",
"eslint": "^9.30.1",
"@types/node": "^22.15.29",
"@typescript-eslint/eslint-plugin": "^8.33.0",
"@typescript-eslint/parser": "^8.33.0",
"@typescript-eslint/utils": "^8.33.0",
"eslint": "^9.28.0",
"jest": "29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-junit": "^16.0.0",
@ -67,8 +67,7 @@
"jest-websocket-mock": "^2.5.0",
"prettier-plugin-organize-imports": "^4.1.0",
"ts-node": "~10.9.1",
"typescript": "^5.8.3",
"webpack": "^5.99.9"
"typescript": "^5.5.4"
},
"pnpm": {
"onlyBuiltDependencies": [
@ -78,5 +77,6 @@
"lmdb",
"msgpackr-extract"
]
}
},
"typings": "./src/typings.d.ts"
}

4078
src-ui/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
import { Component, inject, OnDestroy, OnInit, Renderer2 } from '@angular/core'
import { Component, OnDestroy, OnInit, Renderer2 } from '@angular/core'
import { Router, RouterOutlet } from '@angular/router'
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
import { first, Subscription } from 'rxjs'
@ -29,22 +29,22 @@ import { WebsocketStatusService } from './services/websocket-status.service'
],
})
export class AppComponent implements OnInit, OnDestroy {
private settings = inject(SettingsService)
private websocketStatusService = inject(WebsocketStatusService)
private toastService = inject(ToastService)
private router = inject(Router)
private tasksService = inject(TasksService)
tourService = inject(TourService)
private renderer = inject(Renderer2)
private permissionsService = inject(PermissionsService)
private hotKeyService = inject(HotKeyService)
private componentRouterService = inject(ComponentRouterService)
newDocumentSubscription: Subscription
successSubscription: Subscription
failedSubscription: Subscription
constructor() {
constructor(
private settings: SettingsService,
private websocketStatusService: WebsocketStatusService,
private toastService: ToastService,
private router: Router,
private tasksService: TasksService,
public tourService: TourService,
private renderer: Renderer2,
private permissionsService: PermissionsService,
private hotKeyService: HotKeyService,
private componentRouterService: ComponentRouterService
) {
let anyWindow = window as any
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.mjs'
this.settings.updateAppearanceSettings()

View File

@ -1,5 +1,5 @@
import { AsyncPipe } from '@angular/common'
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import { Component, OnDestroy, OnInit } from '@angular/core'
import {
AbstractControl,
FormControl,
@ -59,10 +59,6 @@ export class ConfigComponent
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy, DirtyComponent
{
private configService = inject(ConfigService)
private toastService = inject(ToastService)
private settingsService = inject(SettingsService)
public readonly ConfigOptionType = ConfigOptionType
// generated dynamically
@ -83,7 +79,11 @@ export class ConfigComponent
storeSub: Subscription
isDirty$: Observable<boolean>
constructor() {
constructor(
private configService: ConfigService,
private toastService: ToastService,
private settingsService: SettingsService
) {
super()
this.configForm.addControl('id', new FormControl())
PaperlessConfigOptions.forEach((option) => {

View File

@ -5,7 +5,6 @@ import {
OnDestroy,
OnInit,
ViewChild,
inject,
} from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'
@ -29,8 +28,12 @@ export class LogsComponent
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy
{
private logService = inject(LogService)
private changedetectorRef = inject(ChangeDetectorRef)
constructor(
private logService: LogService,
private changedetectorRef: ChangeDetectorRef
) {
super()
}
public logs: string[] = []

View File

@ -2,10 +2,10 @@ import { AsyncPipe, ViewportScroller } from '@angular/common'
import {
AfterViewInit,
Component,
Inject,
LOCALE_ID,
OnDestroy,
OnInit,
inject,
} from '@angular/core'
import {
FormControl,
@ -104,20 +104,6 @@ export class SettingsComponent
extends ComponentWithPermissions
implements OnInit, AfterViewInit, OnDestroy, DirtyComponent
{
private documentListViewService = inject(DocumentListViewService)
private toastService = inject(ToastService)
private settings = inject(SettingsService)
currentLocale = inject(LOCALE_ID)
private viewportScroller = inject(ViewportScroller)
private activatedRoute = inject(ActivatedRoute)
readonly tourService = inject(TourService)
private usersService = inject(UserService)
private groupsService = inject(GroupService)
private router = inject(Router)
permissionsService = inject(PermissionsService)
private modalService = inject(NgbModal)
private systemStatusService = inject(SystemStatusService)
activeNavID: number
settingsForm = new FormGroup({
@ -193,7 +179,21 @@ export class SettingsComponent
)
}
constructor() {
constructor(
private documentListViewService: DocumentListViewService,
private toastService: ToastService,
private settings: SettingsService,
@Inject(LOCALE_ID) public currentLocale: string,
private viewportScroller: ViewportScroller,
private activatedRoute: ActivatedRoute,
public readonly tourService: TourService,
private usersService: UserService,
private groupsService: GroupService,
private router: Router,
public permissionsService: PermissionsService,
private modalService: NgbModal,
private systemStatusService: SystemStatusService
) {
super()
this.settings.settingsSaved.subscribe(() => {
if (!this.savePending) this.initialize()

View File

@ -1,5 +1,5 @@
import { NgTemplateOutlet, SlicePipe } from '@angular/common'
import { Component, inject, OnDestroy, OnInit } from '@angular/core'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Router } from '@angular/router'
import {
@ -69,10 +69,6 @@ export class TasksComponent
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy
{
tasksService = inject(TasksService)
private modalService = inject(NgbModal)
private readonly router = inject(Router)
public activeTab: TaskTab
public selectedTasks: Set<number> = new Set()
public togggleAll: boolean = false
@ -109,6 +105,14 @@ export class TasksComponent
: $localize`Dismiss all`
}
constructor(
public tasksService: TasksService,
private modalService: NgbModal,
private readonly router: Router
) {
super()
}
ngOnInit() {
this.tasksService.reload()
timer(5000, 5000)

View File

@ -1,4 +1,4 @@
import { Component, OnDestroy, inject } from '@angular/core'
import { Component, OnDestroy } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Router } from '@angular/router'
import {
@ -36,19 +36,19 @@ export class TrashComponent
extends LoadingComponentWithPermissions
implements OnDestroy
{
private trashService = inject(TrashService)
private toastService = inject(ToastService)
private modalService = inject(NgbModal)
private settingsService = inject(SettingsService)
private router = inject(Router)
public documentsInTrash: Document[] = []
public selectedDocuments: Set<number> = new Set()
public allToggled: boolean = false
public page: number = 1
public totalDocuments: number
constructor() {
constructor(
private trashService: TrashService,
private toastService: ToastService,
private modalService: NgbModal,
private settingsService: SettingsService,
private router: Router
) {
super()
this.reload()
}

View File

@ -1,4 +1,4 @@
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { Subject, first, takeUntil } from 'rxjs'
@ -31,18 +31,22 @@ export class UsersAndGroupsComponent
extends ComponentWithPermissions
implements OnInit, OnDestroy
{
private usersService = inject(UserService)
private groupsService = inject(GroupService)
private toastService = inject(ToastService)
private modalService = inject(NgbModal)
permissionsService = inject(PermissionsService)
private settings = inject(SettingsService)
users: User[]
groups: Group[]
unsubscribeNotifier: Subject<any> = new Subject()
constructor(
private usersService: UserService,
private groupsService: GroupService,
private toastService: ToastService,
private modalService: NgbModal,
public permissionsService: PermissionsService,
private settings: SettingsService
) {
super()
}
ngOnInit(): void {
this.usersService
.listAll(null, null, { full_perms: true })

View File

@ -6,7 +6,7 @@ import {
moveItemInArray,
} from '@angular/cdk/drag-drop'
import { NgClass } from '@angular/common'
import { Component, HostListener, inject, OnInit } from '@angular/core'
import { Component, HostListener, OnInit } from '@angular/core'
import { ActivatedRoute, Router, RouterModule } from '@angular/router'
import {
NgbCollapseModule,
@ -76,27 +76,26 @@ export class AppFrameComponent
extends ComponentWithPermissions
implements OnInit, ComponentCanDeactivate
{
router = inject(Router)
private activatedRoute = inject(ActivatedRoute)
private openDocumentsService = inject(OpenDocumentsService)
savedViewService = inject(SavedViewService)
private remoteVersionService = inject(RemoteVersionService)
settingsService = inject(SettingsService)
tasksService = inject(TasksService)
private readonly toastService = inject(ToastService)
private modalService = inject(NgbModal)
permissionsService = inject(PermissionsService)
private djangoMessagesService = inject(DjangoMessagesService)
appRemoteVersion: AppRemoteVersion
isMenuCollapsed: boolean = true
slimSidebarAnimating: boolean = false
constructor() {
constructor(
public router: Router,
private activatedRoute: ActivatedRoute,
private openDocumentsService: OpenDocumentsService,
public savedViewService: SavedViewService,
private remoteVersionService: RemoteVersionService,
public settingsService: SettingsService,
public tasksService: TasksService,
private readonly toastService: ToastService,
private modalService: NgbModal,
public permissionsService: PermissionsService,
private djangoMessagesService: DjangoMessagesService
) {
super()
const permissionsService = this.permissionsService
if (
permissionsService.currentUserCan(

View File

@ -6,7 +6,6 @@ import {
QueryList,
ViewChild,
ViewChildren,
inject,
} from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Router } from '@angular/router'
@ -70,17 +69,6 @@ import { WorkflowEditDialogComponent } from '../../common/edit-dialog/workflow-e
],
})
export class GlobalSearchComponent implements OnInit {
searchService = inject(SearchService)
private router = inject(Router)
private modalService = inject(NgbModal)
private documentService = inject(DocumentService)
private documentListViewService = inject(DocumentListViewService)
private permissionsService = inject(PermissionsService)
private toastService = inject(ToastService)
private hotkeyService = inject(HotKeyService)
private settingsService = inject(SettingsService)
private locationStrategy = inject(LocationStrategy)
public DataType = DataType
public query: string
public queryDebounce: Subject<string>
@ -102,7 +90,18 @@ export class GlobalSearchComponent implements OnInit {
)
}
constructor() {
constructor(
public searchService: SearchService,
private router: Router,
private modalService: NgbModal,
private documentService: DocumentService,
private documentListViewService: DocumentListViewService,
private permissionsService: PermissionsService,
private toastService: ToastService,
private hotkeyService: HotKeyService,
private settingsService: SettingsService,
private locationStrategy: LocationStrategy
) {
this.queryDebounce = new Subject<string>()
this.queryDebounce

View File

@ -1,4 +1,4 @@
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import { Component, OnDestroy, OnInit } from '@angular/core'
import {
NgbDropdownModule,
NgbProgressbarModule,
@ -20,7 +20,7 @@ import { ToastComponent } from '../../common/toast/toast.component'
],
})
export class ToastsDropdownComponent implements OnInit, OnDestroy {
toastService = inject(ToastService)
constructor(public toastService: ToastService) {}
private subscription: Subscription

View File

@ -1,4 +1,4 @@
import { Component, ElementRef, inject, OnInit, ViewChild } from '@angular/core'
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NavigationEnd, Router } from '@angular/router'
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
@ -23,9 +23,6 @@ export class ChatComponent implements OnInit {
public input: string = ''
public documentId!: number
private chatService: ChatService = inject(ChatService)
private router: Router = inject(Router)
@ViewChild('scrollAnchor') scrollAnchor!: ElementRef<HTMLDivElement>
@ViewChild('chatInput') chatInput!: ElementRef<HTMLInputElement>
@ -38,6 +35,11 @@ export class ChatComponent implements OnInit {
: $localize`Ask a question about a document...`
}
constructor(
private chatService: ChatService,
private router: Router
) {}
ngOnInit(): void {
this.updateDocumentId(this.router.url)
this.router.events
@ -46,6 +48,8 @@ export class ChatComponent implements OnInit {
map((event) => (event as NavigationEnd).url)
)
.subscribe((url) => {
console.log('URL changed:', url)
this.updateDocumentId(url)
})
}

View File

@ -1,5 +1,5 @@
import { DecimalPipe } from '@angular/common'
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
import { Component, EventEmitter, Input, Output } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { Subject } from 'rxjs'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
@ -12,7 +12,9 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
imports: [DecimalPipe, SafeHtmlPipe],
})
export class ConfirmDialogComponent extends LoadingComponentWithPermissions {
activeModal = inject(NgbActiveModal)
constructor(public activeModal: NgbActiveModal) {
super()
}
@Output()
public confirmClicked = new EventEmitter()

View File

@ -1,5 +1,6 @@
import { Component, TemplateRef, ViewChild, inject } from '@angular/core'
import { Component, TemplateRef, ViewChild } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import {
PDFDocumentProxy,
PdfViewerComponent,
@ -16,8 +17,6 @@ import { ConfirmDialogComponent } from '../confirm-dialog.component'
imports: [PdfViewerModule, FormsModule, ReactiveFormsModule, SafeHtmlPipe],
})
export class DeletePagesConfirmDialogComponent extends ConfirmDialogComponent {
private documentService = inject(DocumentService)
public documentID: number
public pages: number[] = []
public currentPage: number = 1
@ -35,8 +34,11 @@ export class DeletePagesConfirmDialogComponent extends ConfirmDialogComponent {
return this.documentService.getPreviewUrl(this.documentID)
}
constructor() {
super()
constructor(
activeModal: NgbActiveModal,
private documentService: DocumentService
) {
super(activeModal)
}
public pdfPreviewLoaded(pdf: PDFDocumentProxy) {

View File

@ -3,8 +3,9 @@ import {
DragDropModule,
moveItemInArray,
} from '@angular/cdk/drag-drop'
import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { takeUntil } from 'rxjs'
import { Document } from 'src/app/data/document'
@ -27,9 +28,6 @@ export class MergeConfirmDialogComponent
extends ConfirmDialogComponent
implements OnInit
{
private documentService = inject(DocumentService)
private permissionService = inject(PermissionsService)
public documentIDs: number[] = []
public archiveFallback: boolean = false
public deleteOriginals: boolean = false
@ -40,8 +38,12 @@ export class MergeConfirmDialogComponent
public metadataDocumentID: number = -1
constructor() {
super()
constructor(
activeModal: NgbActiveModal,
private documentService: DocumentService,
private permissionService: PermissionsService
) {
super(activeModal)
}
ngOnInit() {

View File

@ -1,5 +1,6 @@
import { NgStyle } from '@angular/common'
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { DocumentService } from 'src/app/services/rest/document.service'
@ -12,8 +13,6 @@ import { ConfirmDialogComponent } from '../confirm-dialog.component'
imports: [NgStyle, NgxBootstrapIconsModule, SafeHtmlPipe],
})
export class RotateConfirmDialogComponent extends ConfirmDialogComponent {
documentService = inject(DocumentService)
public documentID: number
public showPDFNote: boolean = true
@ -26,8 +25,11 @@ export class RotateConfirmDialogComponent extends ConfirmDialogComponent {
return degrees
}
constructor() {
super()
constructor(
activeModal: NgbActiveModal,
public documentService: DocumentService
) {
super(activeModal)
}
rotate(clockwise: boolean = true) {

View File

@ -1,5 +1,6 @@
import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { PDFDocumentProxy, PdfViewerModule } from 'ng2-pdf-viewer'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { Document } from 'src/app/data/document'
@ -22,9 +23,6 @@ export class SplitConfirmDialogComponent
extends ConfirmDialogComponent
implements OnInit
{
private documentService = inject(DocumentService)
private permissionService = inject(PermissionsService)
public get pagesString(): string {
let pagesStr = ''
@ -64,8 +62,12 @@ export class SplitConfirmDialogComponent
return this.documentService.getPreviewUrl(this.documentID)
}
constructor() {
super()
constructor(
activeModal: NgbActiveModal,
private documentService: DocumentService,
private permissionService: PermissionsService
) {
super(activeModal)
this.confirmButtonEnabled = this.pages.size > 0
}

View File

@ -1,5 +1,5 @@
import { CurrencyPipe, getLocaleCurrencyCode } from '@angular/common'
import { Component, Input, LOCALE_ID, OnInit, inject } from '@angular/core'
import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
import { takeUntil } from 'rxjs'
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
@ -20,9 +20,6 @@ export class CustomFieldDisplayComponent
extends LoadingComponentWithPermissions
implements OnInit
{
private customFieldService = inject(CustomFieldsService)
private documentService = inject(DocumentService)
CustomFieldDataType = CustomFieldDataType
private _document: Document
@ -66,9 +63,11 @@ export class CustomFieldDisplayComponent
private defaultCurrencyCode: any
constructor() {
const currentLocale = inject(LOCALE_ID)
constructor(
private customFieldService: CustomFieldsService,
private documentService: DocumentService,
@Inject(LOCALE_ID) currentLocale: string
) {
super()
this.defaultCurrencyCode = getLocaleCurrencyCode(currentLocale)
this.customFieldService.listAll().subscribe((r) => {

View File

@ -7,7 +7,6 @@ import {
QueryList,
ViewChild,
ViewChildren,
inject,
} from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'
@ -38,11 +37,6 @@ import { CustomFieldEditDialogComponent } from '../edit-dialog/custom-field-edit
],
})
export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissions {
private customFieldsService = inject(CustomFieldsService)
private modalService = inject(NgbModal)
private toastService = inject(ToastService)
private permissionsService = inject(PermissionsService)
public popperOptions = pngxPopperOptions
@Input()
@ -84,7 +78,12 @@ export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissio
)
}
constructor() {
constructor(
private customFieldsService: CustomFieldsService,
private modalService: NgbModal,
private toastService: ToastService,
private permissionsService: PermissionsService
) {
super()
this.getFields()
}

View File

@ -2,7 +2,6 @@ import { NgTemplateOutlet } from '@angular/common'
import {
Component,
EventEmitter,
inject,
Input,
Output,
QueryList,
@ -179,8 +178,6 @@ export class CustomFieldQueriesModel {
],
})
export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPermissions {
protected customFieldsService = inject(CustomFieldsService)
public CustomFieldQueryComponentType = CustomFieldQueryElementType
public CustomFieldQueryOperator = CustomFieldQueryOperator
public CustomFieldDataType = CustomFieldDataType
@ -248,7 +245,7 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm
public readonly today: string = new Date().toISOString().split('T')[0]
constructor() {
constructor(protected customFieldsService: CustomFieldsService) {
super()
this.selectionModel = new CustomFieldQueriesModel()
this.getFields()

View File

@ -6,7 +6,6 @@ import {
OnDestroy,
OnInit,
Output,
inject,
} from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import {
@ -64,9 +63,7 @@ export enum RelativeDate {
export class DatesDropdownComponent implements OnInit, OnDestroy {
public popperOptions = pngxPopperOptions
constructor() {
const settings = inject(SettingsService)
constructor(settings: SettingsService) {
this.datePlaceHolder = settings.getLocalizedDateInputFormat()
}

View File

@ -13,6 +13,8 @@
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
@if (patternRequired) {
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
}
@if (patternRequired) {
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></pngx-input-check>
}

View File

@ -1,10 +1,11 @@
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { Correspondent } from 'src/app/data/correspondent'
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
@ -12,7 +13,6 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
import { CheckComponent } from '../../input/check/check.component'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component'
@ -22,7 +22,6 @@ import { TextComponent } from '../../input/text/text.component'
templateUrl: './correspondent-edit-dialog.component.html',
styleUrls: ['./correspondent-edit-dialog.component.scss'],
imports: [
CheckComponent,
SelectComponent,
PermissionsFormComponent,
TextComponent,
@ -32,11 +31,13 @@ import { TextComponent } from '../../input/text/text.component'
],
})
export class CorrespondentEditDialogComponent extends EditDialogComponent<Correspondent> {
constructor() {
super()
this.service = inject(CorrespondentService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
constructor(
service: CorrespondentService,
activeModal: NgbActiveModal,
userService: UserService,
settingsService: SettingsService
) {
super(service, activeModal, userService, settingsService)
}
getCreateTitle() {

View File

@ -5,7 +5,6 @@ import {
OnInit,
QueryList,
ViewChildren,
inject,
} from '@angular/core'
import {
FormArray,
@ -14,6 +13,7 @@ import {
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { takeUntil } from 'rxjs'
import {
@ -54,11 +54,13 @@ export class CustomFieldEditDialogComponent
.select_options as FormArray
}
constructor() {
super()
this.service = inject(CustomFieldsService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
constructor(
service: CustomFieldsService,
activeModal: NgbActiveModal,
userService: UserService,
settingsService: SettingsService
) {
super(service, activeModal, userService, settingsService)
}
ngOnInit(): void {

View File

@ -14,6 +14,8 @@
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
@if (patternRequired) {
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
}
@if (patternRequired) {
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
}
</div>

View File

@ -1,10 +1,11 @@
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { DocumentType } from 'src/app/data/document-type'
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
@ -12,7 +13,6 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
import { CheckComponent } from '../../input/check/check.component'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component'
@ -22,7 +22,6 @@ import { TextComponent } from '../../input/text/text.component'
templateUrl: './document-type-edit-dialog.component.html',
styleUrls: ['./document-type-edit-dialog.component.scss'],
imports: [
CheckComponent,
SelectComponent,
PermissionsFormComponent,
TextComponent,
@ -32,11 +31,13 @@ import { TextComponent } from '../../input/text/text.component'
],
})
export class DocumentTypeEditDialogComponent extends EditDialogComponent<DocumentType> {
constructor() {
super()
this.service = inject(DocumentTypeService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
constructor(
service: DocumentTypeService,
activeModal: NgbActiveModal,
userService: UserService,
settingsService: SettingsService
) {
super(service, activeModal, userService, settingsService)
}
getCreateTitle() {

View File

@ -41,9 +41,13 @@ import { EditDialogComponent, EditDialogMode } from './edit-dialog.component'
imports: [FormsModule, ReactiveFormsModule],
})
class TestComponent extends EditDialogComponent<Tag> {
constructor() {
super()
this.service = TestBed.inject(TagService)
constructor(
service: TagService,
activeModal: NgbActiveModal,
userService: UserService,
settingsService: SettingsService
) {
super(service, activeModal, userService, settingsService)
}
getForm(): FormGroup<any> {

View File

@ -1,11 +1,4 @@
import {
Directive,
EventEmitter,
Input,
OnInit,
Output,
inject,
} from '@angular/core'
import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { Observable } from 'rxjs'
@ -36,12 +29,14 @@ export abstract class EditDialogComponent<
extends LoadingComponentWithPermissions
implements OnInit
{
protected service = inject<AbstractPaperlessService<T>>(
AbstractPaperlessService
)
protected activeModal = inject(NgbActiveModal)
protected userService = inject(UserService)
protected settingsService = inject(SettingsService)
constructor(
protected service: AbstractPaperlessService<T>,
private activeModal: NgbActiveModal,
private userService: UserService,
protected settingsService: SettingsService
) {
super()
}
users: User[]

View File

@ -1,10 +1,11 @@
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { Group } from 'src/app/data/group'
import { GroupService } from 'src/app/services/rest/group.service'
@ -25,11 +26,13 @@ import { PermissionsSelectComponent } from '../../permissions-select/permissions
],
})
export class GroupEditDialogComponent extends EditDialogComponent<Group> {
constructor() {
super()
this.service = inject(GroupService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
constructor(
service: GroupService,
activeModal: NgbActiveModal,
userService: UserService,
settingsService: SettingsService
) {
super(service, activeModal, userService, settingsService)
}
getCreateTitle() {

View File

@ -1,11 +1,15 @@
import { Component, ViewChild, inject } from '@angular/core'
import { Component, ViewChild } from '@angular/core'
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbAlert, NgbAlertModule } from '@ng-bootstrap/ng-bootstrap'
import {
NgbActiveModal,
NgbAlert,
NgbAlertModule,
} from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { IMAPSecurity, MailAccount } from 'src/app/data/mail-account'
import { MailAccountService } from 'src/app/services/rest/mail-account.service'
@ -43,11 +47,13 @@ export class MailAccountEditDialogComponent extends EditDialogComponent<MailAcco
@ViewChild('testResultAlert', { static: false }) testResultAlert: NgbAlert
constructor() {
super()
this.service = inject(MailAccountService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
constructor(
service: MailAccountService,
activeModal: NgbActiveModal,
userService: UserService,
settingsService: SettingsService
) {
super(service, activeModal, userService, settingsService)
}
getCreateTitle() {

View File

@ -1,10 +1,11 @@
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { first } from 'rxjs'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { Correspondent } from 'src/app/data/correspondent'
@ -154,34 +155,32 @@ const METADATA_CORRESPONDENT_OPTIONS = [
],
})
export class MailRuleEditDialogComponent extends EditDialogComponent<MailRule> {
private accountService: MailAccountService
private correspondentService: CorrespondentService
private documentTypeService: DocumentTypeService
accounts: MailAccount[]
correspondents: Correspondent[]
documentTypes: DocumentType[]
constructor() {
super()
this.service = inject(MailRuleService)
this.accountService = inject(MailAccountService)
this.correspondentService = inject(CorrespondentService)
this.documentTypeService = inject(DocumentTypeService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
constructor(
service: MailRuleService,
activeModal: NgbActiveModal,
accountService: MailAccountService,
correspondentService: CorrespondentService,
documentTypeService: DocumentTypeService,
userService: UserService,
settingsService: SettingsService
) {
super(service, activeModal, userService, settingsService)
this.accountService
accountService
.listAll()
.pipe(first())
.subscribe((result) => (this.accounts = result.results))
this.correspondentService
correspondentService
.listAll()
.pipe(first())
.subscribe((result) => (this.correspondents = result.results))
this.documentTypeService
documentTypeService
.listAll()
.pipe(first())
.subscribe((result) => (this.documentTypes = result.results))

View File

@ -64,6 +64,8 @@
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
@if (patternRequired) {
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
}
@if (patternRequired) {
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
}

View File

@ -1,12 +1,12 @@
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
import { Component, OnDestroy, inject } from '@angular/core'
import { Component, OnDestroy } from '@angular/core'
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
import { NgbAccordionModule, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectComponent } from '@ng-select/ng-select'
import {
Observable,
@ -60,8 +60,6 @@ export class StoragePathEditDialogComponent
extends EditDialogComponent<StoragePath>
implements OnDestroy
{
private documentsService = inject(DocumentService)
public documentsInput$ = new Subject<string>()
public foundDocuments$: Observable<Document[]>
private testDocument: Document
@ -70,11 +68,14 @@ export class StoragePathEditDialogComponent
public loading = false
public testLoading = false
constructor() {
super()
this.service = inject(StoragePathService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
constructor(
service: StoragePathService,
activeModal: NgbActiveModal,
userService: UserService,
settingsService: SettingsService,
private documentsService: DocumentService
) {
super(service, activeModal, userService, settingsService)
this.initPathObservables()
}

View File

@ -16,6 +16,8 @@
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
@if (patternRequired) {
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
}
@if (patternRequired) {
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
}

View File

@ -1,10 +1,11 @@
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
import { Tag } from 'src/app/data/tag'
@ -35,11 +36,13 @@ import { TextComponent } from '../../input/text/text.component'
],
})
export class TagEditDialogComponent extends EditDialogComponent<Tag> {
constructor() {
super()
this.service = inject(TagService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
constructor(
service: TagService,
activeModal: NgbActiveModal,
userService: UserService,
settingsService: SettingsService
) {
super(service, activeModal, userService, settingsService)
}
getCreateTitle() {

View File

@ -1,10 +1,11 @@
import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { first } from 'rxjs'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { Group } from 'src/app/data/group'
@ -36,21 +37,21 @@ export class UserEditDialogComponent
extends EditDialogComponent<User>
implements OnInit
{
private toastService = inject(ToastService)
private permissionsService = inject(PermissionsService)
private groupsService: GroupService
groups: Group[]
passwordIsSet: boolean = false
public totpLoading: boolean = false
constructor() {
super()
this.service = inject(UserService)
this.groupsService = inject(GroupService)
this.settingsService = inject(SettingsService)
constructor(
service: UserService,
activeModal: NgbActiveModal,
groupsService: GroupService,
settingsService: SettingsService,
private toastService: ToastService,
private permissionsService: PermissionsService
) {
super(service, activeModal, service, settingsService)
this.groupsService
groupsService
.listAll()
.pipe(first())
.subscribe((result) => (this.groups = result.results))

View File

@ -4,7 +4,7 @@ import {
moveItemInArray,
} from '@angular/cdk/drag-drop'
import { NgTemplateOutlet } from '@angular/common'
import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import {
FormArray,
FormControl,
@ -12,7 +12,7 @@ import {
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
import { NgbAccordionModule, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { first } from 'rxjs'
import { Correspondent } from 'src/app/data/correspondent'
@ -171,12 +171,6 @@ export class WorkflowEditDialogComponent
public WorkflowTriggerType = WorkflowTriggerType
public WorkflowActionType = WorkflowActionType
private correspondentService: CorrespondentService
private documentTypeService: DocumentTypeService
private storagePathService: StoragePathService
private mailRuleService: MailRuleService
private customFieldsService: CustomFieldsService
templates: Workflow[]
correspondents: Correspondent[]
documentTypes: DocumentType[]
@ -189,38 +183,40 @@ export class WorkflowEditDialogComponent
private allowedActionTypes = []
constructor() {
super()
this.service = inject(WorkflowService)
this.correspondentService = inject(CorrespondentService)
this.documentTypeService = inject(DocumentTypeService)
this.storagePathService = inject(StoragePathService)
this.mailRuleService = inject(MailRuleService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
this.customFieldsService = inject(CustomFieldsService)
constructor(
service: WorkflowService,
activeModal: NgbActiveModal,
correspondentService: CorrespondentService,
documentTypeService: DocumentTypeService,
storagePathService: StoragePathService,
mailRuleService: MailRuleService,
userService: UserService,
settingsService: SettingsService,
customFieldsService: CustomFieldsService
) {
super(service, activeModal, userService, settingsService)
this.correspondentService
correspondentService
.listAll()
.pipe(first())
.subscribe((result) => (this.correspondents = result.results))
this.documentTypeService
documentTypeService
.listAll()
.pipe(first())
.subscribe((result) => (this.documentTypes = result.results))
this.storagePathService
storagePathService
.listAll()
.pipe(first())
.subscribe((result) => (this.storagePaths = result.results))
this.mailRuleService
mailRuleService
.listAll()
.pipe(first())
.subscribe((result) => (this.mailRules = result.results))
this.customFieldsService
customFieldsService
.listAll()
.pipe(first())
.subscribe((result) => {

View File

@ -1,4 +1,4 @@
import { Component, Input, inject } from '@angular/core'
import { Component, Input } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
@ -13,10 +13,6 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
imports: [FormsModule, NgxBootstrapIconsModule],
})
export class EmailDocumentDialogComponent extends LoadingComponentWithPermissions {
private activeModal = inject(NgbActiveModal)
private documentService = inject(DocumentService)
private toastService = inject(ToastService)
@Input()
title = $localize`Email Document`
@ -41,7 +37,11 @@ export class EmailDocumentDialogComponent extends LoadingComponentWithPermission
public emailSubject: string = ''
public emailMessage: string = ''
constructor() {
constructor(
private activeModal: NgbActiveModal,
private documentService: DocumentService,
private toastService: ToastService
) {
super()
this.loading = false
}

View File

@ -7,7 +7,6 @@ import {
OnInit,
Output,
ViewChild,
inject,
} from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
@ -435,9 +434,6 @@ export class FilterableDropdownComponent
extends LoadingComponentWithPermissions
implements OnInit
{
private filterPipe = inject(FilterPipe)
private hotkeyService = inject(HotKeyService)
@ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
@ViewChild('dropdown') dropdown: NgbDropdown
@ViewChild('buttonItems') buttonItems: ElementRef
@ -540,7 +536,10 @@ export class FilterableDropdownComponent
private keyboardIndex: number
constructor() {
constructor(
private filterPipe: FilterPipe,
private hotkeyService: HotKeyService
) {
super()
this.selectionModelChange.subscribe((updatedModel) => {
this.modelIsDirty = updatedModel.isDirty()

View File

@ -1,4 +1,4 @@
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
const SYMBOLS = {
@ -19,11 +19,11 @@ const SYMBOLS = {
styleUrl: './hotkey-dialog.component.scss',
})
export class HotkeyDialogComponent {
activeModal = inject(NgbActiveModal)
public title: string = $localize`Keyboard shortcuts`
public hotkeys: Map<string, string> = new Map()
constructor(public activeModal: NgbActiveModal) {}
public close(): void {
this.activeModal.close()
}

View File

@ -2,7 +2,6 @@ import {
Component,
EventEmitter,
forwardRef,
inject,
Input,
Output,
} from '@angular/core'
@ -56,9 +55,7 @@ import { UrlComponent } from '../url/url.component'
export class CustomFieldsValuesComponent extends AbstractInputComponent<Object> {
public CustomFieldDataType = CustomFieldDataType
constructor() {
const customFieldsService = inject(CustomFieldsService)
constructor(customFieldsService: CustomFieldsService) {
super()
customFieldsService.listAll().subscribe((items) => {
this.fields = items.results

View File

@ -2,7 +2,6 @@ import {
Component,
EventEmitter,
forwardRef,
inject,
Input,
OnInit,
Output,
@ -46,9 +45,13 @@ export class DateComponent
extends AbstractInputComponent<string>
implements OnInit
{
private settings = inject(SettingsService)
private ngbDateParserFormatter = inject(NgbDateParserFormatter)
private isoDateAdapter = inject<NgbDateAdapter<string>>(NgbDateAdapter)
constructor(
private settings: SettingsService,
private ngbDateParserFormatter: NgbDateParserFormatter,
private isoDateAdapter: NgbDateAdapter<string>
) {
super()
}
@Input()
suggestions: string[]

View File

@ -1,12 +1,5 @@
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
import {
Component,
forwardRef,
inject,
Input,
OnDestroy,
OnInit,
} from '@angular/core'
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'
import {
FormsModule,
NG_VALUE_ACCESSOR,
@ -59,8 +52,6 @@ export class DocumentLinkComponent
extends AbstractInputComponent<any[]>
implements OnInit, OnDestroy
{
private documentsService = inject(DocumentService)
documentsInput$ = new Subject<string>()
foundDocuments$: Observable<Document[]>
loading = false
@ -84,6 +75,10 @@ export class DocumentLinkComponent
return this.selectedDocuments.map((d) => d.id)
}
constructor(private documentsService: DocumentService) {
super()
}
ngOnInit() {
this.loadDocs()
}

View File

@ -1,6 +1,5 @@
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import { LOCALE_ID } from '@angular/core'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { MonetaryComponent } from './monetary.component'
@ -42,6 +41,8 @@ describe('MonetaryComponent', () => {
it('should set the default currency code based on LOCALE_ID', () => {
expect(component.defaultCurrencyCode).toEqual('USD') // default
component = new MonetaryComponent('pt-BR')
expect(component.defaultCurrencyCode).toEqual('BRL')
})
it('should support setting a default currency code', () => {
@ -86,28 +87,3 @@ describe('MonetaryComponent', () => {
expect(component.value).toEqual('USD0.00')
})
})
describe('MonetaryComponent (Alternate Locale)', () => {
let component: MonetaryComponent
let fixture: ComponentFixture<MonetaryComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MonetaryComponent],
providers: [
{ provide: LOCALE_ID, useValue: 'pt-BR' }, // Brazilian Portuguese
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
],
}).compileComponents()
fixture = TestBed.createComponent(MonetaryComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should set the default currency code based on LOCALE_ID', () => {
expect(component.defaultCurrencyCode).toEqual('BRL')
})
})

View File

@ -1,5 +1,5 @@
import { CurrencyPipe, getLocaleCurrencyCode } from '@angular/common'
import { Component, forwardRef, inject, Input, LOCALE_ID } from '@angular/core'
import { Component, forwardRef, Inject, Input, LOCALE_ID } from '@angular/core'
import {
FormsModule,
NG_VALUE_ACCESSOR,
@ -27,8 +27,6 @@ import { AbstractInputComponent } from '../abstract-input'
],
})
export class MonetaryComponent extends AbstractInputComponent<string> {
currentLocale = inject(LOCALE_ID)
public currency: string = ''
public _monetaryValue: string = ''
@ -47,10 +45,11 @@ export class MonetaryComponent extends AbstractInputComponent<string> {
if (currency) this.defaultCurrencyCode = currency
}
constructor() {
constructor(@Inject(LOCALE_ID) currentLocale: string) {
super()
this.currency = this.defaultCurrencyCode =
this.defaultCurrency ?? getLocaleCurrencyCode(this.currentLocale)
this.defaultCurrency ?? getLocaleCurrencyCode(currentLocale)
}
writeValue(newValue: any): void {

View File

@ -1,4 +1,4 @@
import { Component, forwardRef, inject, Input } from '@angular/core'
import { Component, forwardRef, Input } from '@angular/core'
import {
FormsModule,
NG_VALUE_ACCESSOR,
@ -22,14 +22,16 @@ import { AbstractInputComponent } from '../abstract-input'
imports: [FormsModule, ReactiveFormsModule, NgxBootstrapIconsModule],
})
export class NumberComponent extends AbstractInputComponent<number> {
private documentService = inject(DocumentService)
@Input()
showAdd: boolean = true
@Input()
step: number = 1
constructor(private documentService: DocumentService) {
super()
}
nextAsn() {
if (this.value) {
return

View File

@ -1,4 +1,4 @@
import { Component, forwardRef, inject } from '@angular/core'
import { Component, forwardRef } from '@angular/core'
import {
FormsModule,
NG_VALUE_ACCESSOR,
@ -26,9 +26,7 @@ import { AbstractInputComponent } from '../../abstract-input'
export class PermissionsGroupComponent extends AbstractInputComponent<Group> {
groups: Group[]
constructor() {
const groupService = inject(GroupService)
constructor(groupService: GroupService) {
super()
groupService
.listAll()

View File

@ -1,4 +1,4 @@
import { Component, forwardRef, inject } from '@angular/core'
import { Component, forwardRef } from '@angular/core'
import {
FormsModule,
NG_VALUE_ACCESSOR,
@ -8,6 +8,7 @@ import { NgSelectComponent } from '@ng-select/ng-select'
import { first } from 'rxjs/operators'
import { User } from 'src/app/data/user'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
import { AbstractInputComponent } from '../../abstract-input'
@Component({
@ -26,9 +27,7 @@ import { AbstractInputComponent } from '../../abstract-input'
export class PermissionsUserComponent extends AbstractInputComponent<User[]> {
users: User[]
constructor() {
const userService = inject(UserService)
constructor(userService: UserService, settings: SettingsService) {
super()
userService
.listAll()

View File

@ -2,7 +2,6 @@ import {
Component,
EventEmitter,
forwardRef,
inject,
Input,
OnInit,
Output,
@ -46,10 +45,10 @@ import { TagComponent } from '../../tag/tag.component'
],
})
export class TagsComponent implements OnInit, ControlValueAccessor {
private tagService = inject(TagService)
private modalService = inject(NgbModal)
constructor() {
constructor(
private tagService: TagService,
private modalService: NgbModal
) {
this.createTagRef = this.createTag.bind(this)
}

View File

@ -1,4 +1,4 @@
import { Component, Input, inject } from '@angular/core'
import { Component, Input } from '@angular/core'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { SettingsService } from 'src/app/services/settings.service'
import { environment } from 'src/environments/environment'
@ -9,8 +9,6 @@ import { environment } from 'src/environments/environment'
styleUrls: ['./logo.component.scss'],
})
export class LogoComponent {
private settingsService = inject(SettingsService)
@Input()
extra_classes: string
@ -26,6 +24,8 @@ export class LogoComponent {
: null
}
constructor(private settingsService: SettingsService) {}
getClasses() {
return ['logo'].concat(this.extra_classes).join(' ')
}

View File

@ -1,4 +1,4 @@
import { Component, Input, inject } from '@angular/core'
import { Component, Input } from '@angular/core'
import { Title } from '@angular/platform-browser'
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
@ -12,7 +12,7 @@ import { environment } from 'src/environments/environment'
imports: [NgbPopoverModule, NgxBootstrapIconsModule, TourNgBootstrapModule],
})
export class PageHeaderComponent {
private titleService = inject(Title)
constructor(private titleService: Title) {}
_title = ''

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
import { Component, EventEmitter, Input, Output } from '@angular/core'
import {
FormControl,
FormGroup,
@ -24,13 +24,13 @@ import { SwitchComponent } from '../input/switch/switch.component'
],
})
export class PermissionsDialogComponent {
activeModal = inject(NgbActiveModal)
private userService = inject(UserService)
users: User[]
private o: ObjectWithPermissions = undefined
constructor() {
constructor(
public activeModal: NgbActiveModal,
private userService: UserService
) {
this.userService.listAll().subscribe((r) => (this.users = r.results))
}

View File

@ -1,5 +1,5 @@
import { NgClass } from '@angular/common'
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
import { Component, EventEmitter, Input, Output } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectComponent } from '@ng-select/ng-select'
@ -58,9 +58,6 @@ export enum OwnerFilterType {
],
})
export class PermissionsFilterDropdownComponent extends ComponentWithPermissions {
permissionsService = inject(PermissionsService)
private settingsService = inject(SettingsService)
public OwnerFilterType = OwnerFilterType
@Input()
@ -86,12 +83,12 @@ export class PermissionsFilterDropdownComponent extends ComponentWithPermissions
)
}
constructor() {
const userService = inject(UserService)
constructor(
public permissionsService: PermissionsService,
userService: UserService,
private settingsService: SettingsService
) {
super()
const permissionsService = this.permissionsService
if (
permissionsService.currentUserCan(
PermissionAction.View,

View File

@ -1,5 +1,5 @@
import { KeyValuePipe } from '@angular/common'
import { Component, forwardRef, inject, Input, OnInit } from '@angular/core'
import { Component, forwardRef, Input, OnInit } from '@angular/core'
import {
AbstractControl,
ControlValueAccessor,
@ -43,9 +43,6 @@ export class PermissionsSelectComponent
extends ComponentWithPermissions
implements OnInit, ControlValueAccessor
{
private readonly permissionsService = inject(PermissionsService)
private readonly settingsService = inject(SettingsService)
@Input()
title: string = 'Permissions'
@ -79,7 +76,10 @@ export class PermissionsSelectComponent
public allowedTypes = Object.keys(PermissionType)
constructor() {
constructor(
private readonly permissionsService: PermissionsService,
private readonly settingsService: SettingsService
) {
super()
if (!this.settingsService.get(SETTINGS_KEYS.AUDITLOG_ENABLED)) {
this.allowedTypes.splice(this.allowedTypes.indexOf('History'), 1)

View File

@ -1,5 +1,5 @@
import { HttpClient } from '@angular/common/http'
import { Component, inject, Input, OnDestroy, ViewChild } from '@angular/core'
import { Component, Input, OnDestroy, ViewChild } from '@angular/core'
import { NgbPopover, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
import { PdfViewerComponent, PdfViewerModule } from 'ng2-pdf-viewer'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
@ -24,10 +24,6 @@ import { SettingsService } from 'src/app/services/settings.service'
],
})
export class PreviewPopupComponent implements OnDestroy {
private settingsService = inject(SettingsService)
private documentService = inject(DocumentService)
private http = inject(HttpClient)
private _document: Document
@Input()
set document(document: Document) {
@ -86,6 +82,12 @@ export class PreviewPopupComponent implements OnDestroy {
)
}
constructor(
private settingsService: SettingsService,
private documentService: DocumentService,
private http: HttpClient
) {}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(this)
}

View File

@ -1,5 +1,5 @@
import { Clipboard } from '@angular/cdk/clipboard'
import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import {
FormControl,
FormGroup,
@ -46,11 +46,6 @@ export class ProfileEditDialogComponent
extends LoadingComponentWithPermissions
implements OnInit
{
private profileService = inject(ProfileService)
activeModal = inject(NgbActiveModal)
private toastService = inject(ToastService)
private clipboard = inject(Clipboard)
public networkActive: boolean = false
public error: any
@ -88,6 +83,15 @@ export class ProfileEditDialogComponent
public socialAccounts: SocialAccount[] = []
public socialAccountProviders: SocialAccountProvider[] = []
constructor(
private profileService: ProfileService,
public activeModal: NgbActiveModal,
private toastService: ToastService,
private clipboard: Clipboard
) {
super()
}
ngOnInit(): void {
this.networkActive = true
this.profileService

View File

@ -10,7 +10,6 @@ import {
fakeAsync,
tick,
} from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { By } from '@angular/platform-browser'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
@ -34,8 +33,6 @@ describe('ShareLinksDialogComponent', () => {
imports: [
ShareLinksDialogComponent,
NgxBootstrapIconsModule.pick(allIcons),
FormsModule,
ReactiveFormsModule,
],
providers: [
provideHttpClient(withInterceptorsFromDi()),
@ -226,18 +223,16 @@ describe('ShareLinksDialogComponent', () => {
)
})
it('should disable archive switch & option if no archive available', (done) => {
it('should disable archive switch & option if no archive available', () => {
component.hasArchiveVersion = false
component.ngOnInit()
fixture.detectChanges()
expect(component.useArchiveVersion).toBeFalsy()
setTimeout(() => {
// some stupid change detection issue
const inputEl = fixture.debugElement.query(By.css('#versionSwitch'))
.nativeElement as HTMLInputElement
expect(inputEl.disabled).toBeTruthy()
done()
})
expect(
fixture.debugElement.query(By.css("input[type='checkbox']")).attributes[
'ng-reflect-is-disabled'
]
).toBeTruthy()
})
it('should support close', () => {

View File

@ -1,5 +1,5 @@
import { Clipboard } from '@angular/cdk/clipboard'
import { Component, Input, OnInit, inject } from '@angular/core'
import { Component, Input, OnInit } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
@ -16,11 +16,6 @@ import { environment } from 'src/environments/environment'
imports: [FormsModule, ReactiveFormsModule, NgxBootstrapIconsModule],
})
export class ShareLinksDialogComponent implements OnInit {
private activeModal = inject(NgbActiveModal)
private shareLinkService = inject(ShareLinkService)
private toastService = inject(ToastService)
private clipboard = inject(Clipboard)
EXPIRATION_OPTIONS = [
{ label: $localize`1 day`, value: 1 },
{ label: $localize`7 days`, value: 7 },
@ -63,6 +58,13 @@ export class ShareLinksDialogComponent implements OnInit {
useArchiveVersion: boolean = true
constructor(
private activeModal: NgbActiveModal,
private shareLinkService: ShareLinkService,
private toastService: ToastService,
private clipboard: Clipboard
) {}
ngOnInit(): void {
if (this._documentId !== undefined) this.refresh()
}

View File

@ -1,5 +1,5 @@
import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard'
import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import {
NgbActiveModal,
NgbModalModule,
@ -37,14 +37,6 @@ import { environment } from 'src/environments/environment'
],
})
export class SystemStatusDialogComponent implements OnInit {
activeModal = inject(NgbActiveModal)
private clipboard = inject(Clipboard)
private systemStatusService = inject(SystemStatusService)
private tasksService = inject(TasksService)
private toastService = inject(ToastService)
private permissionsService = inject(PermissionsService)
private settingsService = inject(SettingsService)
public SystemStatusItemStatus = SystemStatusItemStatus
public PaperlessTaskName = PaperlessTaskName
public status: SystemStatus
@ -63,6 +55,16 @@ export class SystemStatusDialogComponent implements OnInit {
return this.settingsService.get(SETTINGS_KEYS.AI_ENABLED)
}
constructor(
public activeModal: NgbActiveModal,
private clipboard: Clipboard,
private systemStatusService: SystemStatusService,
private tasksService: TasksService,
private toastService: ToastService,
private permissionsService: PermissionsService,
private settingsService: SettingsService
) {}
public ngOnInit() {
this.versionMismatch =
environment.production &&

View File

@ -1,4 +1,4 @@
import { Component, inject, Input } from '@angular/core'
import { Component, Input } from '@angular/core'
import { Tag } from 'src/app/data/tag'
import {
PermissionAction,
@ -13,12 +13,14 @@ import { TagService } from 'src/app/services/rest/tag.service'
styleUrls: ['./tag.component.scss'],
})
export class TagComponent {
private permissionsService = inject(PermissionsService)
private tagService = inject(TagService)
private _tag: Tag
private _tagID: number
constructor(
private permissionsService: PermissionsService,
private tagService: TagService
) {}
@Input()
public set tag(tag: Tag) {
this._tag = tag

View File

@ -1,6 +1,6 @@
import { Clipboard } from '@angular/cdk/clipboard'
import { DecimalPipe } from '@angular/common'
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
import { Component, EventEmitter, Input, Output } from '@angular/core'
import {
NgbProgressbarModule,
NgbToastModule,
@ -21,8 +21,6 @@ import { Toast } from 'src/app/services/toast.service'
styleUrl: './toast.component.scss',
})
export class ToastComponent {
private clipboard = inject(Clipboard)
@Input() toast: Toast
@Input() autohide: boolean = true
@ -33,6 +31,8 @@ export class ToastComponent {
public copied: boolean = false
constructor(private clipboard: Clipboard) {}
onShown(toast: Toast) {
if (!this.autohide) return

View File

@ -1,4 +1,4 @@
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import { Component, OnDestroy, OnInit } from '@angular/core'
import {
NgbAccordionModule,
NgbProgressbarModule,
@ -20,7 +20,7 @@ import { ToastComponent } from '../toast/toast.component'
],
})
export class ToastsComponent implements OnInit, OnDestroy {
toastService = inject(ToastService)
constructor(public toastService: ToastService) {}
private subscription: Subscription

View File

@ -5,7 +5,7 @@ import {
DragDropModule,
moveItemInArray,
} from '@angular/cdk/drag-drop'
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { RouterModule } from '@angular/router'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
@ -42,13 +42,13 @@ import { WelcomeWidgetComponent } from './widgets/welcome-widget/welcome-widget.
],
})
export class DashboardComponent extends ComponentWithPermissions {
settingsService = inject(SettingsService)
savedViewService = inject(SavedViewService)
private tourService = inject(TourService)
private toastService = inject(ToastService)
public dashboardViews: SavedView[] = []
constructor() {
constructor(
public settingsService: SettingsService,
public savedViewService: SavedViewService,
private tourService: TourService,
private toastService: ToastService
) {
super()
this.savedViewService.listAll().subscribe(() => {

View File

@ -1,7 +1,6 @@
import { AsyncPipe, NgClass, NgStyle } from '@angular/common'
import {
Component,
inject,
Input,
OnDestroy,
OnInit,
@ -85,22 +84,26 @@ export class SavedViewWidgetComponent
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy
{
private documentService = inject(DocumentService)
private router = inject(Router)
private list = inject(DocumentListViewService)
private websocketStatusService = inject(WebsocketStatusService)
openDocumentsService = inject(OpenDocumentsService)
documentListViewService = inject(DocumentListViewService)
permissionsService = inject(PermissionsService)
private settingsService = inject(SettingsService)
private customFieldService = inject(CustomFieldsService)
public DisplayMode = DisplayMode
public DisplayField = DisplayField
public CustomFieldDataType = CustomFieldDataType
private customFields: CustomField[] = []
constructor(
private documentService: DocumentService,
private router: Router,
private list: DocumentListViewService,
private websocketStatusService: WebsocketStatusService,
public openDocumentsService: OpenDocumentsService,
public documentListViewService: DocumentListViewService,
public permissionsService: PermissionsService,
private settingsService: SettingsService,
private customFieldService: CustomFieldsService
) {
super()
}
@Input()
savedView: SavedView

View File

@ -1,6 +1,6 @@
import { DecimalPipe } from '@angular/common'
import { HttpClient } from '@angular/common/http'
import { Component, inject, OnDestroy, OnInit } from '@angular/core'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { RouterModule } from '@angular/router'
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
import * as mimeTypeNames from 'mime-names'
@ -51,12 +51,16 @@ export class StatisticsWidgetComponent
extends ComponentWithPermissions
implements OnInit, OnDestroy
{
private http = inject(HttpClient)
private websocketConnectionService = inject(WebsocketStatusService)
private documentListViewService = inject(DocumentListViewService)
loading: boolean = false
constructor(
private http: HttpClient,
private websocketConnectionService: WebsocketStatusService,
private documentListViewService: DocumentListViewService
) {
super()
}
statistics: Statistics = {}
subscription: Subscription

View File

@ -1,5 +1,5 @@
import { NgClass, NgTemplateOutlet } from '@angular/common'
import { Component, QueryList, ViewChildren, inject } from '@angular/core'
import { Component, QueryList, ViewChildren } from '@angular/core'
import { RouterModule } from '@angular/router'
import {
NgbAlert,
@ -37,12 +37,16 @@ import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
],
})
export class UploadFileWidgetComponent extends ComponentWithPermissions {
private websocketStatusService = inject(WebsocketStatusService)
private uploadDocumentsService = inject(UploadDocumentsService)
settingsService = inject(SettingsService)
@ViewChildren(NgbAlert) alerts: QueryList<NgbAlert>
constructor(
private websocketStatusService: WebsocketStatusService,
private uploadDocumentsService: UploadDocumentsService,
public settingsService: SettingsService
) {
super()
}
getStatus() {
return this.websocketStatusService.getConsumerStatus()
}

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Output, inject } from '@angular/core'
import { Component, EventEmitter, Output } from '@angular/core'
import { NgbAlertModule } from '@ng-bootstrap/ng-bootstrap'
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
@ -9,7 +9,7 @@ import { TourService } from 'ngx-ui-tour-ng-bootstrap'
imports: [NgbAlertModule],
})
export class WelcomeWidgetComponent {
readonly tourService = inject(TourService)
constructor(public readonly tourService: TourService) {}
@Output()
dismiss: EventEmitter<boolean> = new EventEmitter()

View File

@ -1,4 +1,4 @@
import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { FILTER_ASN } from '../../data/filter-rule-type'
import { DocumentService } from '../../services/rest/document.service'
@ -9,11 +9,12 @@ import { DocumentService } from '../../services/rest/document.service'
styleUrls: ['./document-asn.component.scss'],
})
export class DocumentAsnComponent implements OnInit {
private documentsService = inject(DocumentService)
private route = inject(ActivatedRoute)
private router = inject(Router)
asn: string
constructor(
private documentsService: DocumentService,
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit(): void {
this.route.paramMap.subscribe((paramMap) => {

View File

@ -1,6 +1,6 @@
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
import { HttpClient, HttpResponse } from '@angular/common/http'
import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core'
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'
import {
FormArray,
FormControl,
@ -180,27 +180,6 @@ export class DocumentDetailComponent
extends ComponentWithPermissions
implements OnInit, OnDestroy, DirtyComponent
{
private documentsService = inject(DocumentService)
private route = inject(ActivatedRoute)
private tagService = inject(TagService)
private correspondentService = inject(CorrespondentService)
private documentTypeService = inject(DocumentTypeService)
private router = inject(Router)
private modalService = inject(NgbModal)
private openDocumentService = inject(OpenDocumentsService)
private documentListViewService = inject(DocumentListViewService)
private documentTitlePipe = inject(DocumentTitlePipe)
private toastService = inject(ToastService)
private settings = inject(SettingsService)
private storagePathService = inject(StoragePathService)
private permissionsService = inject(PermissionsService)
private userService = inject(UserService)
private customFieldsService = inject(CustomFieldsService)
private http = inject(HttpClient)
private hotKeyService = inject(HotKeyService)
private componentRouterService = inject(ComponentRouterService)
private deviceDetectorService = inject(DeviceDetectorService)
@ViewChild('inputTitle')
titleInput: TextComponent
@ -287,6 +266,31 @@ export class DocumentDetailComponent
DocumentDetailNavIDs = DocumentDetailNavIDs
activeNavID: number
constructor(
private documentsService: DocumentService,
private route: ActivatedRoute,
private tagService: TagService,
private correspondentService: CorrespondentService,
private documentTypeService: DocumentTypeService,
private router: Router,
private modalService: NgbModal,
private openDocumentService: OpenDocumentsService,
private documentListViewService: DocumentListViewService,
private documentTitlePipe: DocumentTitlePipe,
private toastService: ToastService,
private settings: SettingsService,
private storagePathService: StoragePathService,
private permissionsService: PermissionsService,
private userService: UserService,
private customFieldsService: CustomFieldsService,
private http: HttpClient,
private hotKeyService: HotKeyService,
private componentRouterService: ComponentRouterService,
private deviceDetectorService: DeviceDetectorService
) {
super()
}
titleKeyUp(event) {
this.titleSubject.next(event.target?.value)
}

View File

@ -1,5 +1,5 @@
import { AsyncPipe, KeyValuePipe, TitleCasePipe } from '@angular/common'
import { Component, Input, OnInit, inject } from '@angular/core'
import { Component, Input, OnInit } from '@angular/core'
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { Observable, first, map, of } from 'rxjs'
@ -26,12 +26,6 @@ import { UserService } from 'src/app/services/rest/user.service'
],
})
export class DocumentHistoryComponent implements OnInit {
private documentService = inject(DocumentService)
private correspondentService = inject(CorrespondentService)
private storagePathService = inject(StoragePathService)
private documentTypeService = inject(DocumentTypeService)
private userService = inject(UserService)
public AuditLogAction = AuditLogAction
private _documentId: number
@ -44,6 +38,14 @@ export class DocumentHistoryComponent implements OnInit {
public loading: boolean = true
public entries: AuditLogEntry[] = []
constructor(
private documentService: DocumentService,
private correspondentService: CorrespondentService,
private storagePathService: StoragePathService,
private documentTypeService: DocumentTypeService,
private userService: UserService
) {}
ngOnInit(): void {
if (this._documentId) {
this.loading = true

View File

@ -1,4 +1,4 @@
import { Component, inject, Input, OnDestroy, OnInit } from '@angular/core'
import { Component, Input, OnDestroy, OnInit } from '@angular/core'
import {
FormControl,
FormGroup,
@ -71,19 +71,6 @@ export class BulkEditorComponent
extends ComponentWithPermissions
implements OnInit, OnDestroy
{
private documentTypeService = inject(DocumentTypeService)
private tagService = inject(TagService)
private correspondentService = inject(CorrespondentService)
list = inject(DocumentListViewService)
private documentService = inject(DocumentService)
private modalService = inject(NgbModal)
private openDocumentService = inject(OpenDocumentsService)
private settings = inject(SettingsService)
private toastService = inject(ToastService)
private storagePathService = inject(StoragePathService)
private customFieldService = inject(CustomFieldsService)
private permissionService = inject(PermissionsService)
tagSelectionModel = new FilterableDropdownSelectionModel(true)
correspondentSelectionModel = new FilterableDropdownSelectionModel()
documentTypeSelectionModel = new FilterableDropdownSelectionModel()
@ -107,6 +94,23 @@ export class BulkEditorComponent
@Input()
public disabled: boolean = false
constructor(
private documentTypeService: DocumentTypeService,
private tagService: TagService,
private correspondentService: CorrespondentService,
public list: DocumentListViewService,
private documentService: DocumentService,
private modalService: NgbModal,
private openDocumentService: OpenDocumentsService,
private settings: SettingsService,
private toastService: ToastService,
private storagePathService: StoragePathService,
private customFieldService: CustomFieldsService,
private permissionService: PermissionsService
) {
super()
}
applyOnClose: boolean = this.settings.get(
SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE
)

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Output, inject } from '@angular/core'
import { Component, EventEmitter, Output } from '@angular/core'
import {
FormControl,
FormGroup,
@ -38,9 +38,6 @@ import { DocumentService } from 'src/app/services/rest/document.service'
],
})
export class CustomFieldsBulkEditDialogComponent {
private activeModal = inject(NgbActiveModal)
private documentService = inject(DocumentService)
CustomFieldDataType = CustomFieldDataType
@Output()
@ -76,6 +73,11 @@ export class CustomFieldsBulkEditDialogComponent {
public documents: number[] = []
constructor(
private activeModal: NgbActiveModal,
private documentService: DocumentService
) {}
initForm() {
Object.keys(this.form.controls).forEach((key) => {
if (!this._fieldsToAddIds.includes(parseInt(key))) {

View File

@ -6,7 +6,6 @@ import {
Input,
Output,
ViewChild,
inject,
} from '@angular/core'
import { RouterModule } from '@angular/router'
import {
@ -63,11 +62,15 @@ export class DocumentCardLargeComponent
extends LoadingComponentWithPermissions
implements AfterViewInit
{
private documentService = inject(DocumentService)
settingsService = inject(SettingsService)
DisplayField = DisplayField
constructor(
private documentService: DocumentService,
public settingsService: SettingsService
) {
super()
}
@Input()
selected = false

View File

@ -71,14 +71,14 @@
}
@if (displayFields.includes(DisplayField.CREATED)) {
<div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
<ng-template #dateCreatedTooltip>
<ng-template #dateTooltip>
<div class="d-flex flex-column text-light">
<span i18n>Created: {{ document.created | customDate }}</span>
<span i18n>Added: {{ document.added | customDate }}</span>
<span i18n>Modified: {{ document.modified | customDate }}</span>
</div>
</ng-template>
<div class="ps-0 p-1" placement="top" [ngbTooltip]="dateCreatedTooltip">
<div class="ps-0 p-1" placement="top" [ngbTooltip]="dateTooltip">
<i-bs width="1em" height="1em" class="me-2 text-muted" name="calendar-event"></i-bs>
<small>{{document.created | customDate:'mediumDate'}}</small>
</div>
@ -86,14 +86,14 @@
}
@if (displayFields.includes(DisplayField.ADDED)) {
<div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
<ng-template #dateAddedTooltip>
<ng-template #dateTooltip>
<div class="d-flex flex-column text-light">
<span i18n>Created: {{ document.created | customDate }}</span>
<span i18n>Added: {{ document.added | customDate }}</span>
<span i18n>Modified: {{ document.modified | customDate }}</span>
</div>
</ng-template>
<div class="ps-0 p-1" placement="top" [ngbTooltip]="dateAddedTooltip">
<div class="ps-0 p-1" placement="top" [ngbTooltip]="dateTooltip">
<i-bs width="1em" height="1em" class="me-2 text-muted" name="calendar-event"></i-bs>
<small>{{document.added | customDate:'mediumDate'}}</small>
</div>

View File

@ -6,7 +6,6 @@ import {
Input,
Output,
ViewChild,
inject,
} from '@angular/core'
import { RouterModule } from '@angular/router'
import {
@ -64,11 +63,15 @@ export class DocumentCardSmallComponent
extends LoadingComponentWithPermissions
implements AfterViewInit
{
private documentService = inject(DocumentService)
settingsService = inject(SettingsService)
DisplayField = DisplayField
constructor(
private documentService: DocumentService,
public settingsService: SettingsService
) {
super()
}
@Input()
selected = false

View File

@ -1,7 +1,6 @@
import { AsyncPipe, NgClass, NgTemplateOutlet } from '@angular/common'
import {
Component,
inject,
OnDestroy,
OnInit,
QueryList,
@ -104,21 +103,25 @@ export class DocumentListComponent
extends ComponentWithPermissions
implements OnInit, OnDestroy
{
list = inject(DocumentListViewService)
savedViewService = inject(SavedViewService)
route = inject(ActivatedRoute)
private router = inject(Router)
private toastService = inject(ToastService)
private modalService = inject(NgbModal)
private websocketStatusService = inject(WebsocketStatusService)
openDocumentsService = inject(OpenDocumentsService)
settingsService = inject(SettingsService)
private hotKeyService = inject(HotKeyService)
permissionService = inject(PermissionsService)
DisplayField = DisplayField
DisplayMode = DisplayMode
constructor(
public list: DocumentListViewService,
public savedViewService: SavedViewService,
public route: ActivatedRoute,
private router: Router,
private toastService: ToastService,
private modalService: NgbModal,
private websocketStatusService: WebsocketStatusService,
public openDocumentsService: OpenDocumentsService,
public settingsService: SettingsService,
private hotKeyService: HotKeyService,
public permissionService: PermissionsService
) {
super()
}
@ViewChild('filterEditor')
private filterEditor: FilterEditorComponent

View File

@ -8,7 +8,6 @@ import {
OnInit,
Output,
ViewChild,
inject,
} from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import {
@ -241,15 +240,6 @@ export class FilterEditorComponent
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy, AfterViewInit
{
private documentTypeService = inject(DocumentTypeService)
private tagService = inject(TagService)
private correspondentService = inject(CorrespondentService)
private documentService = inject(DocumentService)
private storagePathService = inject(StoragePathService)
permissionsService = inject(PermissionsService)
private customFieldService = inject(CustomFieldsService)
private searchService = inject(SearchService)
generateFilterName() {
if (this.filterRules.length == 1) {
let rule = this.filterRules[0]
@ -323,6 +313,19 @@ export class FilterEditorComponent
return ''
}
constructor(
private documentTypeService: DocumentTypeService,
private tagService: TagService,
private correspondentService: CorrespondentService,
private documentService: DocumentService,
private storagePathService: StoragePathService,
public permissionsService: PermissionsService,
private customFieldService: CustomFieldsService,
private searchService: SearchService
) {
super()
}
@ViewChild('textFilterInput')
textFilterInput: ElementRef
@ -1216,7 +1219,6 @@ export class FilterEditorComponent
resetSelected() {
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
this.documentService.searchQuery = ''
this.filterRules = this._unmodifiedFilterRules
this.updateRules()
}

View File

@ -1,11 +1,4 @@
import {
Component,
EventEmitter,
Input,
OnInit,
Output,
inject,
} from '@angular/core'
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import {
FormControl,
FormGroup,
@ -23,7 +16,7 @@ import { TextComponent } from '../../common/input/text/text.component'
imports: [CheckComponent, TextComponent, FormsModule, ReactiveFormsModule],
})
export class SaveViewConfigDialogComponent implements OnInit {
private modal = inject(NgbActiveModal)
constructor(private modal: NgbActiveModal) {}
@Output()
public saveClicked = new EventEmitter()

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
import { Component, EventEmitter, Input, Output } from '@angular/core'
import {
FormControl,
FormGroup,
@ -28,10 +28,6 @@ import { ComponentWithPermissions } from '../with-permissions/with-permissions.c
],
})
export class DocumentNotesComponent extends ComponentWithPermissions {
private notesService = inject(DocumentNotesService)
private toastService = inject(ToastService)
private usersService = inject(UserService)
noteForm: FormGroup = new FormGroup({
newNote: new FormControl(''),
})
@ -52,7 +48,11 @@ export class DocumentNotesComponent extends ComponentWithPermissions {
updated: EventEmitter<DocumentNote[]> = new EventEmitter()
users: User[]
constructor() {
constructor(
private notesService: DocumentNotesService,
private toastService: ToastService,
private usersService: UserService
) {
super()
this.usersService.listAll().subscribe({
next: (users) => {

View File

@ -1,4 +1,4 @@
import { Component, HostListener, inject } from '@angular/core'
import { Component, HostListener } from '@angular/core'
import {
PermissionAction,
PermissionsService,
@ -15,15 +15,17 @@ import { UploadDocumentsService } from 'src/app/services/upload-documents.servic
imports: [],
})
export class FileDropComponent {
private settings = inject(SettingsService)
private toastService = inject(ToastService)
private uploadDocumentsService = inject(UploadDocumentsService)
private permissionsService = inject(PermissionsService)
private fileLeaveTimeoutID: any
fileIsOver: boolean = false
hidden: boolean = true
constructor(
private settings: SettingsService,
private toastService: ToastService,
private uploadDocumentsService: UploadDocumentsService,
private permissionsService: PermissionsService
) {}
public get dragDropEnabled(): boolean {
return (
this.settings.globalDropzoneEnabled &&

View File

@ -1,8 +1,9 @@
import { NgClass, TitleCasePipe } from '@angular/common'
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import {
NgbDropdownModule,
NgbModal,
NgbPaginationModule,
} from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
@ -11,8 +12,13 @@ import { FILTER_HAS_CORRESPONDENT_ANY } from 'src/app/data/filter-rule-type'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SortableDirective } from 'src/app/directives/sortable.directive'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { PermissionType } from 'src/app/services/permissions.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import {
PermissionsService,
PermissionType,
} from 'src/app/services/permissions.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { ToastService } from 'src/app/services/toast.service'
import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { ManagementListComponent } from '../management-list/management-list.component'
@ -36,37 +42,47 @@ import { ManagementListComponent } from '../management-list/management-list.comp
],
})
export class CorrespondentListComponent extends ManagementListComponent<Correspondent> {
private datePipe = inject(CustomDatePipe)
constructor() {
super()
this.service = inject(CorrespondentService)
this.editDialogComponent = CorrespondentEditDialogComponent
this.filterRuleType = FILTER_HAS_CORRESPONDENT_ANY
this.typeName = $localize`correspondent`
this.typeNamePlural = $localize`correspondents`
this.permissionType = PermissionType.Correspondent
this.extraColumns = [
{
key: 'last_correspondence',
name: $localize`Last used`,
valueFn: (c: Correspondent) => {
if (c.last_correspondence) {
let date = new Date(c.last_correspondence)
if (date.toString() == 'Invalid Date') {
// very old date strings are unable to be parsed
date = new Date(
c.last_correspondence
?.toString()
.replace(/([-+])(\d\d):\d\d:\d\d/gm, `$1$2:00`)
)
constructor(
correspondentsService: CorrespondentService,
modalService: NgbModal,
toastService: ToastService,
documentListViewService: DocumentListViewService,
permissionsService: PermissionsService,
private datePipe: CustomDatePipe
) {
super(
correspondentsService,
modalService,
CorrespondentEditDialogComponent,
toastService,
documentListViewService,
permissionsService,
FILTER_HAS_CORRESPONDENT_ANY,
$localize`correspondent`,
$localize`correspondents`,
PermissionType.Correspondent,
[
{
key: 'last_correspondence',
name: $localize`Last used`,
valueFn: (c: Correspondent) => {
if (c.last_correspondence) {
let date = new Date(c.last_correspondence)
if (date.toString() == 'Invalid Date') {
// very old date strings are unable to be parsed
date = new Date(
c.last_correspondence
?.toString()
.replace(/([-+])(\d\d):\d\d:\d\d/gm, `$1$2:00`)
)
}
return this.datePipe.transform(date)
}
return this.datePipe.transform(date)
}
return ''
return ''
},
},
},
]
]
)
}
public reloadData(): void {

View File

@ -1,4 +1,4 @@
import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import {
NgbDropdownModule,
NgbModal,
@ -42,17 +42,21 @@ export class CustomFieldsComponent
extends LoadingComponentWithPermissions
implements OnInit
{
private customFieldsService = inject(CustomFieldsService)
permissionsService = inject(PermissionsService)
private modalService = inject(NgbModal)
private toastService = inject(ToastService)
private documentListViewService = inject(DocumentListViewService)
private settingsService = inject(SettingsService)
private documentService = inject(DocumentService)
private savedViewService = inject(SavedViewService)
public fields: CustomField[] = []
constructor(
private customFieldsService: CustomFieldsService,
public permissionsService: PermissionsService,
private modalService: NgbModal,
private toastService: ToastService,
private documentListViewService: DocumentListViewService,
private settingsService: SettingsService,
private documentService: DocumentService,
private savedViewService: SavedViewService
) {
super()
}
ngOnInit() {
this.reload()
}

View File

@ -1,8 +1,9 @@
import { NgClass, TitleCasePipe } from '@angular/common'
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import {
NgbDropdownModule,
NgbModal,
NgbPaginationModule,
} from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
@ -10,8 +11,13 @@ import { DocumentType } from 'src/app/data/document-type'
import { FILTER_HAS_DOCUMENT_TYPE_ANY } from 'src/app/data/filter-rule-type'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SortableDirective } from 'src/app/directives/sortable.directive'
import { PermissionType } from 'src/app/services/permissions.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import {
PermissionsService,
PermissionType,
} from 'src/app/services/permissions.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { ToastService } from 'src/app/services/toast.service'
import { DocumentTypeEditDialogComponent } from '../../common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { ManagementListComponent } from '../management-list/management-list.component'
@ -34,14 +40,26 @@ import { ManagementListComponent } from '../management-list/management-list.comp
],
})
export class DocumentTypeListComponent extends ManagementListComponent<DocumentType> {
constructor() {
super()
this.service = inject(DocumentTypeService)
this.editDialogComponent = DocumentTypeEditDialogComponent
this.filterRuleType = FILTER_HAS_DOCUMENT_TYPE_ANY
this.typeName = $localize`document type`
this.typeNamePlural = $localize`document types`
this.permissionType = PermissionType.DocumentType
constructor(
documentTypeService: DocumentTypeService,
modalService: NgbModal,
toastService: ToastService,
documentListViewService: DocumentListViewService,
permissionsService: PermissionsService
) {
super(
documentTypeService,
modalService,
DocumentTypeEditDialogComponent,
toastService,
documentListViewService,
permissionsService,
FILTER_HAS_DOCUMENT_TYPE_ANY,
$localize`document type`,
$localize`document types`,
PermissionType.DocumentType,
[]
)
}
getDeleteMessage(object: DocumentType) {

View File

@ -158,14 +158,13 @@ describe('MailComponent', () => {
it('should show errors on load if load mailAccounts failure', () => {
const toastErrorSpy = jest.spyOn(toastService, 'showError')
jest.spyOn(mailAccountService, 'getCached').mockReturnValue(of(null))
jest
.spyOn(mailAccountService, 'listAll')
.mockImplementation(() =>
throwError(() => new Error('failed to load mail accounts'))
)
completeSetup(mailAccountService)
expect(toastErrorSpy).toHaveBeenCalled()
expect(toastErrorSpy).toBeCalled()
})
it('should show errors on load if load mailRules failure', () => {
@ -176,7 +175,7 @@ describe('MailComponent', () => {
throwError(() => new Error('failed to load mail rules'))
)
completeSetup(mailRuleService)
expect(toastErrorSpy).toHaveBeenCalled()
expect(toastErrorSpy).toBeCalled()
})
it('should support edit / create mail account, show error if needed', () => {

View File

@ -1,5 +1,5 @@
import { AsyncPipe } from '@angular/common'
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { ActivatedRoute } from '@angular/router'
import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'
@ -47,14 +47,6 @@ export class MailComponent
extends ComponentWithPermissions
implements OnInit, OnDestroy
{
mailAccountService = inject(MailAccountService)
mailRuleService = inject(MailRuleService)
private toastService = inject(ToastService)
private modalService = inject(NgbModal)
permissionsService = inject(PermissionsService)
private settingsService = inject(SettingsService)
private route = inject(ActivatedRoute)
public MailAccountType = MailAccountType
mailAccounts: MailAccount[] = []
@ -76,6 +68,18 @@ export class MailComponent
public loadingAccounts: boolean = true
public showAccounts: boolean = false
constructor(
public mailAccountService: MailAccountService,
public mailRuleService: MailRuleService,
private toastService: ToastService,
private modalService: NgbModal,
public permissionsService: PermissionsService,
private settingsService: SettingsService,
private route: ActivatedRoute
) {
super()
}
ngOnInit(): void {
this.mailAccountService
.listAll(null, null, { full_perms: true })

View File

@ -1,7 +1,6 @@
import { HttpErrorResponse } from '@angular/common/http'
import {
Directive,
inject,
OnDestroy,
OnInit,
QueryList,
@ -60,19 +59,21 @@ export abstract class ManagementListComponent<T extends MatchingModel>
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy
{
protected service: AbstractNameFilterService<T>
private modalService: NgbModal = inject(NgbModal)
protected editDialogComponent: any
private toastService: ToastService = inject(ToastService)
private documentListViewService: DocumentListViewService = inject(
DocumentListViewService
)
private permissionsService: PermissionsService = inject(PermissionsService)
protected filterRuleType: number
public typeName: string
public typeNamePlural: string
public permissionType: PermissionType
public extraColumns: ManagementListColumn[]
constructor(
protected service: AbstractNameFilterService<T>,
private modalService: NgbModal,
private editDialogComponent: any,
private toastService: ToastService,
private documentListViewService: DocumentListViewService,
private permissionsService: PermissionsService,
protected filterRuleType: number,
public typeName: string,
public typeNamePlural: string,
public permissionType: PermissionType,
public extraColumns: ManagementListColumn[]
) {
super()
}
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>

View File

@ -1,5 +1,5 @@
import { AsyncPipe } from '@angular/common'
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import { Component, OnDestroy, OnInit } from '@angular/core'
import {
FormControl,
FormGroup,
@ -40,10 +40,6 @@ export class SavedViewsComponent
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy
{
private savedViewService = inject(SavedViewService)
private settings = inject(SettingsService)
private toastService = inject(ToastService)
DisplayMode = DisplayMode
public savedViews: SavedView[]
@ -59,7 +55,11 @@ export class SavedViewsComponent
return this.settings.allDisplayFields
}
constructor() {
constructor(
private savedViewService: SavedViewService,
private settings: SettingsService,
private toastService: ToastService
) {
super()
this.settings.organizingSidebarSavedViews = true
}

View File

@ -1,8 +1,9 @@
import { NgClass, TitleCasePipe } from '@angular/common'
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import {
NgbDropdownModule,
NgbModal,
NgbPaginationModule,
} from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
@ -11,8 +12,13 @@ import { StoragePath } from 'src/app/data/storage-path'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SortableDirective } from 'src/app/directives/sortable.directive'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { PermissionType } from 'src/app/services/permissions.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import {
PermissionsService,
PermissionType,
} from 'src/app/services/permissions.service'
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { ToastService } from 'src/app/services/toast.service'
import { StoragePathEditDialogComponent } from '../../common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { ManagementListComponent } from '../management-list/management-list.component'
@ -36,25 +42,36 @@ import { ManagementListComponent } from '../management-list/management-list.comp
],
})
export class StoragePathListComponent extends ManagementListComponent<StoragePath> {
constructor() {
super()
this.service = inject(StoragePathService)
this.editDialogComponent = StoragePathEditDialogComponent
this.filterRuleType = FILTER_HAS_STORAGE_PATH_ANY
this.typeName = $localize`storage path`
this.typeNamePlural = $localize`storage paths`
this.permissionType = PermissionType.StoragePath
this.extraColumns = [
{
key: 'path',
name: $localize`Path`,
rendersHtml: true,
hideOnMobile: true,
valueFn: (c: StoragePath) => {
return `<code>${c.path?.slice(0, 49)}${c.path?.length > 50 ? '...' : ''}</code>`
constructor(
directoryService: StoragePathService,
modalService: NgbModal,
toastService: ToastService,
documentListViewService: DocumentListViewService,
permissionsService: PermissionsService
) {
super(
directoryService,
modalService,
StoragePathEditDialogComponent,
toastService,
documentListViewService,
permissionsService,
FILTER_HAS_STORAGE_PATH_ANY,
$localize`storage path`,
$localize`storage paths`,
PermissionType.StoragePath,
[
{
key: 'path',
name: $localize`Path`,
rendersHtml: true,
hideOnMobile: true,
valueFn: (c: StoragePath) => {
return `<code>${c.path?.slice(0, 49)}${c.path?.length > 50 ? '...' : ''}</code>`
},
},
},
]
]
)
}
getDeleteMessage(object: StoragePath) {

View File

@ -1,8 +1,9 @@
import { NgClass, TitleCasePipe } from '@angular/common'
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import {
NgbDropdownModule,
NgbModal,
NgbPaginationModule,
} from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
@ -11,8 +12,13 @@ import { Tag } from 'src/app/data/tag'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SortableDirective } from 'src/app/directives/sortable.directive'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { PermissionType } from 'src/app/services/permissions.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import {
PermissionsService,
PermissionType,
} from 'src/app/services/permissions.service'
import { TagService } from 'src/app/services/rest/tag.service'
import { ToastService } from 'src/app/services/toast.service'
import { TagEditDialogComponent } from '../../common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { ManagementListComponent } from '../management-list/management-list.component'
@ -36,24 +42,35 @@ import { ManagementListComponent } from '../management-list/management-list.comp
],
})
export class TagListComponent extends ManagementListComponent<Tag> {
constructor() {
super()
this.service = inject(TagService)
this.editDialogComponent = TagEditDialogComponent
this.filterRuleType = FILTER_HAS_TAGS_ALL
this.typeName = $localize`tag`
this.typeNamePlural = $localize`tags`
this.permissionType = PermissionType.Tag
this.extraColumns = [
{
key: 'color',
name: $localize`Color`,
rendersHtml: true,
valueFn: (t: Tag) => {
return `<span class="badge" style="color: ${t.text_color}; background-color: ${t.color}">${t.color}</span>`
constructor(
tagService: TagService,
modalService: NgbModal,
toastService: ToastService,
documentListViewService: DocumentListViewService,
permissionsService: PermissionsService
) {
super(
tagService,
modalService,
TagEditDialogComponent,
toastService,
documentListViewService,
permissionsService,
FILTER_HAS_TAGS_ALL,
$localize`tag`,
$localize`tags`,
PermissionType.Tag,
[
{
key: 'color',
name: $localize`Color`,
rendersHtml: true,
valueFn: (t: Tag) => {
return `<span class="badge" style="color: ${t.text_color}; background-color: ${t.color}">${t.color}</span>`
},
},
},
]
]
)
}
getDeleteMessage(object: Tag) {

View File

@ -1,4 +1,4 @@
import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
@ -34,13 +34,17 @@ export class WorkflowsComponent
extends LoadingComponentWithPermissions
implements OnInit
{
private workflowService = inject(WorkflowService)
permissionsService = inject(PermissionsService)
private modalService = inject(NgbModal)
private toastService = inject(ToastService)
public workflows: Workflow[] = []
constructor(
private workflowService: WorkflowService,
public permissionsService: PermissionsService,
private modalService: NgbModal,
private toastService: ToastService
) {
super()
}
ngOnInit() {
this.reload()
}

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